Spring筆記-@Order註解和Ordered接口解析
@Order註解和Ordered接口
Order註解用於排序
public @interface Order { /** * The order value. * <p>Default is {@link Ordered#LOWEST_PRECEDENCE}. * @see Ordered#getOrder() */ int value() default Ordered.LOWEST_PRECEDENCE; }
1.OrderUtils
Spring提供瞭OrderUtils來獲取Class的Order註解排序信息
擴展:Priority註解為javax擴展註解,功能與Order相同
public class OrderUtilsTests { @Test public void getSimpleOrder() { assertEquals(Integer.valueOf(50), OrderUtils.getOrder(SimpleOrder.class, null)); } @Test public void getPriorityOrder() { assertEquals(Integer.valueOf(55), OrderUtils.getOrder(SimplePriority.class, null)); } @Order(50) private static class SimpleOrder {} @Priority(55) private static class SimplePriority {} }
2.Ordered接口
對象排序的另一種實現
public interface Ordered { int getOrder(); }
3.OrderComparator
使用OrderComparator來比較2個對象的排序順序
public final class OrderComparatorTests { private final OrderComparator comparator = new OrderComparator(); @Test public void compareOrderedInstancesBefore() { assertEquals(-1, this.comparator.compare( new StubOrdered(100), new StubOrdered(2000))); } @Test public void compareOrderedInstancesSame() { assertEquals(0, this.comparator.compare( new StubOrdered(100), new StubOrdered(100))); } @Test public void compareOrderedInstancesAfter() { assertEquals(1, this.comparator.compare( new StubOrdered(982300), new StubOrdered(100))); } 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; } } }
其內部比較邏輯
return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
- i1比i2小則返回-1
- i1比i2大則返回1
- i1等於i2則返回0
4.AnnotationAwareOrderComparator
AnnotationAwareOrderComparator繼承自OrderComparator
其可以同時處理對象實現Ordered接口或@Order註解
其提供瞭靜態方法sort,可以對List進行排序
public class AnnotationAwareOrderComparator extends OrderComparator { }
測試代碼
public class AnnotationAwareOrderComparatorTests { @Test public void sortInstances() { List<Object> list = new ArrayList<>(); list.add(new B()); list.add(new A()); AnnotationAwareOrderComparator.sort(list); assertTrue(list.get(0) instanceof A); assertTrue(list.get(1) instanceof B); } @Order(1) private static class A { } @Order(2) private static class B { } }
5.Bean註冊順序
Demo2Config的對象將會先於Demo1Config初始化註冊
註意點:其構造函數的初始化並不生效
@Configuration @Order(2) public class Demo1Config { public Demo1Config() { System.out.println("Demo1Config"); } @Bean public Demo1Service demo1Service(){ System.out.println("demo1config 加載瞭"); return new Demo1Service(); } } @Configuration @Order(1) public class Demo2Config { public Demo2Config() { System.out.println("Demo2Config"); } @Bean public Demo2Service demo2Service(){ System.out.println("demo2config 加載瞭"); return new Demo2Service(); } } public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("core.annotation.order2"); } }
輸出的結果信息:
Demo1Config
Demo2Config
demo2config 加載瞭
demo1config 加載瞭
Spring的Orderd接口及@Order,@Primary,@Priority三個註解介紹
今天要來說一下Orderd接口以及@Order、@Primary、@Priority註解這幾個東西,原本隻是想介紹一下@Order,但是這幾個有一定的關聯,因此這裡一起進行介紹。這幾個接口是用來排序,本文主要介紹用法,具體的比如Spring什麼時候對他們排序啊,後面在介紹Spring的處理過程的時候再介紹,還有怎麼排序的這些比較好理解的也不介紹瞭。
1.如何發現Orderd接口及@Order、@Primary、@Priority
在前面文章說過要通過一些常用的註解以及在學習過程中不斷的發現,因此這裡我還是按我學習的思路介紹一下我是如何發現他們的。如果沒有一個發現以及理解的過程有時候可能會很難記住,就比如我之前專門瞭解瞭Spring相關的註解,並且去學習用法,但是不理解稍微一不用就忘記瞭。
首先自己創建一個測試類,創建AnnotationConfigApplicationContext實例。
@Test public void test() { ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
進入AnnotationConfigApplicationContext構造函數可以發現調用瞭無參構造函數,裡面有個創建AnnotatedBeanDefinitionReader的步驟,Spring用BeanDefinition表示一個Bean,因此這個類也很容易理解就是與讀取註解Bean有關的類。
public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) { super(beanFactory); this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); }
繼續進入可以看到AnnotatedBeanDefinitionReader的構造函數,最後一行表示將那些處理註解的基礎設施類添加到 DefaultListableBeanFactory中。進入這個方法中。
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); Assert.notNull(environment, "Environment must not be null"); this.registry = registry; // 創建條件判斷者,後面用來進行條件註解的判斷,關聯@Conditional註解,@Conditional註解內傳入的用於判斷的類要實現Condition接口的match方法 this.conditionEvaluator = new ConditionEvaluator(registry, environment, null); // 將那些處理註解的基礎設施類添加到 DefaultListableBeanFactory中 AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); }
方法中有個判斷AnnotationAwareOrderComparator是否存在步驟,這個類從字面意思可以看出就是個比較器。
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { // 判斷BeanFactory是不是DefaultListableBeanFactory類型,如果不是需要進行轉換 DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry); if (beanFactory != null) { // beanFactory的依賴關系比較器,如果沒有AnnotationAwareOrderComparator這個比較器,就傳入全局默認靜態不可變的order比較器 if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) { beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE); }
查看這個類的介紹可以看到這個類是OrderComparator的派生,OrderComparator是用來對Orderd或者@Order等內部的值進行比較,內部源碼我們不做介紹,就是獲取值然後進行數值的比較。這個類支持Ordered、@Order、@Priority,這些是我們今天要介紹的主要內容瞭,@Primary初始看起來沒有關聯,後面我們再介紹為什麼會有他。
/** * {@code AnnotationAwareOrderComparator} is an extension of * {@link OrderComparator} that supports Spring's * {@link org.springframework.core.Ordered} interface as well as the * {@link Order @Order} and {@link javax.annotation.Priority @Priority} * annotations, with an order value provided by an {@code Ordered} * instance overriding a statically defined annotation value (if any). * * <p>Consult the Javadoc for {@link OrderComparator} for details on the * sort semantics for non-ordered objects. * * @author Juergen Hoeller * @author Oliver Gierke * @author Stephane Nicoll * @since 2.0.1 * @see org.springframework.core.Ordered * @see org.springframework.core.annotation.Order * @see javax.annotation.Priority */ public class AnnotationAwareOrderComparator extends OrderComparator {
2.Orderd、@Order、@Priority、@Primary
這一個接口和三個註解比較簡單,我粗略介紹一下,不做具體的介紹。總的來說都是用來做bean加載的排序。
- ①orderd接口,實現Oderd接口的話要實現int getOrder();這個方法,返回一個整數值,值越小優先級越高。
- ②@Order裡面存儲瞭一個值,默認為Integer的最大值,同樣值越小優先級越高。要註意@Order隻能控制組件的加載順序,不能控制註入的優先級。但是能控制List 裡面存放的XXX的順序,原因是當通過構造函數或者方法參數註入進某個List時,Spring的DefaultListableBeanFactory類會在註入時調用AnnotationAwareOrderComparator.sort(listA)幫我們去完成根據@Order或者Ordered接口序值排序。@Order更加適用於集合註入的排序。
- ③@Priority與@Order類似,@Order是Spring提供的註解,@Priority是JSR 250標準,同樣是值越小優先級越高。但是兩者還是有一定卻別,@Priority能夠控制組件的加載順序,因此@Priority側重於單個註入的優先級排序。此外@Priority優先級比@Order更高,兩者共存時優先加載@Priority。
- ④@Primary是優先級最高的,如果同時有@Primary以及其他幾個的話,@Primary註解的Bean會優先加載。
這個優先級可以在Spring源碼中的DefaultListableBeanFactory類看出,從下面的代碼可以看到優先確定Primary的,然後在根據權重來確定,Order與Priority隻是不同規范定義的兩種註解,兩者效果是類似的。這裡再額外說一下@Qualifier註解,如果beanName和@Qualifier一致,那麼這個優先級更高,有興趣的可以自己去源碼探索一下,後面文章也會詳細介紹@Qualifier這個註解。
/** * Determine the autowire candidate in the given set of beans. * <p>Looks for {@code @Primary} and {@code @Priority} (in that order). * @param candidates a Map of candidate names and candidate instances * that match the required type, as returned by {@link #findAutowireCandidates} * @param descriptor the target dependency to match against * @return the name of the autowire candidate, or {@code null} if none found */ @Nullable protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) { Class<?> requiredType = descriptor.getDependencyType(); String primaryCandidate = determinePrimaryCandidate(candidates, requiredType); if (primaryCandidate != null) { return primaryCandidate; } String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType); if (priorityCandidate != null) { return priorityCandidate; } // Fallback for (Map.Entry<String, Object> entry : candidates.entrySet()) { String candidateName = entry.getKey(); Object beanInstance = entry.getValue(); if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) || matchesBeanName(candidateName, descriptor.getDependencyName())) { return candidateName; } } return null; }
3.測試
測試函數如下所示,隻有簡單的兩行,創建Spring上下文獲取bean,調用s()方法。具體的實現看OrderTest類。
@Test public void test4() { ApplicationContext applicationContext = new AnnotationConfigApplicationContext(OrderTest.class); ((OrderTest)applicationContext.getBean("orderTest")).test.s(); }
①使用兩個@Order註解
如下所示,我們分別給Test1和Test2t設置@Order為3和2,執行後拋出異常,原因是@Order不能控制註入的優先級。
@Configuration public class OrderTest { public interface Test { void s(); } @Service @Order(3) public class Test1 implements Test { @Override public void s() { System.out.println(1); } } @Service @Order(2) public class Test2 implements Test { @Override public void s() { System.out.println(2); } } @Autowired public Test test; }
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'orderTest': Unsatisfied dependency expressed through field 'test'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.huang.config.OrderTest$Test' available: expected single matching bean but found 2: com.huang.config.OrderTest$Test2,com.huang.config.OrderTest$Test1
②使用兩個註解以及一個@Primary註解
我們再上面基於給Test1添加@Primary,由於@Primary優先級更高,因此可以控制註入的優先級,所以 Test1的實例被註入瞭,輸出結果為1。
@Configuration public class OrderTest { public interface Test { void s(); } @Service @Order(3) @Primary public class Test1 implements Test { @Override public void s() { System.out.println(1); } } @Service @Order(2) public class Test2 implements Test { @Override public void s() { System.out.println(2); } } @Autowired public Test test; }
1
Process finished with exit code 0
③既有@Order,又有@Priority
既有@Order,又有@Priority時,可以看到雖然@Order的值更小,之前介紹值越小優先級越高,但是由於@Priority優先級更高,所以註入瞭Test1。
@Configuration public class OrderTest { public interface Test { void s(); } @Service @Priority(3) public class Test1 implements Test { @Override public void s() { System.out.println(1); } } @Service @Order(2) public class Test2 implements Test { @Override public void s() { System.out.println(2); } } @Autowired public Test test; }
1
Process finished with exit code 0
④兩個@Priority註解
兩個@Priority註解同時存在時,值越小優先級越高,因此優先註入的是Test2。
@Configuration public class OrderTest { public interface Test { void s(); } @Service @Priority(4) public class Test1 implements Test { @Override public void s() { System.out.println(1); } } @Service @Priority(3) public class Test2 implements Test { @Override public void s() { System.out.println(2); } } @Autowired public Test test; }
2
Process finished with exit code 0
⑤使用@Order控制集合註入
修改要註入的為Test集合
@Configuration public class OrderTest { public interface Test { void s(); } @Service @Order(2) public class Test1 implements Test { @Override public void s() { System.out.println(1); } } @Service @Order(1) public class Test2 implements Test { @Override public void s() { System.out.println(2); } } @Autowired public List<Test> testList; }
修改測試代碼
@Test public void test4() { ApplicationContext applicationContext = new AnnotationConfigApplicationContext(OrderTest.class); ((OrderTest)applicationContext.getBean("orderTest")).testList.get(0).s(); }
執行結果如下所示,可以看到@Order值小的,優先級更高,在集合的前邊。
2
Process finished with exit code 0
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- Spring應用拋出NoUniqueBeanDefinitionException異常的解決方案
- 淺談SpringBoot Bean加載優先級的問題
- Spring中@order註解用法實戰教程
- Spring中如何使用Comparator接口
- Java Spring 控制反轉(IOC)容器詳解