Spring IOC循环依赖问题
1. 什么是循环依赖
循环依赖其实就是循环引⽤,也就是两个或者两个以上的 Bean 互相持有对⽅,最终形成闭环。⽐如A依赖于B,B又依赖于A。套娃模式无限循环。这⾥不是函数的循环调⽤,是对象的相互依赖关系。循环调⽤其实就是⼀个死循环,除⾮有终结条件。
2. Spring中循环依赖场景有哪些
1. 构造器的循环依赖(构造器注⼊)
2. Field 属性的循环依赖(set注⼊)
3. Spring是如何处理循环依赖的
1. 构造器的循环依赖无法解决
因为构造器要调用构造函数new 一个对象出来,而参数又依赖于另一个对象。创建类A依赖于类B,new 的时候去创建类B发现类B不存在就会出错拋出 BeanCurrentlyInCreationException 异常。
2. prototype 原型bean循环依赖⽆法解决
对于原型bean的初始化过程中不论是通过构造器参数循环依赖还是通过setXxx⽅法产⽣循环依赖,Spring都 会直接报错处理。
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
return (curVal != null &&
(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>)
curVal).contains(beanName))));
}
在获取bean之前如果这个原型bean正在被创建则直接抛出异常。原型bean在创建之前会进⾏标记这个beanName正在被创建,等创建结束之后会删除标记
try {
//创建原型bean之前添加标记
beforePrototypeCreation(beanName);
//创建原型bean
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
//创建原型bean之后删除标记
afterPrototypeCreation(beanName);
}
3. singleton bean 可以通过三级缓存的方式解决循环依赖
- 类A开始调用set方法注入类B,在缓存池中找不到类B,就把类A放到三级缓存池singletonFactorys,转而开始创建类B。
- 创建类B发现要set方法注入类A,在一级缓存容器中singletonObjects发现类A不存在,继续找,发现在三级缓存池中有半成品的类A的对象。
- 根据在三级缓存池中的半成品类A,成功完成类B的创建。然后从三级缓存池放入二级缓存池earlySingletonObjects再放入一级缓存池。
- 类A从一级缓存池singletonObjects成功获取类B,完成对象的创建。成功的解决了循环依赖的问题。
4. spring 源码看循环依赖解决
-
创建类A后开始填充其属性类B
-
半成品类A对象放入三级缓存中
-
类B根据三级缓存池中的半成品类A对象完成创建放入二级缓存当中
-
把二级缓存池中的完成的对象类B放入一级缓存池