Spring循环依赖难道只能通过两个Setter注入解决嘛?
先说结论:并不是,可以是一个setter和一个构造器
如果存在循环依赖的对象关系的话,位置靠上的类会先进行生命周期的加载
所以位置靠上的类必须通过set注入其他属性,这样才不会导致循环依赖的报错
前言
其实想到这个问题是由于网上给出的结论是
于是我便好奇了起来:为什么同样是一个通过Setter注入、一个通过构造器注入,会有两种不同的结果呢?
Setter注入与构造器注入
首先我们先明确哪种是setter注入,哪种是构造器注入
//setter注入
@Autowired
private B b;
//构造器注入
@Autowired
public B(A a) {
this.a = a;
}
为什么可以一个通过set和一个通过构造器进行注入也不会导致循环依赖的报错
Spring默认在package从上至下加载Bean的生命周期:创建->注入->初始化完成
我们假设代码中,A所在位置在B上方
先构造器注入,再set注入
条件:A是构造器注入,B是set注入
由于A在上方那么会先进行A的生命周期
A在构造器创建实例时,由于容器中B是空的,导致A无法通过构造器创建对象,于是报错
即使为A添加了空参构造器也无济于事,因为使用构造器注入会先行调用有参构造器
先set注入,再构造器注入
条件:A是set注入,B是构造器注入
由于A在上方还是会先进行A的生命周期
此时,由于A有默认的无参构造器直接创建一个空对象放入了三级缓存,为其注入B时,发现容器中B是空的
于是开始B的生命周期,因为在三级缓存中A已经存在,所以B可以直接根据这个对象去通过构造器实例化
实际上是会因为引用了A,为A执行一个提前引用来创建一个动态代理对象放入二级缓存中,然后将二级缓存中A的代理对象给B,这里为了方便理解直接忽略了动态代理这一层缓存
于是B被创建、注入、初始化完成后加入到一级缓存,此时又来到了A的注入阶段,直接将B注入
于是A经过注入与初始化后,也加入到了IOC容器中,至此不会导致报错
所以可以得出结论:并不是只能通过两个Setter注入才能解决循环依赖的问题,也可以是一个setter和一个构造器
如果存在循环依赖的对象关系的话,位置靠上的类会先进行生命周期的加载
所以位置靠上的类必须通过set注入其他属性,这样才不会导致循环依赖的报错
结论依据:
- 可以通过实现BeanPostProcessor前置调用类的方法将所有Bean依次输出
- 也可以通过实现BeanNameAware的方法输出Bean的名称
总之我们会发现是B先被初始化完成后才初始化完成了A
所以是先创建了A,然后为其注入B时,启动了B的完整的一个生命周期,才会是B先被初始化完成的这样一个结果