溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么用Spring中的@Order進(jìn)行排序

發(fā)布時(shí)間:2022-08-17 16:30:46 來源:億速云 閱讀:156 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹“怎么用Spring中的@Order進(jìn)行排序”的相關(guān)知識,小編通過實(shí)際案例向大家展示操作過程,操作方法簡單快捷,實(shí)用性強(qiáng),希望這篇“怎么用Spring中的@Order進(jìn)行排序”文章能幫助大家解決問題。

Spring @Order進(jìn)行排序

直接上代碼

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";
        }
    }
}

結(jié)果如下:

[B, A, C]

原理解析:

AnnotationAwareOrderComparator繼承自O(shè)rderComparator

實(shí)際比較的方法如下

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中關(guān)于Order的那點(diǎn)事

本文閱讀源碼版本為spring5.3.1

為啥要用Order

spring是一個(gè)大量使用策略設(shè)計(jì)模式的框架,這意味著有很多相同接口的實(shí)現(xiàn)類,如果不手動指定順序的話,那么使用時(shí)肯定會有問題。而Order給我們提供了一種編碼設(shè)置順序的可能。

關(guān)于Order

spring中提供了多種方式來設(shè)置優(yōu)先級,有Ordered,PriorityOrdered接口,有Order注解,除此之外,spring4.1以后,還可以使用Priority注解。下面我將針對這幾種用法從源碼的角度來進(jìn)行分析。

Ordered,PriorityOrdered接口 

public interface Ordered {
    /**
     * 最高優(yōu)先值
     */
    int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
    /**
     * 最低優(yōu)先值
     */
    int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
    int getOrder();
}

PriorityOrdered繼承了Ordered,但并未提供任何方法,這是一個(gè)標(biāo)記了優(yōu)先級的接口,和Ordered相比,PriorityOrdered就是高人一等,spring中提供了比較器OrderComparator,可以通過構(gòu)建一個(gè)OrderComparator,調(diào)用其compare方法,不過OrderComparator提供了一個(gè)靜態(tài)sort方法,我們無需自己構(gòu)建OrderComparator了,排序的結(jié)果按照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;
        }
    }
}

小結(jié)

  • PriorityOrdered優(yōu)先級比Ordered高,與設(shè)置的order值無關(guān)。

  • 若兩個(gè)對象都實(shí)現(xiàn)了Ordered或PriorityOrdered接口,那么設(shè)置的order值越小,優(yōu)先值越高。

  • 若沒有實(shí)現(xiàn)Ordered或PriorityOrdered接口,默認(rèn)是最低的優(yōu)先級。

OrderComparator#compare解讀

在看compare之前,我覺得將OrderSourceProvider這個(gè)函數(shù)式接口放在前面講解一下,閱讀源碼時(shí)會更清晰一點(diǎn)。 

    @FunctionalInterface
    public interface OrderSourceProvider {
        /**
         * 對給定對象校驗(yàn)并返回一個(gè)新的對象
         */
        @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) {
        // 這里會判斷是否實(shí)現(xiàn)了PriorityOrdered接口
        boolean p1 = (o1 instanceof PriorityOrdered);
        boolean p2 = (o2 instanceof PriorityOrdered);
        // 這里會看到根本沒有比較order的值,只要實(shí)現(xiàn)PriorityOrdered接口,就會排在前面
        if (p1 && !p2) {
            return -1;
        }else if (p2 && !p1) {
            return 1;
        }
        // 獲取對象設(shè)置的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) {
                // 如果返回的是數(shù)組
                if (orderSource.getClass().isArray()) {
                    for (Object source : ObjectUtils.toObjectArray(orderSource)) {
                        // 只要找到對象設(shè)置的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時(shí),返回值最大
        return Ordered.LOWEST_PRECEDENCE;
    }
    protected Integer findOrder(Object obj) {
        // 沒有實(shí)現(xiàn)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;
        }
        // 調(diào)用父類的findOrder方法無法找到設(shè)定的order值時(shí)
        return findOrderFromAnnotation(obj);
    }
    
    private Integer findOrderFromAnnotation(Object obj) {
        AnnotatedElement element = (obj instanceof AnnotatedElement ? (AnnotatedElement) obj : obj.getClass());
        // 對整個(gè)類型層次結(jié)構(gòu)執(zhí)行完整搜索,包括父類和接口
        MergedAnnotations annotations = MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY);
        // 獲取注解中設(shè)置的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 {
    }
}

小結(jié)

@Order與@Priority注解放置在類,接口或參數(shù)上,可以被繼承;它們之間是可以互相替換的關(guān)系。

應(yīng)用

spring源碼中有很多地方都顯式的調(diào)用AnnotationAwareOrderComparator的sort方法,也有一些地方調(diào)用的OrderComparator的sort方法,大家自己可以找找看。

我這里發(fā)現(xiàn)了一點(diǎn)有意思的地方,我們?nèi)绻x多個(gè)ControllerAdvice的bean,分別通過實(shí)現(xiàn)Ordered,PriorityOrdered接口來定義執(zhí)行時(shí)的順序,會發(fā)現(xiàn)上面我們總結(jié)的 PriorityOrdered優(yōu)先級就是比Ordered高 這一點(diǎn)不成立,其實(shí)只是spring將ControllerAdvice相關(guān)信息封裝了一下欺騙了我們。我看的源碼的版本是5.3.1,低于5.2版本的不會發(fā)生這樣的事情。這里我們就來看看5.2版本前后源碼有哪些變化,導(dǎo)致了這個(gè)現(xiàn)象的發(fā)生。

這里就拿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要窄一點(diǎn)的,它不支持注解,那么上面這樣的改動是不是就意味著我們定義ControllerAdvice時(shí),就不能使用@Order與@Pri-ority呢?

其實(shí)它是支持的,ControllerAdviceBean#findAnnotatedBeans方法中會將我們定義的Con-trollerAdvice類包裝成ControllerAdviceBean,而ControllerAdviceBean是實(shí)現(xiàn)了Ordered接口的,那么OrderComparator#sort方法要想支持使用注解,ControllerAdviceBean的getOrder方法中就必須干點(diǎn)啥,分析了挺多,我們還是看源碼實(shí)現(xiàn)吧。

    // 5.2版本后
    public int getOrder() {
        if (this.order == null) {
            String beanName = null;
            Object resolvedBean = null;
            // 這里根據(jù)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();
            }
            // 這里只判斷了是否實(shí)現(xiàn)了Ordered接口,并沒有對實(shí)現(xiàn)PriorityOrdered作特殊處理
            // 這里優(yōu)先判斷是否實(shí)現(xiàn)了Ordered接口,如果同時(shí)使用注解的話將被忽略
            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的實(shí)現(xiàn)應(yīng)該被忽略
    @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 ());
        }
    }
}

關(guān)于“怎么用Spring中的@Order進(jìn)行排序”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識,可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會為大家更新不同的知識點(diǎn)。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI