天天看點

Spring Bean的生命周期

Spring Bean的生命周期

Spring Bean的生命周期

可以簡述為以下九步

執行個體化bean對象(通過構造方法或者工廠方法)

設定對象屬性(setter等)(依賴注入)

如果Bean實作了BeanNameAware接口,工廠調用Bean的setBeanName()方法傳遞Bean的ID。(和下面的一條均屬于檢查Aware接口)

如果Bean實作了BeanFactoryAware接口,工廠調用setBeanFactory()方法傳入工廠自身

将Bean執行個體傳遞給Bean的前置處理器的postProcessBeforeInitialization(Object bean,String beanname)方法

調用Bean的初始化方法

将Bean執行個體傳遞給Bean的後置處理器的postProcessAfterInitialization(Object bean,String beanname)方法

使用Bean

容器關閉之前,調用Bean的銷毀方法

beans.xm

<col>

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

Spring Bean的生命周期

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

Spring中循環依賴場景有:'

構造器的循環依賴

field屬性的循環依賴

Spring的單例對象的初始化主要分為三步:

Spring Bean的生命周期

(1)createBeanInstance:執行個體化,其實也就是調用對象的構造方法執行個體化對象

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

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

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

那麼我們要解決循環引用也應該從初始化過程着手,對于單例來說,在Spring容器整個生命周期内,有且隻有一個對象,是以很容易想到這個對象應該存在Cache中,Spring為了解決單例的循環依賴問題,使用了三級緩存。

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

/** Cache of singleton objects: bean name --&gt; bean instance */

private final Map&lt;String, Object&gt; singletonObjects = new ConcurrentHashMap&lt;String, Object&gt;(256);

/** Cache of singleton factories: bean name --&gt; ObjectFactory */

private final Map&lt;String, ObjectFactory&lt;?&gt;&gt; singletonFactories = new HashMap&lt;String, ObjectFactory&lt;?&gt;&gt;(16);

/** Cache of early singleton objects: bean name --&gt; bean instance */

private final Map&lt;String, Object&gt; earlySingletonObjects = new HashMap&lt;String, Object&gt;(16);

這三級緩存分别指

singletonFactories : 單例對象工廠的cache

earlySingletonObjects :提前暴光的單例對象的Cache

singletonObjects:單例對象的cache

我們在建立bean的時候,首先想到的是從cache中擷取這個單例的bean,這個緩存就是singletonObjects。Spring首先從一級緩存singletonObjects中擷取。如果擷取不到,并且對象正在建立中,就再從二級緩存earlySingletonObjects中擷取。如果還是擷取不到且允許singletonFactories通過getObject()擷取,就從三級緩存singletonFactory.getObject()(三級緩存)擷取。如果擷取到了則,從singletonFactories中移除,并放入earlySingletonObjects中。其實也就是從三級緩存移動到了二級緩存。從上面三級緩存的分析,我們可以知道,Spring解決循環依賴的訣竅就在于singletonFactories這個三級cache。

這裡就是解決循環依賴的關鍵,這段代碼發生在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對象後順利完成了初始化階段1、2、3,完全初始化之後将自己放入到一級緩存singletonObjects中。此時傳回A中,A此時能拿到B的對象順利完成自己的初始化階段2、3,最終A也完成了初始化,進去了一級緩存singletonObjects中,而且更加幸運的是,由于B拿到了A的對象引用,是以B現在hold住的A對象完成了初始化。

知道了這個原理時候,肯定就知道為啥Spring不能解決"A的構造方法中依賴了B的執行個體對象,同時B的構造方法中依賴了A的執行個體對象"這類問題了!因為加入singletonFactories三級緩存的前提是執行了構造器,是以構造器的循環依賴沒法解決。

Spring Bean的生命周期