循环依赖其实就是循环引用,也就是两个或者两个以上的 Bean 互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A。
注意,这里不是函数的循环调用,是对象的相互依赖关系。循环调用其实就是一个死循环,除非有终结条件。
Spring中循环依赖场景有:
其中,构造器的循环依赖问题方法解决,只能拋出 BeanCurrentlyInCreationException
异常,在解决属性循环依赖时,spring采用的是提前暴露对象的方法。
对于原型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 的循环依赖。
setXxx
或者@Autowired
进行循环依赖Spring 的循环依赖的理论依据基于 Java 的引用传递,当获得对象的引用时,对象的属性是可以延后设置的,但是构造器必须是在获取引用之前
Spring通过setXxx
或者**@Autowired方法解决循环依赖其实是通过提前暴露一个ObjectFactory
对象来完成的,简单来说ClassA在调用构造器完成对象初始化之后,在调用ClassA的setClassB方法之前就把ClassA实例化的对象通过ObjectFactory
提前暴露到Spring容器**中。
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");
}
//将初始化后的对象提前已ObjectFactory对象注入到容器中
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
本文参考资料《Spring高级源码笔记》,需要的同学添加助理VX:
C18173184271
免费获取
如果你需要这份完整版的《Spring高级源码笔记》
,只需你多多支持我这篇文章。
多多支持,即可免费获取资料——三连之后(承诺:100%免费)
快速入手通道:添加助理VX:C18173184271
免费获取!诚意满满!!!
Spring面试专题文章点击这里!!!