Spring源码导读之解决循环依赖

本文深入解析Spring框架中解决循环依赖的机制,详细介绍了getSingleton、getSingletonFactory及getEarlyBeanReference等方法的作用,阐述了在AOP场景下如何正确处理循环依赖,确保代理对象的正确注入。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SingleTonBean提到AbstractBeanFactory#getBean(java.lang.String) 会先调用一次getSingleton。

Object sharedInstance = getSingleton(beanName)这行代码,获取一个singleton,这里的这个sharedInstance ,故名思意是分享的意思。想象这样一个场景:

A 依赖 B

B 依赖 A

那么创建A的时候,为A注入属性时需要注入B,从而触发B的doGetBean,那么B回头也会会触发A的doGetBean,因为A已经在创建中,所以B获取A的时候,就在这里直接getSingleton() 获取了A的早期引用。

再看下getSingleton(beanName)

public Object getSingleton(String beanName) {
	// 默认允许获取早期引用allowEarlyReference == true
   return getSingleton(beanName, true);
}


protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	// 先从singletonObjects获取,如果在singletonObjects中获取到了, 那么一定时该引用已经创建成功了
	// B获取A的时候, A还在创建中,singletonObjects没有注册A对象,所以获取不到
	Object singletonObject = this.singletonObjects.get(beanName);
	// 接着判断 如果A正在创建中, 那么尝试获取创建中的A对象
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		synchronized (this.singletonObjects) {
			// 从早期 earlySingletonObjects 中获取A对象, 第一次出现循环依赖的时候时获取不到的(为什么获取不到? 继续看下面),这里也是null,
			singletonObject = this.earlySingletonObjects.get(beanName);
			if (singletonObject == null && allowEarlyReference) {
				// 如果允许早期引用, 才会进入该方法体, 也就是说如果allowEarlyReference == true才会进入, 当然B获取A的时候, allowEarlyReference==true
				// 首先从singletonFactories获取A的 singletonFactory, B获取A的时候是可以从这里获取到的
				// 因为在A创建的时候, doCreateBean中 调用 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean))传入了一个singletonFactory的lambda表达式
				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
				if (singletonFactory != null) {
					// 调用singletonFactory.getObject()获取singletonObject, 其实就是调用getEarlyBeanReference
					singletonObject = singletonFactory.getObject();
					// 拿到singletonObject 后 将其加入到 earlySingletonObjects中
					// 很关键: 1、这里解释第14行注释,这里我们知道为什么第一次在earlySingletonObjects中获取不到 singletonObject了。
					// 2、只有第一次出现循环引用的时候才会进入到这里, 后面如果有C还依赖A,那么就会从 earlySingletonObjects中 直接获取了
					// 3、如果传入 allowEarlyReference == false, 并且还获取到 A 对象,那么一定出现过循环依赖, 因为只有出现循环依赖才会走到这个方法体
					this.earlySingletonObjects.put(beanName, singletonObject);
					// 将 调用singletonFactory 从 singletonFactories 移除。
					this.singletonFactories.remove(beanName);
				}
			}
		}
	}
	return singletonObject;
}

上面注释我们知道 如下定理:

1、getSingleton 先从 singletonObjects中取对象

2、如果没取到并且如果对象正在创建中,则从earlySingletonObjects 中取

3、如果还是没取到并且允许早期引用,则从singletonFactories 中取。并将其放置到earlySingletonObjects 。然后返回。

有一下推论:

1、如果从 singletonObjects取到对象,那么该对象一定创建完毕。

2、第一次循环依赖无法在earlySingletonObjects 中获取到依赖对象。

3、第二次之后如果出现循环依赖,earlySingletonObjects 一定从中获取,也就是说如果A 和 B循环依赖,A又和C循环依赖,那么C获取A的时候一定是 从 earlySingletonObjects 获取,B一定从 singletonFactories 中获取。

4、如果 A正在创建中传入 allowEarlyReference == false 还可以获取到A的引用那么,一定出现了循环依赖。

 

