什么是循环依赖?比如如下一个例子
public class A{
private B b;
}
public class B{
private A a;
}
这个例子存在的问题:理论上spring创建A的时候依赖了B,然后spring就会去加载B,但是这个时候B又依赖了A,spring又去加载A,就会陷入一个死循环,但我们在实际使用spring的时候并没有出现这样的循环,这是因为spring设计之初就考虑了这个问题,那么spring是如何解决的呢?我们先要明确的是spring对循环依赖的处理有三种情况,分别为
Spring获取一个Bean的流程就说从一级到三级依次去寻找这个Bean
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
//isSingletonCurrentlyInCreation()判断当前单例bean是否正在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
//allowEarlyReference 是否允许从singletonFactories中通过getObject拿到对象
if (singletonObject == null && allowEarlyReference) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
//从singletonFactories中移除,并放入earlySingletonObjects中。
//其实也就是从三级缓存移动到了二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
如果一二三级均没有这个Bean,就会走新建方法,Spring创建一个Bean的时候也会有3个步骤,如下
我们再来说上面的循环依赖的例子:A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中,此时进行第二步填充属性,发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(肯定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象,B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,进去了一级缓存singletonObjects中。
构造器循环依赖
this .singletonsCurrentlylnCreation.add(beanName)将当前正要创建的bean 记录在缓存中 Spring 容器将每一个正在创建的bean 标识符放在一个“当前创建bean 池”中, bean 标识 柏:在创建过程中将一直保持在这个池中,因此如果在创建bean 过程中发现自己已经在“当前 创建bean 池” 里时,将抛出BeanCurrentlylnCreationException 异常表示循环依赖;而对于创建 完毕的bean 将从“ 当前创建bean 池”中清除掉。
多例循环依赖
对于“prototype”作用域bean, Spring 容器无法完成依赖注入,因为Spring 容器不进行缓 存“prototype”作用域的bean ,因此无法提前暴露一个创建中的bean 。
这里面涉及到的问题很复杂,可以看这篇文章,我怕文章被删了,所以留了下图。
总结起来就是几点:一级缓存是放完全初始化好的对象的,如果只需要IOC功能其实一级缓存就能解决。但涉及到循环依赖,我们就需要暴露出一个没有初始化好的对象,那么我们不能把初始化好的和没好的都放到一级缓存里面吧?那不是乱套了,所以这个时候就需要引入二级缓存,把初始化好的放到一级缓存里面去,没好的放到二级缓存里面去。看起来可以完美解决问题了,但如果有代理对象的话,实际流程就会变成下面这样。我们假设上诉例子中A为代理对象
你可能会有点疑问,为什么要把一个构建A代理对象的工厂放入到三级缓存中?为什么不能直接把已经生成好的代理对象放到二级缓存呢?这其实是出于性能的考虑,因为如果没有用到循环依赖的话,如果我们一开始就把生成好的代理对象放到二级缓存里面去,是不是有点影响性能呢?
详细的看下面
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有