巧用Spring中的@Order进行排序
作者:Jon Kee
Spring @Order进行排序
直接上代码
public class OrderAnnotationTest { public static void main(String[] args) { A a = new A(); B b = new B(); C c = new C(); List<Object> orderList = new ArrayList<>(3); orderList.add(a); orderList.add(b); orderList.add(c); orderList.sort(AnnotationAwareOrderComparator.INSTANCE); System.out.println(orderList); } @Order(0) static class A { @Override public String toString() { return "A"; } } @Order(-1) static class B { @Override public String toString() { return "B"; } } @Order(2) static class C { @Override public String toString() { return "C"; } } }
结果如下:
[B, A, C]
原理解析:
AnnotationAwareOrderComparator继承自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); return Integer.compare(i1, i2); }
Spring中关于Order的那点事
本文阅读源码版本为spring5.3.1
为啥要用Order
spring是一个大量使用策略设计模式的框架,这意味着有很多相同接口的实现类,如果不手动指定顺序的话,那么使用时肯定会有问题。而Order给我们提供了一种编码设置顺序的可能。
关于Order
spring中提供了多种方式来设置优先级,有Ordered,PriorityOrdered接口,有Order注解,除此之外,spring4.1以后,还可以使用Priority注解。下面我将针对这几种用法从源码的角度来进行分析。
Ordered,PriorityOrdered接口
public interface Ordered { /** * 最高优先值 */ int HIGHEST_PRECEDENCE = Integer.MIN_VALUE; /** * 最低优先值 */ int LOWEST_PRECEDENCE = Integer.MAX_VALUE; int getOrder(); }
PriorityOrdered继承了Ordered,但并未提供任何方法,这是一个标记了优先级的接口,和Ordered相比,PriorityOrdered就是高人一等,spring中提供了比较器OrderComparator,可以通过构建一个OrderComparator,调用其compare方法,不过OrderComparator提供了一个静态sort方法,我们无需自己构建OrderComparator了,排序的结果按照order值从小到大排序。
demo
public class OrderDemo{ private final OrderComparator comparator = new OrderComparator(); @Test void comparePriorityOrderedInstanceToStandardOrderedInstanceWithSamePriority() { assertThatPriorityOrderedAlwaysWins(new StubPriorityOrdered(100), new StubOrdered(100)); } @Test void comparePriorityOrderedInstanceToStandardOrderedInstanceWithLowerPriority() { assertThatPriorityOrderedAlwaysWins(new StubPriorityOrdered(100), new StubOrdered(200)); } @Test void compareOrderedInstancesBefore() { assertThat(this.comparator.compare(new StubOrdered(100), new StubOrdered(2000))).isEqualTo(-1); } @Test void compareOrderedInstancesNullFirst() { assertThat(this.comparator.compare(null, new StubOrdered(100))).isEqualTo(1); } @Test void compareOrderedInstancesNullLast() { assertThat(this.comparator.compare(new StubOrdered(100), null)).isEqualTo(-1); } @Test void test1() { assertThat(this.comparator.compare(new Object (), new StubOrdered(2000))).isEqualTo(1); } private static class StubOrdered implements Ordered { private final int order; StubOrdered(int order) { this.order = order; } @Override public int getOrder() { return this.order; } } private static class StubPriorityOrdered implements PriorityOrdered { private final int order; StubPriorityOrdered(int order) { this.order = order; } @Override public int getOrder() { return this.order; } } }
小结
- PriorityOrdered优先级比Ordered高,与设置的order值无关。
- 若两个对象都实现了Ordered或PriorityOrdered接口,那么设置的order值越小,优先值越高。
- 若没有实现Ordered或PriorityOrdered接口,默认是最低的优先级。
OrderComparator#compare解读
在看compare之前,我觉得将OrderSourceProvider这个函数式接口放在前面讲解一下,阅读源码时会更清晰一点。
@FunctionalInterface public interface OrderSourceProvider { /** * 对给定对象校验并返回一个新的对象 */ @Nullable Object getOrderSource(Object obj); }
demo
public class OrderDemo{ private final OrderComparator comparator = new OrderComparator(); private static class TestSourceProvider implements OrderComparator.OrderSourceProvider { private final Object target; private final Object orderSource; TestSourceProvider(Object target, Object orderSource) { this.target = target; this.orderSource = orderSource; } @Override public Object getOrderSource(Object obj) { if (target.equals(obj)) { return orderSource; } return null; } } @Test void compareWithSourceProviderArray() { Comparator<Object> customComparator = this.comparator.withSourceProvider( new TestSourceProvider(5L, new Object[] {new StubOrdered(10), new StubOrdered(-25)})); assertThat(customComparator.compare(5L, new Object())).isEqualTo(-1); } @Test void compareWithSourceProviderArrayNoMatch() { Comparator<Object> customComparator = this.comparator.withSourceProvider( new TestSourceProvider(5L, new Object[] {new Object(), new Object()})); assertThat(customComparator.compare(new Object(), 5L)).isEqualTo(0); } @Test void compareWithSourceProviderEmpty() { Comparator<Object> customComparator = this.comparator.withSourceProvider( new TestSourceProvider(50L, new Object())); assertThat(customComparator.compare(new Object(), 5L)).isEqualTo(0); } }
接下来我们来阅读compare源码。
public int compare(@Nullable Object o1, @Nullable Object o2) { return doCompare(o1, o2, null); } private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) { // 这里会判断是否实现了PriorityOrdered接口 boolean p1 = (o1 instanceof PriorityOrdered); boolean p2 = (o2 instanceof PriorityOrdered); // 这里会看到根本没有比较order的值,只要实现PriorityOrdered接口,就会排在前面 if (p1 && !p2) { return -1; }else if (p2 && !p1) { return 1; } // 获取对象设置的order值 int i1 = getOrder(o1, sourceProvider); int i2 = getOrder(o2, sourceProvider); return Integer.compare(i1, i2); } private int getOrder(@Nullable Object obj, @Nullable OrderSourceProvider sourceProvider) { Integer order = null; if (obj != null && sourceProvider != null) { Object orderSource = sourceProvider.getOrderSource(obj); if (orderSource != null) { // 如果返回的是数组 if (orderSource.getClass().isArray()) { for (Object source : ObjectUtils.toObjectArray(orderSource)) { // 只要找到对象设置的order值,就跳出 order = findOrder(source); if (order != null) { break; } } }else { order = findOrder(orderSource); } } } // 如果我们没有提供OrderSourceProvider return (order != null ? order : getOrder(obj)); } protected int getOrder(@Nullable Object obj) { if (obj != null) { Integer order = findOrder(obj); if (order != null) { return order; } } // object为null时,返回值最大 return Ordered.LOWEST_PRECEDENCE; } protected Integer findOrder(Object obj) { // 没有实现Ordered接口将返回null return (obj instanceof Ordered ? ((Ordered) obj).getOrder() : null); }
@Order与@Priority
spring中提供了对@Order与@Priority支持的比较器AnnotationAwareOrderComparator,该类继承OrderComparator,并覆盖了findOrder方法,我们来一起看下源码。
protected Integer findOrder(Object obj) { Integer order = super.findOrder(obj); if (order != null) { return order; } // 调用父类的findOrder方法无法找到设定的order值时 return findOrderFromAnnotation(obj); } private Integer findOrderFromAnnotation(Object obj) { AnnotatedElement element = (obj instanceof AnnotatedElement ? (AnnotatedElement) obj : obj.getClass()); // 对整个类型层次结构执行完整搜索,包括父类和接口 MergedAnnotations annotations = MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY); // 获取注解中设置的order值 Integer order = OrderUtils.getOrderFromAnnotations(element, annotations); if (order == null && obj instanceof DecoratingProxy) { return findOrderFromAnnotation(((DecoratingProxy) obj).getDecoratedClass()); } return order; } static Integer getOrderFromAnnotations(AnnotatedElement element, MergedAnnotations annotations) { if (!(element instanceof Class)) { return findOrder(annotations); } // 加入缓存中 Object cached = orderCache.get(element); if (cached != null) { return (cached instanceof Integer ? (Integer) cached : null); } Integer result = findOrder(annotations); orderCache.put(element, result != null ? result : NOT_ANNOTATED); return result; } // 没有找到Order注解后才去寻找@Priority注解 private static Integer findOrder(MergedAnnotations annotations) { MergedAnnotation<Order> orderAnnotation = annotations.get(Order.class); if (orderAnnotation.isPresent()) { return orderAnnotation.getInt(MergedAnnotation.VALUE); } MergedAnnotation<?> priorityAnnotation = annotations.get(JAVAX_PRIORITY_ANNOTATION); if (priorityAnnotation.isPresent()) { return priorityAnnotation.getInt(MergedAnnotation.VALUE); } return null; }
demo
public class AnnotationAwareOrderComparatorTests { @Test void sortInstancesWithSubclass() { List<Object> list = new ArrayList<>(); list.add(new B()); list.add(new C()); AnnotationAwareOrderComparator.sort(list); assertThat(list.get(0) instanceof C).isTrue(); assertThat(list.get(1) instanceof B).isTrue(); } @Test void sortInstancesWithOrderAndPriority() { List<Object> list = new ArrayList<>(); list.add(new B()); list.add(new A2()); AnnotationAwareOrderComparator.sort(list); assertThat(list.get(0) instanceof A2).isTrue(); assertThat(list.get(1) instanceof B).isTrue(); } @Order(1) private static class A { } @Order(2) private static class B { } private static class C extends A { } @Priority(1) private static class A2 { } }
小结
@Order与@Priority注解放置在类,接口或参数上,可以被继承;它们之间是可以互相替换的关系。
应用
spring源码中有很多地方都显式的调用AnnotationAwareOrderComparator的sort方法,也有一些地方调用的OrderComparator的sort方法,大家自己可以找找看。
我这里发现了一点有意思的地方,我们如果定义多个ControllerAdvice的bean,分别通过实现Ordered,PriorityOrdered接口来定义执行时的顺序,会发现上面我们总结的 PriorityOrdered优先级就是比Ordered高 这一点不成立,其实只是spring将ControllerAdvice相关信息封装了一下欺骗了我们。我看的源码的版本是5.3.1,低于5.2版本的不会发生这样的事情。这里我们就来看看5.2版本前后源码有哪些变化,导致了这个现象的发生。
这里就拿RequestMappingHandlerAdapter初始化去寻找ControllerAdvice注解的代码来举例
private void initControllerAdviceCache() { if (getApplicationContext() == null) { return; } List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext()); // 5.2版本前使用下面注释的这行代码,5.2之后这行代码就去掉了,而是在上面findAnnotatedBeans // 方法中使用OrderComparator.sort(adviceBeans) //AnnotationAwareOrderComparator.sort(adviceBeans); ... }
我们知道OrderComparator适用范围是比AnnotationAwareOrderComparator要窄一点的,它不支持注解,那么上面这样的改动是不是就意味着我们定义ControllerAdvice时,就不能使用@Order与@Pri-ority呢?
其实它是支持的,ControllerAdviceBean#findAnnotatedBeans方法中会将我们定义的Con-trollerAdvice类包装成ControllerAdviceBean,而ControllerAdviceBean是实现了Ordered接口的,那么OrderComparator#sort方法要想支持使用注解,ControllerAdviceBean的getOrder方法中就必须干点啥,分析了挺多,我们还是看源码实现吧。
// 5.2版本后 public int getOrder() { if (this.order == null) { String beanName = null; Object resolvedBean = null; // 这里根据beanName获取bean if (this.beanFactory != null && this.beanOrName instanceof String) { beanName = (String) this.beanOrName; String targetBeanName = ScopedProxyUtils.getTargetBeanName(beanName); boolean isScopedProxy = this.beanFactory.containsBean(targetBeanName); if (!isScopedProxy && !ScopedProxyUtils.isScopedTarget(beanName)) { resolvedBean = resolveBean(); } }else { resolvedBean = resolveBean(); } // 这里只判断了是否实现了Ordered接口,并没有对实现PriorityOrdered作特殊处理 // 这里优先判断是否实现了Ordered接口,如果同时使用注解的话将被忽略 if (resolvedBean instanceof Ordered) { this.order = ((Ordered) resolvedBean).getOrder(); }else { if (beanName != null && this.beanFactory instanceof ConfigurableBeanFactory) { ConfigurableBeanFactory cbf = (ConfigurableBeanFactory) this.beanFactory; try { BeanDefinition bd = cbf.getMergedBeanDefinition(beanName); if (bd instanceof RootBeanDefinition) { Method factoryMethod = ((RootBeanDefinition) bd).getResolvedFactoryMethod(); if (factoryMethod != null) { // 这里将会从注解@Order与@Priority中获取order值 this.order = OrderUtils.getOrder(factoryMethod); } } }catch (NoSuchBeanDefinitionException ex) { // ignore -> probably a manually registered singleton } } if (this.order == null) { if (this.beanType != null) { this.order = OrderUtils.getOrder(this.beanType, Ordered.LOWEST_PRECEDENCE); } else { this.order = Ordered.LOWEST_PRECEDENCE; } } } } return this.order; }
源码分析后,我们来看一段测试demo
public class ControllerAdviceBeanTests { @ControllerAdvice @Order(100) @Priority(200) static class OrderedControllerAdvice implements Ordered { @Override public int getOrder() { return 42; } } @ControllerAdvice // Order和@Priority由于Order的实现应该被忽略 @Order(100) @Priority(200) static class PriorityOrderedControllerAdvice implements PriorityOrdered { @Override public int getOrder() { return 55; } } @Configuration(proxyBeanMethods = false) static class Config { @Bean OrderedControllerAdvice orderedControllerAdvice() { return new OrderedControllerAdvice(); } @Bean PriorityOrderedControllerAdvice priorityOrderedControllerAdvice() { return new PriorityOrderedControllerAdvice(); } } @Test @SuppressWarnings({"rawtypes", "unchecked"}) public void findAnnotatedBeansSortsBeans() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class); List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(context); // 输出顺序并不是 55 42,而是42,55 for (ControllerAdviceBean adviceBean : adviceBeans) { System.out.println (adviceBean.getOrder ()); } } }
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。