什么是循环依赖
循环依赖其实就是循环引⽤,也就是两个或者两个以上的 Bean 互相持有对⽅,最终形成闭环。⽐如A依赖于B,B依赖于C,C⼜依赖于A。
注意,这⾥不是函数的循环调⽤,是对象的相互依赖关系。循环调⽤其实就是⼀个死循环,除⾮有终结条件。
Spring中循环依赖场景有:
- 构造器的循环依赖(构造器注⼊)
- Field 属性的循环依赖(set注⼊)
其中,构造器的循环依赖问题⽆法解决,只能拋出 BeanCurrentlyInCreationException
异常,在解决属性循环依赖时,spring采⽤的是提前暴露对象的⽅法。
循环依赖处理机制
单例 bean 构造器参数循环依赖(⽆法解决)
prototype 原型 bean循环依赖(⽆法解决)
对于原型bean的初始化过程中不论是通过构造器参数循环依赖还是通过setXxx⽅法产⽣循环依赖,Spring都 会直接报错处理。
AbstractBeanFactory.doGetBean()⽅法:
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);
}
总结:Spring 不⽀持原型 bean 的循环依赖。
单例bean通过setXxx或者@Autowired进⾏循环依赖
Spring
的循环依赖的理论依据基于 Java 的引⽤传递
,当获得对象的引⽤时,对象的属性是可以延后设置的,但是构造器必须是在获取引⽤之前Spring通过setXxx或者@Autowired⽅法
解决循环依赖其实是通过提前暴露⼀个ObjectFactory对象
来完成的,简单来说ClassA在调⽤构造器完成对象初始化之后,在调⽤ClassA的setClassB⽅法之前就把ClassA实例化的对象通过ObjectFactory提前暴露到Spring容器中。
循环依赖源码分析
(1)A实例化过程之后立马放入三级缓存(提前暴露自己)
refresh -> finishBeanFactoryInitialization-> getBean -> doGetBean -> createBean -> doCreateBean中
- 判断A存在循环依赖,将A对象放入到三级缓存
singletonFactories
中
// 解决循环依赖的关键步骤,判断是否存在循环依赖:单例 && 允许循环依赖 && 当前bean正在创建中
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
// 如果需要提前暴露单例Bean,则将该Bean放入三级缓存中
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 将刚创建的bean放入三级缓存中singleFactories(key是beanName,value是FactoryBean)
//如果支持则暴露一个工厂
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) {
/**
* 如果单例池当中不存在才会add
* 因为这里主要为了循环依赖服务的代码
* 如果bean存在单例池的话其实已经是一个完整的bean了
* 一个完整的bean自然也是已经完成属性注入,循环依赖已经依赖上了
* 所以如果这个对象已经是一个完整的bean,就不需要关心,不需要进入 if
* 如果一级缓存中没有该实例,则放入三级缓存中
*/
if (!this.singletonObjects.containsKey(beanName)) {
// 放入三级缓存
this.singletonFactories.put(beanName, singletonFactory);
/**
* 从三级缓存中remove掉当前的bean
* 为什么要remove?抛开细节,这三个map当中其实其实存的都是一个对象
* spring的做法是三个不能同时存在
*/
// 从二级缓存中移除
this.earlySingletonObjects.remove(beanName);
//将beanName注册到registeredSingletons缓存(已经注册的单例集合)
this.registeredSingletons.add(beanName);
}
}
}
- A对象中需要进行属性注入,发现依赖了B对象
(2)B对象实例化过程和A实例化过程一样,将自己放入到三级缓存中之后下一步进行属性注入发现依赖于A,那么就去三级缓存使用尚未成型的Bean A,并将A升级放到二级缓存中。
- B对象实例化放入三级缓存中
- B对象中需要进行属性注入,发现依赖了A对象
(3) 再次加工Bean A的过程以及放入二级缓存示意图
(4)B对象创建完成之后放入一级缓存中
(5)A对象从一级缓存中获取到B对象,注入到A对象中