巧用Spring中的@Order进行排序

目录
  • Spring @Order进行排序
  • Spring中关于Order的那点事
    • 为啥要用Order
    • 关于Order
    • OrderComparator#compare解读
    • @Order与@Priority
    • 应用

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 ());
        }
    }
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Springboot中的@Order如何使用

    在spring-boot 2.6.2下测试,@Order并不会影响bean的装载顺序,声明了@Component的类,无论是构造方法.@PostConstruct注解声明的方法,还是实现的InitializingBean接口中的afterPropertiesSet()方法,如果beanClass位于同样的目录层级,这些方法的调用只会受到className的顺序影响: @Component @Slf4j @Order(2) public class Bean1 implements Initial

  • 基于SpringBoot开机启动与@Order注解

    目录 SpringBoot开机启动与@Order注解 spring @Order标记 @Order标记定义了组件的加载顺序 使用spring 3.x 和spring 4.x 的例子 SpringBoot开机启动与@Order注解 package com.example.zcw.runner; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.ApplicationArguments; import org.spri

  • SpringBoot之Order注解启动顺序说明

    目录 Order注解启动顺序 order的规则 见下 它们的启动日志 @Order注解提供消费顺序 @org.springframework.core.annotation.Order Order注解启动顺序 order的规则 order的值越小,优先级越高 order如果不标注数字,默认最低优先级,因为其默认值是int最大值 该注解等同于实现Ordered接口getOrder方法,并返回数字. @Retention(RetentionPolicy.RUNTIME) @Target({Eleme

  • 巧用Spring中的@Order进行排序

    目录 Spring @Order进行排序 Spring中关于Order的那点事 为啥要用Order 关于Order OrderComparator#compare解读 @Order与@Priority 应用 Spring @Order进行排序 直接上代码 public class OrderAnnotationTest {     public static void main(String[] args) {         A a = new A();         B b = new B

  • Spring中@order注解用法实战教程

    目录 前言 一.观察@order源码 二.@order实战 三.@order失效原因 四.解决排序问题 五.排序源码分析 六.@AutoConfigureOrder 总结 前言 @order注解是spring-core包下的一个注解,@Order的作用是定义Spring IOC容器中Bean的执行顺序的优先级(这里的顺序也可以理解为存放到容器中的先后顺序).开发过程当中有时候经常会出现配置依赖关系,例如注入A对象使用了 @ConditionalOnBean(B.class),意思是要求容器当中必

  • Oracle数据库中ORDER BY排序和查询按IN条件的顺序输出

    ORDER BY非稳定的排序 提一个问题: oracle在order by 排序时,是稳定排序算法吗? 发现用一个type进行排序后,做分页查询,第一页的数据和第二页的数据有重复 怀疑是order by 时,两次排列的顺序不一致 看到业务描述的问题可以得到的结论order by排序不稳定,还有第一个印象就是,type肯定是不唯一的,并且没有索引吧. 这里先科普下排序的稳定性,举个最简单的例子,1,2,3,1,4,5 排序 排序的结果是1,1,2,3,4,5,这时候观察这个1,如果第一个1还是排序

  • 解决mybatis中order by排序无效问题

    1.#将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号.如:order by #{user_id},如果传入的值是111,那么解析成sql时的值为order by "111", 如果传入的值是id,则解析成的sql为order by "id". 2.$将传入的数据直接显示生成在sql中.如:order by ${user_id},如果传入的值是111,那么解析成sql时的值为order by 111, 如果传入的值是id,则解析成的sql为order b

  • 详解MySQL中Order By排序和filesort排序的原理及实现

    目录 1.Order By原理 2.filesort排序算法 3.优化排序 1.Order By原理 MySQL的Order By操作用于排序,并且会有多种不同的排序算法,他们的性能都是不一样的. 假设有一个表,建表的sql如下: CREATE TABLE `obtest` ( `id` BIGINT NOT NULL AUTO_INCREMENT, `a` VARCHAR ( 100 ) NOT NULL, `b` VARCHAR ( 100 ) NOT NULL, `c` VARCHAR (

  • MySQL中order by排序语句的原理解析

    order by 是怎么工作的? 表定义 CREATE TABLE `t1` ( `id` int(11) NOT NULL, `city` varchar(16) NOT NULL, `name` varchar(16) NOT NULL, `age` int(11) NOT NULL, `addr` varchar(128) DEFAULT NULL, PRIMARY KEY (`id`), KEY `city` (`city`)) ENGINE=InnoDB; SQL语句可以这样写: se

  • 详解spring中使用solr的代码实现

    在介绍solr的使用方法之前,我们需要安装solr的服务端集群.基本上就是安装zookeeper,tomcat,jdk,solr,然后按照需要配置三者的配置文件即可.由于本人并没有具体操作过如何进行solr集群的搭建.所以关于如何搭建solr集群,读者可以去网上查看其它资料,有很多可以借鉴.这里只介绍搭建完solr集群之后,我们客户端是如何访问solr集群的. 之前介绍过,spring封装nosql和sql数据库的使用,都是通过xxxTemplate.solr也不例外. 我们需要引入solr的j

  • 详解JAVA Spring 中的事件机制

    说到事件机制,可能脑海中最先浮现的就是日常使用的各种 listener,listener去监听事件源,如果被监听的事件有变化就会通知listener,从而针对变化做相应的动作.这些listener是怎么实现的呢?说listener之前,我们先从设计模式开始讲起. 观察者模式 观察者模式一般包含以下几个对象: Subject:被观察的对象.它提供一系列方法来增加和删除观察者对象,同时它定义了通知方法notify().目标类可以是接口,也可以是抽象类或具体类. ConcreteSubject:具体的

  • Spring中如何使用Comparator接口

    我们先来回顾下Comparator接口在我们日常开发中的作用,Comparator比较器接口可以将自身传递给排序方法(比如Collections.sort或Arrays.sort),以便对排序顺序进行精确控制.比如: List<Integer> intList = Arrays.asList(2, 3, 1); Collections.sort(intList, (o1, o2) -> { return o2-o1; }); 输出 [3, 2, 1] Comparator可以用来控制某些

  • Spring中bean集合注入的方法详解

    目录 Map注入 List注入 Set注入 数组注入 应用 哈喽大家好啊,我是Hydra. Spring作为项目中不可缺少的底层框架,提供的最基础的功能就是bean的管理了.bean的注入相信大家都比较熟悉了,但是有几种不太常用到的集合注入方式,可能有的同学会不太了解,今天我们就通过实例看看它的使用. 首先,声明一个接口: public interface UserDao { String getName(); } 然后定义两个类来分别实现这个接口,并通过@Component注解把bean放入s

随机推荐