- 什么是bean循环依赖
循环依赖即循环引用,两个或者两个以上的bean相互之间持有对方。如A引用了B,B引用了C,C又引用了A,他们最终形成了一个环。Spring容器循环依赖包括构造器循环依赖和setter循环依赖。
- Spring怎么处理bean循环依赖
Spring容器将每一个正在创建的bean标识符放在一个“正在创建bean池”中,只要bean还未完成创建过程,则这个bean的标识会一直保留在此池中。Spring处理循环依赖的时候,区分三种场景来处理:
1.构造器依赖的场景
此场景下,Spring无法解决循环依赖,会直接抛出异常。
创建bean步骤:
Spring容器创建TestA,首先去“正在创建bean池”中查找,查找不到,继续准备其需要的构造器参数testB,并将testA标记符记录到“正在创建bean池”中。
Spring容器创建TestB,首先去“正在创建bean池”中查找,查找不到,继续准备其需要的构造器参数testC,并将testB标记符记录到“正在创建bean池”中。
Spring容器创建TestC,首先去“正在创建bean池”中查找,查找不到,继续准备其需要的构造器参数testA,并将testC标记符记录到“正在创建bean池”中。到此Spring容器准备去创建"testA"bean,但是发现在“正在创建bean池”中已经有记录,不再进行下去直接抛出BeanCurrentlyInCreationException,表示循环依赖
2.setter依赖且是单例作用域的场景
此场景下,Spring可以通过提前缓存简单bean的机制解决。
创建bean步骤:
Spring容器创建TestA,根据无参构造器创建bean TestA,并暴露一个ObjectFactory用于提前暴露一个创建中的bean,并将“testA”标识符记录到“正在创建bean池”中,然后进行setter注入"testB"。
Spring容器创建TestB,根据无参构造器创建bean TestB,并暴露一个ObjectFactory用于提前暴露一个创建中的bean,并将“testB”标识符记录到“正在创建bean池”中,然后进行setter注入"testC"。
Spring容器创建TestC,根据无参构造器创建bean TestC,并暴露一个ObjectFactory用于提前暴露一个创建中的bean,并将“testB”标识符记录到“正在创建bean池”中,然后进行setter注入"testA"。进行注入"testA"时由于步骤(1)中已经提前暴露一个简单bean,所以可以根据其ObjectFactory获取到创建中的“testA”。 最终完成三个Bean的创建。
此部分的源码如下:
AbstractAutowireCapableBeanFactory.java
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
//...略过异常处理
}
mbd.postProcessed = true;
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
//...略过异常处理
}
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
//...略过异常处理
}
}
}
}
// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
//...略过异常处理
}
return exposedObject;
}注意源码中的“boolean earlySingletonExposure” 判断标识符,还有“addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));”处理方法。
另外对于singleton作用域的bean,可通过setAllowCircularReferences(false)来禁用循环依赖。
3.setter依赖且是prototype作用域的场景
此场景下,Spring无法解决循环依赖。
根据源码上的判断逻辑,Spring只对于单例作用域支持了提前暴露bean;prototype作用域并不能进行提前缓存,无法提前暴露;所以不能解决循环依赖问题。
- 总结
构造器方式的循环依赖:spring不能解决,直接抛出异常
setter方式的循环依赖:
(1)单例作用域的bean,spring可解决循环依赖
(2)prototype作用域的bean,spring不能解决循环依赖