Spring是怎麼解決循環依賴的

1. 什麼是循環依賴?

循環依賴其實就是循環引用,也就是兩個或則兩個以上的bean互相持有對方,最終造成閉環。好比A依賴於B,B依賴於C,C又依賴於A。以下圖:spring

這裏寫圖片描述

注意,這裏不是函數的循環調用,是對象的相互依賴關係。循環調用其實就是一個死循環,除非有終結條件。緩存

Spring中循環依賴場景有: 
(1)構造器的循環依賴 
(2)field屬性的循環依賴。函數

2. 怎麼檢測是否存在循環依賴

檢測循環依賴相對比較容易,Bean在建立的時候能夠給該Bean打標,若是遞歸調用回來發現正在建立中的話,即說明了循環依賴了。this

3. Spring怎麼解決循環依賴

Spring的循環依賴的理論依據實際上是基於Java的引用傳遞,當咱們獲取到對象的引用時,對象的field或則屬性是能夠延後設置的(可是構造器必須是在獲取引用以前)。spa

Spring的單例對象的初始化主要分爲三步: 
bean初始化 
(1)createBeanInstance:實例化,其實也就是調用對象的構造方法實例化對象code

(2)populateBean:填充屬性,這一步主要是多bean的依賴屬性進行填充xml

(3)initializeBean:調用spring xml中的init 方法。對象

從上面講述的單例bean初始化步驟咱們能夠知道,循環依賴主要發生在第1、第二部。也就是構造器循環依賴和field循環依賴。blog

那麼咱們要解決循環引用也應該從初始化過程着手,對於單例來講,在Spring容器整個生命週期內,有且只有一個對象,因此很容易想到這個對象應該存在Cache中,Spring爲了解決單例的循環依賴問題,使用了三級緩存遞歸

首先咱們看源碼,三級緩存主要指:

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

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

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

這三級緩存分別指: 
singletonFactories : 單例對象工廠的cache 
earlySingletonObjects :提早暴光的單例對象的Cache 
singletonObjects:單例對象的cache

咱們在建立bean的時候,首先想到的是從cache中獲取這個單例的bean,這個緩存就是singletonObjects。主要調用方法就就是:

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

上面的代碼須要解釋兩個參數:

  • isSingletonCurrentlyInCreation()判斷當前單例bean是否正在建立中,也就是沒有初始化完成(好比A的構造器依賴了B對象因此得先去建立B對象, 或則在A的populateBean過程當中依賴了B對象,得先去建立B對象,這時的A就是處於建立中的狀態。)
  • allowEarlyReference 是否容許從singletonFactories中經過getObject拿到對象

分析getSingleton()的整個過程,Spring首先從一級緩存singletonObjects中獲取。若是獲取不到,而且對象正在建立中,就再從二級緩存earlySingletonObjects中獲取。若是仍是獲取不到且容許singletonFactories經過getObject()獲取,就從三級緩存singletonFactory.getObject()(三級緩存)獲取,若是獲取到了則:

this.earlySingletonObjects.put(beanName, singletonObject);
                        this.singletonFactories.remove(beanName);

從singletonFactories中移除,並放入earlySingletonObjects中。其實也就是從三級緩存移動到了二級緩存。

從上面三級緩存的分析,咱們能夠知道,Spring解決循環依賴的訣竅就在於singletonFactories這個三級cache。這個cache的類型是ObjectFactory,定義以下:

public interface ObjectFactory<T> {
    T getObject() throws BeansException;
}

這個接口在下面被引用

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

這裏就是解決循環依賴的關鍵,這段代碼發生在createBeanInstance以後,也就是說單例對象此時已經被建立出來(調用了構造器)。這個對象已經被生產出來了,雖然還不完美(尚未進行初始化的第二步和第三步),可是已經能被人認出來了(根據對象引用能定位到堆中的對象),因此Spring此時將這個對象提早曝光出來讓你們認識,讓你們使用。

這樣作有什麼好處呢?讓咱們來分析一下「A的某個field或者setter依賴了B的實例對象,同時B的某個field或者setter依賴了A的實例對象」這種循環依賴的狀況。A首先完成了初始化的第一步,而且將本身提早曝光到singletonFactories中,此時進行初始化的第二步,發現本身依賴對象B,此時就嘗試去get(B),發現B尚未被create,因此走create流程,B在初始化第一步的時候發現本身依賴了對象A,因而嘗試get(A),嘗試一級緩存singletonObjects(確定沒有,由於A還沒初始化徹底),嘗試二級緩存earlySingletonObjects(也沒有),嘗試三級緩存singletonFactories,因爲A經過ObjectFactory將本身提早曝光了,因此B可以經過ObjectFactory.getObject拿到A對象(雖然A尚未初始化徹底,可是總比沒有好呀),B拿到A對象後順利完成了初始化階段一、二、3,徹底初始化以後將本身放入到一級緩存singletonObjects中。此時返回A中,A此時能拿到B的對象順利完成本身的初始化階段二、3,最終A也完成了初始化,進去了一級緩存singletonObjects中,並且更加幸運的是,因爲B拿到了A的對象引用,因此B如今hold住的A對象完成了初始化。

知道了這個原理時候,確定就知道爲啥Spring不能解決「A的構造方法中依賴了B的實例對象,同時B的構造方法中依賴了A的實例對象」這類問題了!由於加入singletonFactories三級緩存的前提是執行了構造器,因此構造器的循環依賴無法解決。

相關文章
相關標籤/搜索