您好,登錄后才能下訂單哦!
這篇文章主要介紹“怎么用Spring中的@Order進行排序”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“怎么用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); }
本文閱讀源碼版本為spring5.3.1
spring是一個大量使用策略設計模式的框架,這意味著有很多相同接口的實現類,如果不手動指定順序的話,那么使用時肯定會有問題。而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接口,默認是最低的優先級。
在看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); }
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 ()); } } }
關于“怎么用Spring中的@Order進行排序”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。