【spring源碼系列】之【Bean的循環依賴】

希望之光永遠向着目標清晰的人敞開。

1. 循環依賴概述

循環依賴通俗講就是循環引用,指兩個或兩個以上對象的bean相互引用對方,A依賴於B,B依賴於A,最終形成一個閉環。

Spring循環依賴的場景有兩種:

  • 構造器的循環依賴
  • field 屬性的循環依賴
    對於構造器的循環依賴,Spring 是無法解決,只能拋出 BeanCurrentlyInCreationException 異常;對於field 屬性的循環依賴,Spring 只解決 scope 爲 singleton 的循環依賴,對於scope 爲 prototype 的 bean Spring 無法解決,直接拋出 BeanCurrentlyInCreationException 異常。下面重點分析屬性依賴的情況。

2. 循環依賴執行流程


以上流程圖針對如下代碼進行演示:

@Component
public class CircularRefA {

    public CircularRefA() {
        System.out.println("============CircularRefA()===========");
    }

    //這裏會觸發CircularRefB類型的getBean操作
    @Autowired
    private CircularRefB circularRefB;
}
@Component
public class CircularRefB {

    public CircularRefB() {
        System.out.println("============CircularRefB()===========");
    }

    //又會觸發A的getBean操作
    @Autowired
    private CircularRefA circularRefA;
}

step1: A類實例化執行,第一次三個緩存中都沒有,會走doGetBean一路走到createBeanInstance,完成無參構造函數實例化,並在addSingletonFactory中設置三級緩存;
step2:A類populateBean進行依賴注入,隨後觸發了B類屬性的getBean操作;
step3: B類與A類類似,第一次三個緩存中也都沒有,無參構造函數實例化後,設置三級緩存,將自己加入三級緩存;
step4:B類populateBean進行依賴注入,這裏觸發了A類屬性的getBean操作;
step5: A類之前正在創建,此時已經是第二次進入,由於一級二級緩存中都沒有,會從三級緩存中獲取,並且允許 bean提前暴露則從三級緩存中拿到對象工廠,從工廠中拿到對象成功後,升級到二級緩存,並刪除三級緩存;若以後有別的類引用的話就從二級緩存中進行取;
step6:B類拿到了A的提前暴露實例注入到A類屬性中了,此時完成B類的實例化;
step7:A類之前依賴B類,B的實例化完成,進而促使A的實例化也完成,並且此時A的類B屬性已經有值,A類繼續走後續的afterSingletonCreateion與addSingleton方法,刪除正在創建緩存中的實例,並將實例從二級緩存移入以及緩存,同時刪除二三級緩存;

以上是A類實例化的全過程,下面會針對源碼逐一分析。

3. 源碼分析

首先創建A的實例,需要從A的getBean方法開始,到doGetBean,第一次優先從緩存中取,進入getSingleton方法:

	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// 從一級緩存singletonObjects中取
		Object singletonObject = this.singletonObjects.get(beanName);
		// 一級緩存爲空,且單例對象正在創建
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				// 從二級緩存earlySingletonObjects中取
				singletonObject = this.earlySingletonObjects.get(beanName);
				// 如果二級緩存也爲空,且允許bean提前暴露
				if (singletonObject == null && allowEarlyReference) {
					// 從三級緩存singletonFactories中取
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					// 如果三級緩存不爲空
					if (singletonFactory != null) {
						// 調用getEarlyBeanReference方法提前暴露bean
						singletonObject = singletonFactory.getObject();
						// 提前暴露的bean放入二級緩存,
						this.earlySingletonObjects.put(beanName, singletonObject);
						// 將提前暴露的bean從三級緩存中刪除
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

這個方法主要是從三個緩存中獲取,分別是:singletonObjectsearlySingletonObjectssingletonFactories,三者定義如下:

	/** Cache of singleton objects: bean name to bean instance. */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

	/** Cache of singleton factories: bean name to ObjectFactory. */
	private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

	/** Cache of early singleton objects: bean name to bean instance. */
	private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

意義如下:

  • singletonObjects:單例對象的cache
  • singletonFactories : 單例對象工廠的cache
  • earlySingletonObjects :提前暴露的單例對象的Cache

解決循環依賴的關鍵是三個緩存,其中一級緩存singletonObjects存放完全實例化的對象,對象以及其依賴的屬性都有值;二級緩存earlySingletonObjects存放半實例化的對象,相當於在內存開闢了空間,已完成創建,但是還未進行屬性賦值,可以提前暴露使用;三級緩存singletonFactories爲對象工廠,用來創建提前暴露的bean並放入二級緩存中。

首次初始化A時,三個緩存中都沒有對象,會進入如下getSingleton(String beanName, ObjectFactory<?> singletonFactory)方法,該方法第二個參數是個函數式接口,當內部調用getObject方法時,會調用createBean方法:

				// 創建bean實例
				if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							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.
							destroySingleton(beanName);
							throw ex;
						}
					});
					......
				

