目录
1.循环依赖
循环依赖就是循环引用,例如:
>依赖自身
>A依赖于B,B又依赖于A
>A依赖于B,B依赖于C,C又依赖于A
以上几种情况都会导致循环依赖
2.出现场景
>构造器的循环依赖
使用构造方法的方式来注入依赖,并且类A中依赖类B,类B也同时依赖类A,这样两个类都无法正常进行Bean的创建,就会抛出异常:BeanCurrentlyInCreationException
@Component
public class A {
private B b;
@Autowired
public A(B b) {
this.b = b;
}
}
@Component
public class B {
private A a;
@Autowired
public B(A a) {
this.a = a;
}
}
>field属性的循环依赖
3.如何检测
在创建bean的时候可以给该bean打标签,如果递归调用回来发现正在创建的话,即说明发生了循环依赖
4.Spring单例对象初始化步骤
createBeanInstance实例化 调用对象的构造方法实例化对象
populateBean 这一步主要是对多bean的依赖属性进行填充
initializeBean 调用applicationContext.xml中的init初始化方法
5.循环依赖的解决
Spring是通过三级缓存来解决循环依赖的
一级缓存: singletonObjects存储的是所有创建好了的单例Bean
二级缓存:earlySingletonObjects完成实例化,但是还未进行属性注入及初始化的对象
三级缓存:singletonFactories提前暴露的一个单例工厂,二级缓存中存储的就是从这个工厂中获取到的对象。
通过查看DefaultSingletonBeanRegistry类的源码可以看到,三级缓存
Spring通过三级缓存解决了循环依赖,其中一级缓存为单例池(singletonObjects),二级缓存为早期曝光对象earlySingletonObjects,三级缓存为早期曝光对象工厂(singletonFactories)。
流程如下:
1.首先
A
完成初始化第一步并将自己
提前曝光
出来(通过
ObjectFactory
将自己提前曝光),在
初始化的时候,发现自己依赖对象
B
,此时就会去尝试
get(B)
,这个时候发现
B
还没有被创建
出来;
2.
然后
B
就走创建流程,在
B
初始化的时候,同样发现自己依赖
C
,
C
也没有被创建出来;
3.
这个时候
C
又开始初始化进程,但是在初始化的过程中发现自己依赖
A
,于是尝试
get(A)
。这
个时候由于
A
已经添加至缓存中(一般都是添加至三级缓存
singletonFactories
),通过
ObjectFactory
提前曝光,所以可以通过
ObjectFactory#getObject()
方法来拿到
A
对象。
C
拿
到
A
对象后顺利完成初始化,然后将自己添加到一级缓存中;
4.
回到
B
,
B
也可以拿到
C
对象,完成初始化,
A
可以顺利拿到
B
完成初始化。到这里整个链路
就已经完成了初始化过程了。