Spring|Spring @Order 注解,你可能理解错了
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
【Spring|Spring @Order 注解,你可能理解错了】
文章图片
如上如所示,SpringFactoriesLoader 按指定类型加载对应配置时,可以生效。全局搜索该方法得到如下:
文章图片
Spring Boot 在启动的时候对 spring.factories 中的相关配置进行读取时,就使用了该方法。
4.3 spring-test 模块
文章图片
如上图所示:
- ApplicationContextInitializer
- TestExecutionListener
文章图片
4.5 spring-boot
文章图片
spring-boot 中可以被影响的有:
- ApplicationRunner
- CommandLineRunner
- ErrorViewResolver
- getSpringFactoriesInstances 方法调用的地方
文章图片
- ApplicationListener
- FailureAnalyzer
- TypeSupplier
- ErrorPageRegistrar
- WebServerFactoryCustomizer
- ServletContextInitializer
通过 @Aspect 对相同的调用点进行增强时,当存在多个增强同时希望控制其顺序时,可以使用 @Order
5.2 装配集合类型
@Component
public class FilterChain {
private List filterList;
public FilterChain(List 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 注解,暂不支持。
AnnotationAwareOrderComparator
的父类 OrderComparator
的调用点。但是可以得出一个结论:Order 并不能改变 spring 实例化 Bean 的顺序。只能改变 Bean 运行顺序。因此,在实际配置中, Bean 之间的装配,依赖 spring 的默认装配机制来保证。对于间接依赖,可以通过 @DependsOn 注解进行微调。对于 spring boot 的
*AutoConfiguration
来说,可以通过- @AutoConfigureBefore
- @AutoConfigureAfter
- @AutoConfigureOrder
推荐阅读
- Spring Cloud Alibaba 大型互联网领域多场景最佳实践吾爱
- SpringBoot|SpringBoot + ES基本项目搭建实例
- 【Spring|【Spring AOP】暴力打通两个切面之间的通信
- SpringBoot|SpringBoot 多环境配置文件切换
- SpringMVC中常用参数校验类注解使用示例教程
- java|java spring mvc处理器映射器介绍
- Spring|Spring Cache缓存框架
- Spring|小唐开始学 Spring Boot——(3)利用mybatis访问数据表
- SpringBoot MongoDB批量删除指定日期前的文件
- Java|Keycloak简单几步实现对Spring Boot应用的权限控制