尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Spring OrderComparator 源码解析:Spring 中 Ordered 排序机制的底层实现

Spring OrderComparator 源码解析:Spring 中 Ordered 排序机制的底层实现 Spring OrderComparator 源码解析Spring 中 Ordered 排序机制的底层实现【免费下载链接】source-code-hunter 从源码层面剖析挖掘互联网行业主流技术的底层实现原理为广大开发者 “提升技术深度” 提供便利。目前开放 Spring 全家桶Mybatis、Netty、Dubbo 框架及 Redis、Tomcat 中间件等项目地址: https://gitcode.com/doocs/source-code-hunter导读OrderComparator是 Spring 框架中负责对实现了Ordered/PriorityOrdered接口的对象进行优先级排序的核心组件它支撑了 Spring 容器中BeanPostProcessor、BeanFactoryPostProcessor以及 SpringMVC 中多个HandlerMapping的有序调度。本文以 docs/Spring/clazz/Spring-OrderComparator.md 为主体深入拆解doCompare、getOrder、findOrder三个核心方法的执行逻辑并结合本仓库中 SpringMVC、SpringBoot 等源码笔记揭示OrderComparator在实际框架中的调用场景与排序效果。一、OrderComparator 的职责与核心 API在 Spring 中当存在多个同类组件需要按优先级依次处理时通常会让它们实现Ordered接口并返回一个order数值数值越小优先级越高而OrderComparator就是这套排序规则的裁判。OrderComparator提供的核心静态方法如下sort(List? list)对列表进行原地排序withSourceProvider(OrderSourceProvider sourceProvider)返回一个带有排序源提供者的新的比较器实例用于支持从对象关联的其他对象上获取order值内部通过doCompare完成两两比较。其中OrderSourceProvider是一个函数式接口它允许比较器不直接读取对象自身的Ordered实现而是通过getOrderSource(Object obj)获取该对象关联的排序源可以是单个对象也可以是一个数组再从排序源上解析出order值。这一设计让排序逻辑与业务对象解耦是理解后续测试用例的关键。二、核心排序逻辑doCompare 方法doCompare是OrderComparator的私有核心比较方法原文档中的源码实现如下private int doCompare(Nullable Object o1, Nullable Object o2, Nullable OrderSourceProvider sourceProvider) { boolean p1 (o1 instanceof PriorityOrdered); boolean p2 (o2 instanceof PriorityOrdered); if (p1 !p2) { return -1; } else if (p2 !p1) { return 1; } int i1 getOrder(o1, sourceProvider); int i2 getOrder(o2, sourceProvider); // 对比两个Order值得大小返回 return Integer.compare(i1, i2); }该方法的执行分两个阶段第一阶段PriorityOrdered 优先判定。PriorityOrdered继承自Ordered表示最高优先级。比较时先检查两个对象是否为PriorityOrdered实例若o1是PriorityOrdered而o2不是直接返回-1o1排前面若o2是PriorityOrdered而o1不是直接返回1o2排前面两者都是或都不是PriorityOrdered时进入第二阶段。从源码结构看这一设计使得 Spring 容器在批量注册后置处理器时可以保证实现PriorityOrdered的处理器永远先于普通Ordered处理器执行这正是 docs/Spring/IoC/BeanPostProcessor.md 与 docs/Spring/clazz/Spring-BeanFactoryPostProcessor.md 中反复出现的先处理 PriorityOrdered再处理 Ordered规则的底层支撑。第二阶段order 数值比较。通过getOrder分别取出o1、o2的order值最终调用Integer.compare(i1, i2)完成大小比较i1 i2返回负数o1排在前面order 越小优先级越高i1 i2返回 0两者视为同优先级i1 i2返回正数o2排在前面。三、order 值提取getOrder 与 OrderSourceProviderdoCompare第二阶段依赖getOrder方法从对象上解析出排序值原文档源码如下private int getOrder(Nullable Object obj, Nullable OrderSourceProvider sourceProvider) { Integer order null; if (obj ! null sourceProvider ! null) { // 获取Order Object orderSource sourceProvider.getOrderSource(obj); if (orderSource ! null) { if (orderSource.getClass().isArray()) { // 获取 OrderSourceProvider 的值 Object[] sources ObjectUtils.toObjectArray(orderSource); for (Object source : sources) { // 找 order 返回 order findOrder(source); if (order ! null) { break; } } } else { // 寻找 order order findOrder(orderSource); } } } return (order ! null ? order : getOrder(obj)); }这段代码的查找策略可以概括为只有当obj与sourceProvider都非空时才走OrderSourceProvider路径调用sourceProvider.getOrderSource(obj)拿到排序源orderSource排序源是数组通过ObjectUtils.toObjectArray(orderSource)转成对象数组然后遍历数组逐个调用findOrder一旦找到非空order立即break即取数组中第一个有 order 值的元素作为依据排序源是单对象直接调用findOrder(orderSource)兜底回退如果sourceProvider为空或没有解析出任何order则回退到getOrder(obj)——即直接从对象自身解析。这种先查排序源、找不到再回退到对象自身的两级查找策略使得同一个比较器既能处理包装类对象通过OrderSourceProvider关联到真实排序对象又能处理直接实现Ordered的普通对象具备极强的通用性。四、findOrder 与 StubOrderedorder 的最终来源findOrder是 order 值解析的最底层方法源码如下Nullable protected Integer findOrder(Object obj) { // 获取Ordered实现类 return (obj instanceof Ordered ? ((Ordered) obj).getOrder() : null); }逻辑非常简单只有对象实现了Ordered接口才会调用其getOrder()方法取得排序值否则返回null表示没有 order 信息。它是一个protected方法子类AnnotationAwareOrderComparator会重写它从而支持从Order注解等元数据中解析 order 值。原文档中的测试桩类StubOrdered则演示了Ordered接口的标准实现方式private static final class StubOrdered implements Ordered { private final int order; public StubOrdered(int order) { this.order order; } Override public int getOrder() { return this.order; } }五、测试用例验证compareWithSourceProviderArray原文档给出了关键测试用例用于验证OrderSourceProvider的数组分支Test public void compareWithSourceProviderArray() { ComparatorObject customComparator this.comparator.withSourceProvider( new TestSourceProvider(5L, new Object[]{new StubOrdered(10), new StubOrdered(-25)})); assertEquals(-1, customComparator.compare(5L, new Object())); }逐步推演该用例的执行过程withSourceProvider将比较器与TestSourceProvider绑定5L对应的排序源是Object[]{new StubOrdered(10), new StubOrdered(-25)}调用customComparator.compare(5L, new Object())时o1 5L、o2 new Object()两者都不是PriorityOrdered进入 order 比较阶段对5L走getOrderSource得到排序源数组遍历后findOrder(new StubOrdered(10))命中order 10StubOrdered(-25)不再被检查因为第一个就找到了对new Object()走OrderSourceProvider路径拿不到任何 order且它没有实现OrderedfindOrder返回null此时回退逻辑(order ! null ? order : getOrder(obj))中getOrder(obj)依然返回null。而OrderComparator中会将null归一化为Ordered.LOWEST_PRECEDENCE即Integer.MAX_VALUE因此Integer.compare(10, Integer.MAX_VALUE) -1断言成立——带有 order 排序值的对象排在无排序值对象之前。下图展示了测试执行过程中getOrder方法处理数组排序源的调试快照orderSource为Object[2]数组数组元素分别为order10与order-25的两个StubOrdered实例继续向下调试可以看到循环遍历sources数组后order变量被赋值为10来自数组中第一个元素StubOrdered随后循环因找到非空 order 而终止最终Integer.compare(i1, i2)完成比较并返回结果一次完整的排序判定就此结束。六、框架中的真实调用场景OrderComparator及其子类AnnotationAwareOrderComparator在 Spring 生态中被广泛用于多个同类组件按优先级调度的场景本仓库中的源码笔记可以提供多个印证场景一SpringMVC 中多个 HandlerMapping 的排序在 docs/Spring/SpringMVC/SpringMVC的设计与实现.md 的initHandlerMappings方法中DispatcherServlet 从容器中收集到所有HandlerMapping后会调用OrderComparator.sort(this.handlerMappings)保持有序性// 查找所有的 HandlerMapping从 应用上下文context 及其双亲上下文中 MapString, HandlerMapping matchingBeans BeanFactoryUtils.beansOfTypeIncludingAncestors( context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings new ArrayListHandlerMapping(matchingBeans.values()); // 保持 HandlerMappings 的有序性 OrderComparator.sort(this.handlerMappings); }这样在请求处理时DispatcherServlet 会严格按照 order 升序依次尝试各个HandlerMapping确保高优先级的映射器如注解驱动的RequestMappingHandlerMapping先于兜底的映射器如SimpleUrlHandlerMapping执行匹配。场景二SpringBoot 中 SpringFactories 实例的排序在 docs/SpringBoot/Spring-Boot-Run.md 的getSpringFactoriesInstances方法中SpringBoot 通过SpringFactoriesLoader.loadFactoryNames读取spring.factories并实例化后会调用AnnotationAwareOrderComparator.sort(instances)对实例排序SetString names new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 创建SpringFactory实例 ListT instances createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); /** * 排序 {link Ordered} */ AnnotationAwareOrderComparator.sort(instances);AnnotationAwareOrderComparator是OrderComparator的子类它在findOrder的重写中额外支持了从Order注解中解析排序值从而让 SpringBoot 的ApplicationContextInitializer、SpringApplicationRunListener等组件既能通过实现Ordered接口、也能通过Order注解来控制初始化顺序。场景三BeanPostProcessor 的分组注册在 docs/Spring/clazz/Spring-BeanFactoryPostProcessor.md 的registerBeanPostProcessors流程中容器会将后置处理器按PriorityOrdered、Ordered、普通实现分为三组并逐组注册与调用docs/Spring/IoC/BeanPostProcessor.md 也明确指出仅当 BeanPostProcessor 实现Ordered接口时才能通过设置 order 属性来控制实例的执行顺序。这些分组规则在比较层面的最终实现正是本文剖析的OrderComparator.doCompare中PriorityOrdered优先判定与Integer.compare数值比较两段逻辑。七、总结OrderComparator的排序规则可以凝练为一条优先级链接口级别PriorityOrdered实现永远排在普通Ordered实现之前数值级别同级别的对象按getOrder()返回的数值升序排列数值越小优先级越高取值兜底优先通过OrderSourceProvider从关联对象上取 order取不到时回退到对象自身再取不到则按最低优先级Ordered.LOWEST_PRECEDENCEInteger.MAX_VALUE处理。掌握这套机制后无论是要调整 SpringMVC 中HandlerMapping的匹配顺序、控制 SpringBoot 初始化器的执行先后还是自定义一批需要按优先级执行的业务组件都可以通过实现Ordered/PriorityOrdered接口或使用Order注解配合AnnotationAwareOrderComparator轻松实现而无需关心比较器内部的实现细节。【免费下载链接】source-code-hunter 从源码层面剖析挖掘互联网行业主流技术的底层实现原理为广大开发者 “提升技术深度” 提供便利。目前开放 Spring 全家桶Mybatis、Netty、Dubbo 框架及 Redis、Tomcat 中间件等项目地址: https://gitcode.com/doocs/source-code-hunter创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表