spring是如何解決循環依賴的


首先,需要明確的是spring對循環依賴的處理有三種情況:

①構造器的循環依賴:這種依賴spring是處理不了的,直 接拋出BeanCurrentlylnCreationException異常。

②單例模式下的setter循環依賴:通過“三級緩存”處理循環依賴。

③非單例循環依賴:無法處理。

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

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

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

1、構造器循環依賴

this .singletonsCurrentlylnCreation.add(beanName)將當前正要創建的bean 記錄在緩存中 Spring 容器將每一個正在創建的bean 標識符放在一個“當前創建bean 池”中, bean 標識 柏:在創建過程中將一直保持在這個池中,因此如果在創建bean 過程中發現自己已經在“當前 創建bean 池” 里時,將拋出BeanCurrentlylnCreationException 異常表示循環依賴;而對於創建 完畢的bean 將從“ 當前創建bean 池”中清除掉。

2、setter循環依賴

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

/** 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);

這三級緩存的作用分別是:

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

earlySingletonObjects :完成實例化但是尚未初始化的,提前暴光的單例對象的Cache (二級緩存)

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對象后順利完成了初始化階段1、2、3,完全初始化之后將自己放入到一級緩存singletonObjects中。java培訓此時返回A中,A此時能拿到B的對象順利完成自己的初始化階段2、3,最終A也完成了初始化,進去了一級緩存singletonObjects中,而且更加幸運的是,由於B拿到了A的對象引用,所以B現在hold住的A對象完成了初始化。

3、非單例循環依賴

對於“prototype”作用域bean, Spring 容器無法完成依賴注入,因為Spring 容器不進行緩 存“prototype”作用域的bean ,因此無法提前暴露一個創建中的bean 。


免責聲明!

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



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