Spring为什么需要三级缓存来解决循环依赖
1:为什么会产生循环依赖
public class A {
private B b;
}
public class B {
private A a;
}
在Spring中初始化一个Bean并不是简单的new A()这么简单,需要经过属性注入以及各种后置处理器的处理。
-
1:当我们初始化A的时候,在属性注入会发现需要注入一个对象B
-
2:此时Spring就会去Bean容器中找是不是有这个B对象,如果有就返回
-
3:如果没有就会初始化B,此时在对B进行属性注入的时候发现需要A,又要去初始化A
-
4:此时就造成了循环依赖的情况
2:Spring是如何解决循环依赖的
Spring是使用三级缓存来解决循环依赖的,准确来说应该是四层
- singletonObjects:缓存已经初始化好的Bean
- earlySingletonObjects:缓存正在初始化的Bean
- singletonFactories:缓存的创建Bean的ObjectFactory,表示对象工厂,表示用来创建早期bean对象的工厂。
3:为什么需要三级缓存?二层不行吗?一层呢?
- 1:创建A,将A这个原始对象放入缓存中
- 2:属性注入,需要B对象
- 3:创建B,进行属性注入需要A对象,从缓存中拿到原始对象A进行属性注入
- 4:B创建完毕,然后A从容器中拿到B进行属性注入
为什么B可以使用缓存中的原始A对象,因为单例在容器中只有一个,虽然现在B中的属性A还是个原始对象,也就是还没有进行属性赋值,但是后续在A初始化完之后,B使用的还是这个A对象
从图中可以看出其实我们使用一层缓存也可以解决循环依赖了,也就是earlySingletonObjects,那Spring为什么还要整个三级缓存呢?
在我们创建A的使用,缓存中放的是原始A对象,如果这个对象是要经过AOP代理的对象怎么办呢?本来对象B中的A应该是个代理对象,但是现在缓存拿到的并不是代理对象,还有一个问题,对于B来说,我怎么知道要的这个A对象是普通对象还是需要经过AOP代理过后的代理对象呢?
4: singletonFactories的作用
singletonFactories中存的是某个beanName对应的ObjectFactory,在bean的生命周期中,生成完原始对象之后,就会构造一个ObjectFactory存入singletonFactories中。这个ObjectFactory是一个函数式接口,所以支持Lambda表达式: () -> getEarlyBeanReference( beanName , mbd , bean )
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
也就是说在初始化A的时候,在属性注入之前会提前创建A对象的ObjectFactory放到这个singletonFactories中,后续B对象在需要A对象的时候就会调用这个 () -> getEarlyBeanReference( beanName , mbd , bean ) 来获取A对象。此时如果这个A对象不需要经过AOP代理的就会生成一个普通对象,如果后续需要经过AOP代理此时就会生成一个代理对象返回给B进行属性注入。
所以真正解决循环依赖的是singletonFactories,现在看起来是不是觉得没什么问题了呢
- 假设这个A对象是需要经过AOP代理生成一个代理对象的,B对象使用的也是A的代理对象,看起来似乎没什么问题,但是AOP代理是在初始化之后执行的,也就是说你现在虽然提前生成了A的代理对象,但是在初始化A对象之后还要经过AOP的一次代理,那么作为单例来说,这二个代理对象是不一样的。
5:第四级别缓存 - earlyProxyReferences
在经过 () -> getEarlyBeanReference(beanName,mbd,bean) 得到的对象如果是经过AOP代理的对象就会放到这个earlyProxyReferences中,这样后续在初始化后置处理器中只要判断earlyProxyReferences中有没有这个Bean,如果有就不用再次做代理了
6:总结
Spring在解决循环依赖虽然使用了三级缓存,可以说是四级缓存,但是真正解决循环依赖的主要还是singletonFactories这一级的缓存,所以至少是三级缓存才能解决循环依赖
转载自:https://juejin.cn/post/7219202318994309180