1. 问题形容

最近在梳理我的项目中的基础设施模块,心愿将主动扫描(@ComponentScan)的形式,改为基于 @Configuration 的形式,这样在编写测试类的时候,可能有抉择的对基础设施相干的 Bean 进行拆卸。通过这样的梳理和思考,可能晋升模块的内聚性。
然而,在操作的过程中基于理论状况,认为某些 Bean 的实例化有先后顺序,因而想当然的认为可能通过 @Order 注解(或者 Ordered 接口)来实现 Bean 实例化的先后顺序。其实不然。

2. 问题剖析

咱们定义了如下 3 个 Bean。别离实现 Ordered 接口,并别离返回 3、2、1。按预期的成果,应该值越小,越先初始化。

@Slf4j@Componentpublic class AOrderBean implements Ordered {    public AOrderBean() {        log.info("init AOrderBean");    }    @Override    public int getOrder() {        return 3;    }}@Slf4j@Componentpublic class BOrderBean implements Ordered {    public BOrderBean() {        log.info("init BOrderBean");    }    @Override    public int getOrder() {        return 2;    }}@Slf4j@Componentpublic class COrderBean implements Ordered {    public COrderBean() {        log.info("init COrderBean");    }    @Override    public int getOrder() {        return 1;    }}

程序运行后果:

从运行后果剖析,Ordered 接口并没有达到预期的成果。

3. Order排序的原理

通过剖析 Spring 源码,发现基于 Order 的程序性问题是通过 AnnotationAwareOrderComparator 实现的。该比拟器调用的中央,就是 Order 失效的中央。

4. Order 失效的场景

通过全局搜寻,在 Spring 和 Spring Boot 我的项目中,有如下中央应用到了该类:

4.1 spring-context 模块

如上图所示,在spring-context模块中有如下接口对 Order 失效:

  • Condition 接口
  • DeferredImportSelector 导入内部拆卸配置
  • ApplicationListener
  • EventListenerFactory
  • SchedulingConfigurer

4.2 spring-core 模块

如上如所示,SpringFactoriesLoader 按指定类型加载对应配置时,能够失效。全局搜寻该办法失去如下:

Spring Boot 在启动的时候对 spring.factories 中的相干配置进行读取时,就应用了该办法。

4.3 spring-test 模块

如上图所示:

  • ApplicationContextInitializer
  • TestExecutionListener

4.4 spring-web 模块

4.5 spring-boot

spring-boot 中能够被影响的有:

  • ApplicationRunner
  • CommandLineRunner
  • ErrorViewResolver
  • getSpringFactoriesInstances 办法调用的中央
  • ApplicationListener
  • FailureAnalyzer
  • TypeSupplier
  • ErrorPageRegistrar
  • WebServerFactoryCustomizer
  • ServletContextInitializer

5. 其余已知失效场景

5.1 @Aspect 注解

通过 @Aspect 对雷同的调用点进行加强时,当存在多个加强同时心愿管制其程序时,能够应用 @Order

5.2 拆卸汇合类型

@Componentpublic class FilterChain {    private List<Filter> filterList;    public FilterChain(List<Filter> filterList) {        System.out.println(filterList.getClass().getSimpleName());        this.filterList = filterList;    }    @PostConstruct    public void init() {        filterList.stream().map(Filter::getName).forEach(System.out::println);    }}

如上述代码所示,通过汇合类型拆卸,将所有实现了 Filter 接口的 Bean,拆卸到 filterList 时,如果各个 Filter 对应的 Bean 实现了 @Order,最终 List 中的 Bean 将时有序的。

5.3 PostProcessor

  • BeanPostProcessor
  • BeanFactoryPostProcessor
    上述两类处理器,在主动拆卸的 ApplicationContext 中通过实现 Ordered 接口,可能管制程序,然而对于 @Order 注解,暂不反对。

6. 论断

限于篇幅,上边的查找可能并不全面,比方并没有查找 AnnotationAwareOrderComparator 的父类 OrderComparator 的调用点。然而能够得出一个论断:

Order 并不能扭转 spring 实例化 Bean 的程序。只能扭转 Bean 运行程序。因而,在理论配置中, Bean 之间的拆卸,依赖 spring 的默认拆卸机制来保障。对于间接依赖,能够通过 @DependsOn 注解进行微调。

对于 spring boot 的 *AutoConfiguration 来说,能够通过

  • @AutoConfigureBefore
  • @AutoConfigureAfter
  • @AutoConfigureOrder

来进行拆卸程序的管制。该形式在基于扫描拆卸下的 @Configuration 模式,并不失效。