天天看點

Spring 之 循環依賴詳解

概念

如果 class A 中依賴了 class B并且class B 中也依賴了class A,形成一個閉環就會産生循環依賴的問題。

解決

構造器注入方式的循環依賴,無法解決;

Setter注入方式的循環依賴,解決方式:

  1. Spring先用構造器執行個體化Bean對象,将執行個體化結束的對象放到一個Map中,并且Spring提供擷取這個未設定屬性的執行個體化對象的引用方法;
  2. 在進行屬性注入的時候,依次擷取到對應的Bean執行個體對象進行注入;

原理

在 AbstractBeanFactory 的 doGetBean 方法中,會首先通過緩存中去查找單例 Bean 對象。調用 getSingleton 方法,源碼如下:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
	//檢查緩存中是否存在執行個體
	Object singletonObject = this.singletonObjects.get(beanName);
	//如果單例bean為空但工廠内正在加載
	if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		//如果為空,則鎖定全局變量并進行處理
		synchronized (this.singletonObjects) {
			//如果此bean正在加載則不處理
			singletonObject = this.earlySingletonObjects.get(beanName);
			//是否允許從提前曝光緩存singletonFactories中通過getObject拿到對象,解決屬性注入循環依賴問題的關鍵
			if (singletonObject == null && allowEarlyReference) {
				//當某些方法需要提前初始化的時候則會調用addSingletonFactory方法将對應的ObjectFactory初始化政策存儲在singletonFactories
				ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
				if (singletonFactory != null) {
					//調用預先設定的getObject方法
					singletonObject = singletonFactory.getObject();
					//記錄在緩存中,earlySingletonObjects和singletonFactories互斥
					this.earlySingletonObjects.put(beanName, singletonObject);
					this.singletonFactories.remove(beanName);
				}
			}
		}
	}
	return singletonObject;
}
           

從這個方法上我們可以看到 Spring 使用三級緩存解決循環依賴問題,每個緩存存儲執行個體對象引用的不同生命周期:

  • singletonFactories : 單例對象工廠的cache,緩存第一步執行個體化後的對象
  • earlySingletonObjects :提前曝光的單例對象的Cache ,緩存第二步填充屬性後的對象(用于檢測循環引用,與singletonFactories互斥)
  • singletonObjects:單例對象的cache,緩存第三步初始化完成後的對象

在通過上一篇Spring 之 DI 詳解,我們知道 Bean 初始化的三個重要步驟為:

  1. 調用 createBeanInstance 方法進行執行個體化對象
  2. 調用 populateBean 方法進行依賴注入
  3. 調用 initializeBean 方法執行 BeanPostProcessor 後置處理器

我們來看看執行個體化完之後在什麼時候存儲到 singletonFactories 緩存中,在 AbstractAutowireCapableBeanFactory.doCreateBean() 方法中。

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
		throws BeanCreationException {

	//封裝被建立的Bean對象
	BeanWrapper instanceWrapper = null;
	if (mbd.isSingleton()) {
		instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
	}
	if (instanceWrapper == null) {
		//根據指定bean使用對應的政策建立執行個體對象,如:指定的工廠方法、根據參數選擇構造函數、預設無參構造方法
		instanceWrapper = createBeanInstance(beanName, mbd, args);
	}
	
	//...

	// 向容器中緩存單例模式的Bean對象,以防止循環引用
	boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
			isSingletonCurrentlyInCreation(beanName));
	if (earlySingletonExposure) {
		if (logger.isTraceEnabled()) {
			logger.trace("Eagerly caching bean '" + beanName +
					"' to allow for resolving potential circular references");
		}
		//為避免後期循環依賴,盡早持有對象的引用
		addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
	}
	
	//...
}
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
	Assert.notNull(singletonFactory, "Singleton factory must not be null");
	//提前曝光執行個體化完成暫未進行屬性注入的bean對象,以解決屬性注入的循環引用問題
	synchronized (this.singletonObjects) {
		if (!this.singletonObjects.containsKey(beanName)) {
			this.singletonFactories.put(beanName, singletonFactory);
			this.earlySingletonObjects.remove(beanName);
			this.registeredSingletons.add(beanName);
		}
	}
}
           

可以看到在 createBeanInstance 方法執行個體化對象之後(此時還未進行依賴注入)就會暴露到 singletonFactories 三級緩存集合中,以解決屬性注入的循環引用問題。

在 AbstractBeanFactory 的 doGetBean 方法中,調用 createBean 方法

//建立單例模式的Bean的執行個體對象
if (mbd.isSingleton()) {
	//調用匿名内部類建立Bean執行個體對象,建立Bean執行個體對象,并且注冊給所依賴的對象
	sharedInstance = getSingleton(beanName, () -> {
		try {
			//建立一個指定Bean執行個體對象,如果有父級繼承,則合并子類和父類的定義
			return createBean(beanName, mbd, args);
		}
		catch (BeansException ex) {
			// Explicitly remove instance from singleton cache: It might have been put there
			// eagerly by the creation process, to allow for circular reference resolution.
			// Also remove any beans that received a temporary reference to the bean.
			//從單例模式的Bean緩存中清除執行個體對象
			destroySingleton(beanName);
			throw ex;
		}
	});
	//擷取給定Bean的執行個體對象,主要完成FactoryBean擷取執行個體化對象過程
	bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
           

繼續跟進 DefaultSingletonBeanRegistry.getSingleton() 方法,源碼如下:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
	Assert.notNull(beanName, "Bean name must not be null");
	//全局變量需要同步
	synchronized (this.singletonObjects) {
		//首先檢查對應的bean是否已經加載過,因為singleton模式其實就是複用已建立的bean
		Object singletonObject = this.singletonObjects.get(beanName);
		//如果為空才可以進行singleton的bean的初始化
		if (singletonObject == null) {
			if (this.singletonsCurrentlyInDestruction) {
				throw new BeanCreationNotAllowedException(beanName,
						"Singleton bean creation not allowed while singletons of this factory are in destruction " +
						"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
			}
			beforeSingletonCreation(beanName);
			boolean newSingleton = false;
			boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
			if (recordSuppressedExceptions) {
				this.suppressedExceptions = new LinkedHashSet<>();
			}
			try {
				//初始化bean
				singletonObject = singletonFactory.getObject();
				newSingleton = true;
			}
			catch (IllegalStateException ex) {
				singletonObject = this.singletonObjects.get(beanName);
				if (singletonObject == null) {
					throw ex;
				}
			}
			catch (BeanCreationException ex) {
				if (recordSuppressedExceptions) {
					for (Exception suppressedException : this.suppressedExceptions) {
						ex.addRelatedCause(suppressedException);
					}
				}
				throw ex;
			}
			finally {
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = null;
				}
				afterSingletonCreation(beanName);
			}
			if (newSingleton) {
				//加入代表已經初始化完成的單例bean緩存中
				addSingleton(beanName, singletonObject);
			}
		}
		return singletonObject;
	}
}
protected void addSingleton(String beanName, Object singletonObject) {
	synchronized (this.singletonObjects) {
		this.singletonObjects.put(beanName, singletonObject);
		this.singletonFactories.remove(beanName);
		this.earlySingletonObjects.remove(beanName);
		this.registeredSingletons.add(beanName);
	}
}
           

從上面可看到,當 Bean 執行個體初始化完成後将會删除 singletonFactories 和 earlySingletonObjects 緩存中的引用,添加到 singletonObjects 緩存中。

總結

最後梳理一下Spring解決循環依賴的流程

1. 類 A 與 B 中屬性互相引用,造成循環依賴
2. A 執行個體化完成,将自己提前曝光到 singletonFactories 緩存中
3. A 執行個體進行依賴注入,發現自己依賴對象 B,就嘗試擷取 B 執行個體引用
4. B 此時還沒初始化,先進行執行個體化并将自己曝光到 singletonFactories 緩存中
5. B 執行個體進行依賴注入,發現自己依賴對象 A,就嘗試擷取 A 執行個體引用
6. 由于 A 執行個體尚未初始化完成,從 singletonObjects 中擷取不到,從 earlySingletonObjects 中擷取也沒有,最後從 singletonFactories 中擷取到
7. A 執行個體從 singletonFactories 中删除,添加到 earlySingletonObjects 緩存中去
8. B 執行個體拿到 A 執行個體引用後順利完成依賴注入及初始化,并将自己從 singletonFactories 緩存中删除,添加到 singletonObjects 緩存中
9. A 執行個體擷取 B 執行個體引用後,A 執行個體也能繼續完成依賴注入及後續初始化操作
10. A 是從 earlySingletonObjects 中删除,添加到 singletonObjects 緩存中
           

在整個過程中,A 和 B 的執行個體引用并未改變過。隻是在 Bean 生命周期的不同階段。