先進入getSingleton方法:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
		Assert.notNull(beanName, "Bean name must not be null");
		synchronized (this.singletonObjects) {
			Object singletonObject = this.singletonObjects.get(beanName);
			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 + "'");
				}
				// bean單例創建前
				beforeSingletonCreation(beanName);
				boolean newSingleton = false;
				boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
				if (recordSuppressedExceptions) {
					this.suppressedExceptions = new LinkedHashSet<>();
				}
				try {
					// 調用createBean方法創建bean
					singletonObject = singletonFactory.getObject();
					newSingleton = true;
				}
				catch (IllegalStateException ex) {
					// Has the singleton object implicitly appeared in the meantime ->
					// if yes, proceed with it since the exception indicates that state.
					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;
					}
					// 創建完成後要從正在實例化的bean集合singletonsCurrentlyInCreation中刪除該bean
					afterSingletonCreation(beanName);
				}
				if (newSingleton) {
					// bean加入緩存
					addSingleton(beanName, singletonObject);
				}
			}
			return singletonObject;
		}
	}

該方法完成流程圖中的四個步驟:
step1:bean單例創建前,將beanName放入singletonsCurrentlyInCreation緩存;
step2: singletonFactory.getObject()調用外面函數式接口中的createBean方法創建bean;
step3afterSingletonCreation方法將beanName從singletonsCurrentlyInCreation緩存刪除,表示已創建完;
step4: 實例化後加入一級緩存,二三級緩存刪除。

以上1、3、4涉及代碼如下:

	protected void beforeSingletonCreation(String beanName) {
		// 將正在創建的bean放入緩存singletonsCurrentlyInCreation
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}
	protected void afterSingletonCreation(String beanName) {
		// 正在創建中的緩存容器singletonsCurrentlyInCreation清除剛剛創建的bean
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
			throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
		}
	}
	protected void addSingleton(String beanName, Object singletonObject) {
		synchronized (this.singletonObjects) {
			// 一級緩存存放bean
			this.singletonObjects.put(beanName, singletonObject);
			// 三級緩存移除bean
			this.singletonFactories.remove(beanName);
			// 二級緩存移除bean
			this.earlySingletonObjects.remove(beanName);
			//
			this.registeredSingletons.add(beanName);
		}
	}

其中step2流程較長,當A第一次實例化時,走到創建無參構造函數實例化createBeanInstance,隨後會走addSingletonFactory方法:

	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這個三級緩存纔是解決循環依賴的關鍵,該代碼在createBeanInstance方法之後,也就是說這個bean其實已經被創建出來了,但是它還不是很完美(沒有進行屬性填充和初始化),但是對於其他依賴它的對象而言已經足夠了(可以根據對象引用定位到堆中對象),能夠被認出來了,所以Spring在這個時候選擇將該對象提前曝光出來讓大家認識認識。當三級緩存有值後,後面如果再次用到該bean的時候,會從三級緩存中,並通過提前暴露,升級到二級緩存中,到這裏我們發現三級緩存singletonFactories和二級緩存earlySingletonObjects中的值都有出處了,那一級緩存在哪裏設置的呢?就是在A創建完,並把A依賴的屬性B也創建完後,B有依賴於A,再次進入A後,A直接從二級緩存中獲取,從而促使B對象創建完,隨即A也就創建完成,A完成createBean後走上面的step4中的addSingletion方法,完成一級緩存的設置。

4.總結

Spring在創建bean的時候並不是等它完全完成,而是在創建過程中將創建中的 bean 的 ObjectFactory 提前曝光(即加入到 singletonFactories 緩存中),這樣一旦下一個 bean 創建的時候需要依賴 bean ,則直接使用 ObjectFactory 的 getObject() 獲取了,故在緩存中使用三級緩存獲取到實例,並將實例升級到二級緩存,供後續實例如需二次使用時,可直接從二級緩存中取,待實例完全創建後,升級到一級緩存,並清理二級三級緩存,總而言之提前暴露三級緩存,以及一二三級緩存的綜合使用是解決循環依賴的關鍵,各級緩存各司其職,又能夠相互呼應,spring的設計實在精妙,給我們自己設計項目提供了一種優秀的思考方式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章