首頁 > 軟體

Spring筆記-@Order註解和Ordered介面解析

2022-08-17 22:02:01

@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

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com