最近在梳理项目中的基础设施模块,希望将自动扫描(@ComponentScan)的方式,改为基于 @Configuration 的方式,这样在编写测试类的时候,能够有选择的对基础设施相关的 Bean 进行装配。通过这样的梳理和思考,能够提升模块的内聚性。
但是,在操作的过程中基于实际情况,认为某些 Bean 的实例化有先后顺序,因此想当然的认为能够通过 @Order 注解(或者 Ordered 接口)来实现 Bean 实例化的先后顺序。其实不然。
我们定义了如下 3 个 Bean。分别实现 Ordered 接口,并分别设置序号为返回 3、2、1。按预期的效果,应该值越小,越先初始化。
@Slf4j
@Component
public class AOrderBean implements Ordered {
public AOrderBean() {
log.info("AOrderBean created");
}
@Override
public int getOrder() {
return 3;
}
}
@Slf4j
@Component
public class BOrderBean implements Ordered {
public BOrderBean() {
log.info("BOrderBean created");
}
@Override
public int getOrder() {
return 2;
}
}
@Slf4j
@Component
public class COrderBean implements Ordered {
public COrderBean() {
log.info("COrderBean created");
}
@Override
public int getOrder() {
return 1;
}
}
程序运行结果:
从运行结果分析,Ordered 接口并没有达到预期的效果。
通过分析 Spring 源码,发现基于 Order 的顺序性问题是通过 AnnotationAwareOrderComparator 实现的。该比较器调用的地方,就是 Order 生效的地方。
通过全局搜索,在 Spring 和 Spring Boot 项目中,有如下地方使用到了该类:
IDEA查看指定类在哪些地方用到快捷键-XQLEE'Blog
如上图所示,在spring-context模块中有如下接口对 Order 生效:
如上如所示,SpringFactoriesLoader 按指定类型加载对应配置时,可以生效。
全局搜索该方法得到如下:
注意:spring boot 3.x已经弃用spring.factories了
通过 @Aspect 对相同的调用点进行增强时,当存在多个增强同时希望控制其顺序时,可以使用 @Order
@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 将时有序的。
BeanPostProcessor
BeanFactoryPostProcessor
ApplicationContext
中通过实现 Ordered 接口,能够控制顺序,但是对于 @Order
注解,暂不支持。限于篇幅,上边的查找可能并不全面,比如并没有查找 AnnotationAwareOrderComparator
的父类 OrderComparator
的调用点。但是可以得出一个结论:
Order 并不能改变 spring 实例化 Bean 的顺序。只能改变 Bean 运行顺序。因此,在实际配置中, Bean 之间的装配,依赖 spring 的默认装配机制来保证。对于间接依赖,可以通过
@DependsOn
注解进行微调。
对于 spring boot 的 *AutoConfiguration 来说,可以通过
@AutoConfigureBefore
@AutoConfigureAfter
@AutoConfigureOrder
来进行装配顺序的控制。该方式在基于扫描装配下的 @Configuration
模式,并不生效。
https://blog.xqlee.com/article/2504211341286705.html