spring的三级缓存如何解决循环依赖
# 什么是循环依赖?
# 官方解释
虽然官方提倡使用构造方法来依赖注入,但是构造方法解决不了循环依赖,可以使用setter方法来解决。
# 个人理解
a对象持有b对象的引用,b对象持有a对象的引用。这样说好像察觉不到问题,可以试想下当我们自己创建a对象时,是不是先要调用a对象的构造方法,然后再进行属性的填充,这才是一个完整的a对象,但是当调用对象a的构造方法后,发现需要先拿到完整的b对象,则需要先完成b对象的创建,然后调用b对象的构造方法,b对象进行属性填充时,发现需要a对象,然后无限的循环下去。。。
而这个问题在spring中,a,b对象都会存放到容器中,就会导致a,b对象一直处于创建中的状态,彼此等待对方完整的对象。
# 循环依赖的产生的条件
# 单例bean的加载顺序
读取bean的元信息(xml,注解),构建一个map存储 <beanName,BeanDefinition> 利用beanName和BeanDefinition反射构成一个实例对象,只是调用构造函数,没有填充属性 ,再经过填充属性 构成一个完整的bean对象,最后放入单例bean map中。
但是spring容器可不止一个,如果出现了互相依赖的情况,比如下面
// A 持有b
public class A {
private B b;
}
// B持有a
public class B {
private A a;
}
2
3
4
5
6
7
8
9
10
11
12
我们再按照刚才的单例bean的加载顺序看,先加载A,利用A的BeanDefinition信息反射出a对象,然后填充属性,发现需要对象b,然后会去容器中找有没有单例b对象; 如果没有,则创建b对象,仍然利用B的BeanDefinition反射创建b对象,然后填充属性,发现a对象是需要的属性,再去容器中找a对象,然而a对象并没有实例化完全,存放到单例map中,所以也找不到a对象,所以创建b的过程中也会去创建a,不断地循环往复,最后抛出异常
# 循环依赖造成的问题
# 解决循环依赖的原理
还是上面的例子,在实例化A对象后,及反射之后,会将不完整的对象存放到map中,spring把它叫做三级缓存,这样在b对象填充属性时,可以从三级缓存中获取到a对象从而完成属性填充,完成bean的创建,存放到单例map中。
# 结合spring源码,分析spring如何解决循环依赖
DefaultSingletonBeanRegistry.java
/** Cache of singleton objects: bean name to bean instance. 一级缓存*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. 三级缓存*/
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
// 首先看单例map(一级缓存) 有没有bean对象
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 如果一级缓存没有 那么从三级缓存里面获取
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42