剑指spring源码(五)—循环依赖
循环依赖问题是一个非常热门,非常有意思的话题,每次想到这个问题都会很高兴,第一次接触循环引用还是看java虚拟机的时候,如何判断一个对象已死?计数器法虽简单但却存在ABA问题,采用可达性分析算法才算解决了这个问题。如今学习spring又碰到了类似的问题,怎能不高兴
spring的循环依赖是什么
假设有A,B两个类,A类有B属性,B类有A属性,那么当spring扫描到这两个类,如何对这两个类进行实例化
public class A{
B b;
}
public class B{
A a;
}
如上面代码,我先实例化A对象,然后给A注入属性B,但是他发现此时B还没有被实例化,于是去实例化B,然后给B注入属性A,但是此时A还不是一个完整的A,即A不在单例池中,如果不采取措施,他们两个将无限循环下去。如何解决呢?
想要解决循环依赖,必须先发现这个问题,我们可以用一个singletonsCurrentlyInCreation集合存储当前正在创建的bean,什么叫正在创建的bean,即暂时没有被加到单例池的,如果再getBean时刻他发现singletonsCurrentlyInCreation有自己了,那么此时肯定是循环依赖了,那么如何解决呢?不考虑aop的情况下,我们可以定义一个earlySingletonObjects 早期单例池存储没有进行DI的bean。基于上面的假设,我们再来模拟ABA问题
还是先实例化A,把A加入到singletonsCurrentlyInCreation,earlySingletonObjects 中,给A注入属性B,实例化B,把B加入到singletonsCurrentlyInCreation,earlySingletonObjects,给B注入属性A,此时再去getBean(A.class),他就会发现singletonsCurrentlyInCreation已经有自己了,所以断定此时发生循环依赖,但是他会去earlySingletonObjects 把A取出来给B注入,此时B就完成了,加入singletonObjects(单例池),A再回过头来把B注入即可。 可能有人会说,不对啊,B类注入了一个没有DI的A对象,其实这没有关系的,就像引用传递一样,传一个对象到另一个函数
其实上面这些思路在没有aop的情况下是完全正确的,并且经得起考证,因为本人手写ioc就是这个逻辑,并且正确解决了循环依赖,有aop的情况会涉及到另一个很复杂的东西,singletonFactories, 这个我把它理解为aop单例工厂,它可以调用后置处理器产生aop代理对象
spring循环依赖图示
spring循环依赖代码分析
对应图示getSingleton(beanName);
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//单例池中拿 第一级缓存
Object singletonObject = this.singletonObjects.get(beanName);
//单例池没有
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
//锁住单例池
synchronized (this.singletonObjects) {
//early中拿 第三级缓存
singletonObject = this.earlySingletonObjects.get(beanName);
//第三级缓存拿不到
if (singletonObject == null && allowEarlyReference) {
//singletonFactories拿 ,第二级缓存 object工厂生产代理加工代理对对象
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//第二级缓存拿到了
singletonObject = singletonFactory.getObject();
//put到三级缓存,为了高效,singletonFactory.getObject()这一步耗时
this.earlySingletonObjects.put(beanName, singletonObject);
//移除二级缓存
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
对应图示提前暴露工厂
//单例&&true(默认支持循环依赖)&& 此bean正在创建
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
//添加到单例池 提前暴露工厂
//第四次后置处理器
//SmartInstantiationAwareBeanPostProcessor---getEarlyBeanReference
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
//单例池没有
if (!this.singletonObjects.containsKey(beanName)) {
//添加到二级缓存
this.singletonFactories.put(beanName, singletonFactory);
//移除三级缓存
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
对于aop代理,普通的是在倒数第二步进行代理的,但是循环引用情况下会提前到循环依赖的getEarlyBeanReference这一步就会完成,singletonFactory.getObject()这个代码调用的的就是getEarlyBeanReference方法,而该方法就是返回aop代理对象
更详细的分析请参考
循环依赖