Spring如何解決循環依賴


今天我們來了解下Spring是如何解決循環依賴的。

准備工作:

1.測試代碼

  • 創建CurrentlyService1和CurrentlyService2。兩個類分別依賴注入對方

2.了解@Autowire原理。

  • 對於這次探索循環依賴,如果不知道的,就當他是針對這個屬性調用createBean。

3.ObjectFactory接口。

  • 他有一個getObject方法。其實就是為了提早曝光object

4.DefaultSingletonBeanRegistry中的三個map。

  • singletonObjects

    存放已經創建好的bean對應的object。beanName->object

  • singletonFactories

    存放beanName對應的ObjectFactory。beanName->ObjectFactory

  • earlySingletonObjects

    存放beanName對應的ObjectFactory.getObject。beanName->object

  • singletonObjects和earlySingletonObjects對應存放的object有什么實質性區別?

開始逐步分析

創建bean的前面我們不分析了。直接到AbstractAutowireCapableBeanFactory#doCreateBean 實例化之后。

1.將bean提早曝光

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");
	}
	addSingletonFactory(beanName, new ObjectFactory<Object>() {
		@Override
		public Object getObject() throws BeansException {
			return getEarlyBeanReference(beanName, mbd, bean);
		}
	});
}

1.1 earlySingletonExposure

當bean為單例 && 容器配置允許循環依賴 && bean正在創建

滿足以上三個條件才會解決循環依賴

1.2 創建一個ObjectFactory並重寫getObject方法

getEarlyBeanReference走的是InstantiationAwareBeanPostProcessorAdapter#getEarlyBeanReference。
點進去可以看到其實就是返回自己

1.3 addSingletonFactory。將ObjectFactory放入singletonFactories

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
	Assert.notNull(singletonFactory, "Singleton factory must not be null");
	synchronized (this.singletonObjects) {
		if (!this.singletonObjects.containsKey(beanName)) {
			this.singletonFactories.put(beanName, singletonFactory);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}
}

我們可以看到singletonFactories添加了ObjectFactory后會把earlySingletonObjects給remove掉。這兩個map其實是互斥的,有他沒我,有我沒他。

2.解決循環依賴

假設我們現在要創建CurrentlyService1,經過了1的所有步驟后(即提早曝光)。開始populateBean。

我們要注入CurrentlyService2,其實就是創建CurrentlyService2這個bean。這時候CurrentlyService2自己走一遍1的所有步驟,再進行populateBean的時候,要注入CurrentlyService1,即創建CurrentlyService1這個bean。

這時候我們看AbstractBeanFactory#doGetBean里的
Object sharedInstance = getSingleton(beanName);

里面調用getSingleton方法傳的allowEarlyReference入參是true(注意這里。后續其他地方調用傳的是false)

在了解循環依賴之前。我一直以為這僅僅是從緩存中獲取已經加載完成的bean。
其實不然。我們來看下里面實現

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	Object singletonObject = this.singletonObjects.get(beanName);
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		synchronized (this.singletonObjects) {
			singletonObject = this.earlySingletonObjects.get(beanName);
			if (singletonObject == null && allowEarlyReference) {
				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
				if (singletonFactory != null) {
					singletonObject = singletonFactory.getObject();
					this.earlySingletonObjects.put(beanName, singletonObject);
					this.singletonFactories.remove(beanName);
				}
			}
		}
	}
	return (singletonObject != NULL_OBJECT ? singletonObject : null);
}

首先,Object singletonObject = this.singletonObjects.get(beanName);其實就是我以前的理解,從緩存中獲取已經加載完成的bean。因為CurrentlyService1還沒創建完成。singletonObject自然是null。往下走earlySingletonObjects.get(beanName),這里在1.3中已經被remove掉了,也是null。往下走this.singletonFactories.get(beanName)。這里1.3中添加了所以不是null。然后就是回調singletonFactory.getObject方法。然后兩個互斥map處理一下。

這時,CurrentlyService2的屬性填充(populateBean)已經處理完畢。

看下面的代碼

if (earlySingletonExposure) {
	Object earlySingletonReference = getSingleton(beanName, false);
	if (earlySingletonReference != null) {
		if (exposedObject == bean) {
			exposedObject = earlySingletonReference;
		}
		.....(這部分鏈路不常見。不分析)
	}
}

這時,我們又調用了getSingleton(beanName, false)方法。只不過allowEarlyReference是false。

根據幾個map最終返回的是null。至此CurrentlyService2已創建完畢。

繼續回到CurrentlyService1的創建。由於CurrentlyService2已創建完畢。CurrentlyService1的屬性填充(populateBean)已經處理完畢。這時,調用getSingleton(beanName, false)。不同於CurrentlyService2的結果,返回的結果不為null。因為在創建CurrentlyService2的過程中已經把CurrentlyService2對應的互斥map給改了。

然后將提早曝光的object返回

bean完全創建完成后。互斥map都會清空。

總結

1.所有單例的bean在創建完成之前都會提早曝光。

2.提早曝光就是預先為這個bean創建好一個ObjectFactory。一旦發現是循環依賴。就調用ObjectFactory.getObject返回實例。

3.Spring判斷循環依賴的條件是
earlySingletonObjects里是否存在這個ObjectFactory.getObject生成的object。(或者另外一個map,兩個互斥的)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM