spring是如何解決循環依賴的?

循環依賴就是N個類中循環嵌套引用,若是在平常開發中咱們用new 對象的方式發生這種循環依賴的話程序會在運行時一直循環調用,直至內存溢出報錯。下面說一下Spring是若是解決的。spring

首先,須要明確的是spring對循環依賴的處理有三種狀況: ①構造器的循環依賴:這種依賴spring是處理不了的,直 接拋出BeanCurrentlylnCreationException異常。 ②單例模式下的setter循環依賴:經過「三級緩存」處理循環依賴。 ③非單例循環依賴:沒法處理。緩存

spring單例對象的初始化大略分爲三步:bash

  1. createBeanInstance:實例化,其實也就是調用對象的構造方法實例化對象
  2. populateBean:填充屬性,這一步主要是多bean的依賴屬性進行填充
  3. initializeBean:調用spring xml中的init 方法。

從上面講述的單例bean初始化步驟咱們能夠知道,循環依賴主要發生在第1、第二步。也就是構造器循環依賴和field循環依賴。 接下來,咱們具體看看spring是如何處理三種循環依賴的。ui

一、構造器循環依賴

this .singletonsCurrentlylnCreation.add(beanName)將當前正要建立的bean 記錄在緩存中 Spring 容器將每個正在建立的bean 標識符放在一個「當前建立bean 池」中, bean 標識 柏:在建立過程當中將一直保持在這個池中,所以若是在建立bean 過程當中發現本身已經在「當前 建立bean 池」 裏時,將拋出BeanCurrentlylnCreationException 異常表示循環依賴;而對於建立 完畢的bean 將從「 當前建立bean 池」中清除掉。this

二、setter循環依賴

Spring爲了解決單例的循環依賴問題,使用了三級緩存。spa

/** Cache of singleton objects: bean name –> bean instance */
private final Map singletonObjects = new ConcurrentHashMap(256);
/** Cache of singleton factories: bean name –> ObjectFactory */
private final Map> singletonFactories = new HashMap>(16);
/** Cache of early singleton objects: bean name –> bean instance */
private final Map earlySingletonObjects = new HashMap(16);
複製代碼

這三級緩存的做用分別是:prototype

singletonFactories : 進入實例化階段的單例對象工廠的cache (三級緩存)code

earlySingletonObjects :完成實例化可是還沒有初始化的,提早暴光的單例對象的Cache (二級緩存)xml

singletonObjects:完成初始化的單例對象的cache(一級緩存)對象

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

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    //isSingletonCurrentlyInCreation()判斷當前單例bean是否正在建立中
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        synchronized (this.singletonObjects) {
            singletonObject = this.earlySingletonObjects.get(beanName);
            //allowEarlyReference 是否容許從singletonFactories中經過getObject拿到對象
            if (singletonObject == null && allowEarlyReference) {
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    //從singletonFactories中移除,並放入earlySingletonObjects中。
                    //其實也就是從三級緩存移動到了二級緩存
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
複製代碼

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

public interface ObjectFactory<T> {
    T getObject() throws BeansException;
}
複製代碼

這個接口在AbstractBeanFactory裏實現,並在核心方法doCreateBean()引用下面的方法:

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以後,populateBean()以前,也就是說單例對象此時已經被建立出來(調用了構造器)。這個對象已經被生產出來了,此時將這個對象提早曝光出來,讓你們使用。

這樣作有什麼好處呢?讓咱們來分析一下「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對象完成了初始化。

三、非單例循環依賴

對於「prototype」做用域bean, Spring 容器沒法完成依賴注入,由於Spring 容器不進行緩 存「prototype」做用域的bean ,所以沒法提早暴露一個建立中的bean 。

相關文章
相關標籤/搜索