关于springboot:Spring-Order-注解你可能理解错了

9次阅读

共计 2441 个字符,预计需要花费 7 分钟才能阅读完成。

1. 问题形容

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

2. 问题剖析

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

@Slf4j
@Component
public class AOrderBean implements Ordered {public AOrderBean() {log.info("init AOrderBean");
    }

    @Override
    public int getOrder() {return 3;}
}

@Slf4j
@Component
public class BOrderBean implements Ordered {public BOrderBean() {log.info("init BOrderBean");
    }

    @Override
    public int getOrder() {return 2;}
}

@Slf4j
@Component
public 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 拆卸汇合类型

@Component
public 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 模式,并不失效。

正文完
 0