根据如上定理和推论,结合想象A 和 B 循环依赖 ,我们看AbstractAutowireCapableBeanFactory#doCreateBean 的代码:

	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;
		}

		// 这里又会进入一次post-processors
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				mbd.postProcessed = true;
			}
		}

		// 解决循环依赖进行一次提前暴露
		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");
			}
			// 缓存中添加一个singletonFactory, 用于被依赖者调用时获取其早期暴露, 这里主要解决在AOP情况下的循环依赖的问题,后面会有案例详解
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// exposedObject 是用于暴露给用户的引用,如果用到AOP, 那么它必须指向代理对象
		Object exposedObject = bean;
		try {
			// 完成属性注入
			populateBean(beanName, mbd, instanceWrapper);
			// bean的初始化
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				throw (BeanCreationException) ex;
			}
			else {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
			}
		}

		if (earlySingletonExposure) {
			// 在不允许早期早期暴露的条件获取其早期暴露, 获得其早期引用, 听不懂, 没事后面又专门介绍
			Object earlySingletonReference = getSingleton(beanName, false);
			// 如果不允许早期暴露,却又获得了其earlySingletonReference, 那么必然出现了循环依赖 听不懂, 没事后面又专门介绍
			if (earlySingletonReference != null) {
				// 如果出现了循环依赖, 并且exposedObject == bean 这里两个引用在AOP中都是被代理对象。
				// 如果出现了exposedObject != bean。说明出现被代理对象中注入了代理对象,这种不被允许,则抛出异常。听不懂, 没事后面又专门介绍
				if (exposedObject == bean) {
					// 将 earlySingletonReference 付给 exposedObject , 因为这里的很可能指向了代理对象, 需要将代理对象暴露返回
					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()) {
						throw new BeanCurrentlyInCreationException(beanName,
								"Bean with name '" + beanName + "' has been injected into other beans [" +
								StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
								"] in its raw version as part of a circular reference, but has eventually been " +
								"wrapped. This means that said other beans do not use the final version of the " +
								"bean. This is often the result of over-eager type matching - consider using " +
								"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}

		// Register bean as disposable.
		try {
			// 如果有必要的化, 将得到的bean进行注册
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;
	}

比如现在正在创建A对象, A 经历了 三个阶段

1、实例化,

2、属性赋值,

3 、初始化(初始化中可能会又AOP行为)

关于循环依赖的代码解释:

1、在代码instanceWrapper = this.factoryBeanInstanceCache.remove(beanName) 调用后,获取到A的引用。

2、接着调用 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)) 将可以获取到A的早期引用的SingletonFactory 加入到 singletonFactories 

3、调用populateBean(beanName, mbd, instanceWrapper) 对A进行赋值,从而引发B的创建。B创建之后走到这行代码会调用A的doGetBean 从而调用getSingleton(beanName == A) 的方法。从而B能获取到A的引用。

4、B创建完成之后,继续A的后面流程,从而循环依赖解决。

 

为什么要搞singletonFactories ?还要调用其 getEarlyBeanReference ? B 获取A的时候会调用这个方法。

我们看下getEarlyBeanReference的代码。

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}

如果我们不看 if 中的条件判断, 其实B 获取 A的时候调用 singletonFactory,其实就是直接获取A的早期引用。关键就是这个if语句。这个if语句,其实是解决了,在AOP情景下的循环依赖。

我们再看,后面一段代码:

exposedObject = initializeBean(beanName, exposedObject, mbd);进行初始化时可能会调用wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);进行AOP,那么 exposedObject其实是指向了被代理对象。

如果我们不经过处理而是在B获取A时候直接返回 A的原始对象,那么B中注入的A便不是代理对象,只有A注入了B的代理对象。那么B中调用A方法时,AOP就失效了。Spring不允许这样的AOP行为,因此看到下面有个判断。

注意 Object earlySingletonReference = getSingleton(beanName, false); 中allowEarlyReference == false,根据推论4:如果 A正在创建中传入 allowEarlyReference == false 还可以获取到A的引用那么,一定出现了循环依赖。

那么如果出现循环依赖,就需要将经历过initializeBean 的 exposedObject 与原来的 bean(这个是经历初始化之前记录的引用)。

如果二者不同,则会抛一个BeanCurrentlyInCreationException。

 

因此使用者在进行AOP时则要遵守一个规则: 

1、Spring允许在applyBeanPostProcessorsAfterInitialization中进行AOP。

2、也允许在getEarlyBeanReference中进行AOP。

3、不出现循环依赖时,则只在 applyBeanPostProcessorsAfterInitialization实现AOP就行了

4、但是如果出现循环依赖,则必须在getEarlyBeanReference进行AOP, 这样B获取A的时候,获取的就是A的代理对象。并且,不可以在applyBeanPostProcessorsAfterInitialization。这样保证 在回到A的时候  exposedObject ==  bean。实现者必须在两个方法中加判断,只允许其中一个做了AOP。Spring的@Aspect 注解实现的AOP,它就遵顼这个原则。可以查看我的一个demo,演示了如何解决AOP的循环依赖问题

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值