Spring笔记-@Order注解和Ordered接口解析

目录
  • @Order注解和Ordered接口
    • 1.OrderUtils
    • 2.Ordered接口
    • 3.OrderComparator
    • 4.AnnotationAwareOrderComparator
    • 5.Bean注册顺序
  • Spring的Orderd接口及@Order,@Primary,@Priority三个注解介绍
    • 1.如何发现Orderd接口及@Order、@Primary、@Priority
    • 2.Orderd、@Order、@Priority、@Primary
    • 3.测试

@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

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

(0)

相关推荐

  • 基于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如何使用

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

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

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

  • Spring笔记-@Order注解和Ordered接口解析

    目录 @Order注解和Ordered接口 1.OrderUtils 2.Ordered接口 3.OrderComparator 4.AnnotationAwareOrderComparator 5.Bean注册顺序 Spring的Orderd接口及@Order,@Primary,@Priority三个注解介绍 1.如何发现Orderd接口及@Order.@Primary.@Priority 2.Orderd.@Order.@Priority.@Primary 3.测试 @Order注解和Ord

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

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

  • Spring @Order注解使用详解

    目录 前言 例子一 例子二 例子三 实例论证 源码分析 前言 很长一段时间没有写博客了,今天一时心血来潮,突然写出这篇文章就很突兀.但是看到网上关于Spring的@Order注解的不是特别准确的结论,想着还是表达一下看法,当然也是通过写文章来让自己的思路更清晰一点,如果有不是很赞同的部分,希望可以一起讨论. 首先先说结论:Spring的@Order注解(或者实现Ordered接口.或者实现PriorityOrdered接口),不决定Bean的实例化顺序和执行顺序,更不会影响Spring的Bean

  • 详解Spring bean的注解注入之@Autowired的原理及使用

    一.@Autowired 概念: @Autowired 注释,它可以对类成员变量.方法及构造函数进行标注,完成自动装配的工作. 通过 @Autowired的使用来消除 set ,get方法. 在使用@Autowired之前,我们对一个bean配置起属性时,用的是 <property name="属性名" value=" 属性值"/> 使用@Autowired之后,我们只需要在需要使用的地方使用一个@Autowired 就可以了. 代码使用: public

  • Spring中@DependsOn注解的作用及实现原理解析

    本文给大家讲解Spring中@DependsOn注解的作用及实现原理! 官方文档解释 Beans on which the current bean depends. Any beans specified are guaranteed to be created by the container before this bean. Used infrequently in cases where a bean does not explicitly depend on another thro

  • Spring中@Configuration注解修改的类生成代理原因解析

    目录 前言 说明 场景 处理分析 总结 前言 在Spring中只要被@Configuration注解修饰的类,Spring就会为其生成代理对象,至于这样做的主要原因就是为了解决生成对象的单例问题. 说明 实际上作者在ConfigurationClassEnhancer这个类也有注解说明 场景 如果Spring不做处理,下面输出的一定的是false,但是实际上输出的结果是true,那么只有可能是代理类做了特殊处理. @Configuration public class MyConfigurati

  • spring注解识别一个接口的多个实现类方法

    1.比如有一个接口如下: public interface Filter { public String doFilter(String param); } 2.有如下接口实现类: public class TimeFilter implements Filter { @Override public String doFilter(String param) { return param.replace("aa", "bb"); } }  3.使用方式:在接口实现

  • 参数校验Spring的@Valid注解用法解析

    参数校验Spring的@Valid注解 @Valid 注解通常用于对象属性字段的规则检测. 以新增一个员工为功能切入点,以常规写法为背景,慢慢烘托出 @Valid 注解用法详解. 那么,首先,我们会有一个员工对象 Employee,如下 : public class Employee { /** 姓名 */ public String name; /** 年龄 */ public Integer age; public String getName() { return name; } publ

  • Spring AOP结合注解实现接口层操作日志记录

    目录 1.表和实体设计 1.实体设计 2.表结构设计 2.日志注解 3.核心AOP类 4.用到的工具类 5.测试类 6.测试结果 1.表和实体设计 1.实体设计 实体基类 @Data //映射将仅应用于其子类 @MappedSuperclass //指定要用于实体或映射超类的回调侦听器类.此注释可以应用于实体类或映射的超类. @EntityListeners(AuditingEntityListener.class) public class BaseEntity implements Seri

  • spring boot@EnableXXXX注解编程模型讲解

    目录 @EnableXXXX编程模型 @Import注解处理时机节点(@Confguration注解的类处理) ImportSelector ImportBeanDefinitionRegistrar处理 @Configurtion注解的类处理 统一调用配置类解析出来的信息注册BeanDefinition @EnableXXXX编程模型 在spring boot中,@EnableXXX注解的功能通常是开启某一种功能.根据某些外部配置自动装配一些bean,来达到开启某些功能的目的.光说很抽象,要具

随机推荐