假设我们现在有两个需要创建的Bean,分别为BeanA和BeanB,在BeanA创建时依赖BeanB,在BeanB创建时依赖BeanA,如下面代码:
@Component
public class BeanB {
@Autowired
private BeanA a;
}
@Component
public class BeanA {
@Autowired
private BeanB b;
}
上面的代码构成了最基本的循环依赖,那么Spring是如何解决这个问题的呢,涉及到了Spring的三级缓存:
- singletonObject:进行了完整的生命周期; 一级缓存
- earlySingletonObject: 提前暴露的bean,但是没有进行完整的生命周期,在实例化之后,初始化之前; 二级缓存
- singletonFactory:里面存了一个lambda表达式,把执行的结果存到二级缓存中,这个执行结果可能是原始对象也可能是aop之后的代理对象; 三级缓存
为什么需要三级缓存?
拿上面的BeanA和BeanB我们来具体说一下:
BeanA实例化之后 -> 把lambda表达式getEarlyBeanReference(beanName,mbd,bean)存入三级缓存中 -> 依赖注入时发现需要BeanB
—> 实例化B -> 把lambda表达式getEarlyBeanReference(beanName,mbd,bean)存入三级缓存中 -> 依赖注入时发现需要BeanA -> 从单例Bean中获取(一级缓存)获取,获取不到 -> 从二级缓存缓存中获取,获取不到 -> 从刚才A暴露的三级缓存里面获取到这个lambda表达式进行执行 ->lambda执行结果(可能是原始对象也可能是代理对象) 放入二级缓存中,删除三级缓存
-> 执行初始化前,初始化,初始化后 ->把Bean放入singletonObject中
从上面的流程中,我们其实可以看到真正解决循环依赖的问题是三级缓存提前暴露了这个lambda表达式。
那为什么在这里需要三级缓存直接使用二级缓存不可以嘛?
假定我们使用了二级缓存,但是后续在初始化完之后进行了AOP的操作,那么就会有问题了,比如BeanA 依赖了BeanB,在注入BeanB时使用的是二级缓存原始对象
但是BeanB在初始化完之后还进行了AOP,那么BeanA注入的BeanB和本身的BeanB最后放入单例池的BeanB就有矛盾了,一个是没有进行AOP,一个是进行了AOP
如果还是没有看明白,可以参考下面图片,一目了然:
点击阅读原文