Spring bean循环依赖

377 阅读4分钟

  • 什么是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不能解决循环依赖