Spring循環依賴解決方式源碼解析

1. 什麼是循環依賴?

循環依賴其實就是循環引用,也就是兩個或則兩個以上的bean互相持有對方,最終形成閉環。比如A依賴於B,B依賴於A我們直接上代碼

先創建一個類ServiceA依賴於ServiceB,然後ServiceB又依賴於ServiceA

@Component
public class ServiceA {

    @Autowired
    private  ServiceB serviceb;
@Component
public class ServiceB {

    @Autowired
    private  ServiceA servicea;

2.Spring的單例對象的初始化主要分爲三步:

  1. createBeanInstance:實例化,其實也就是調用對象的構造方法實例化對象

  2. 填充屬性,這一步主要是多bean的依賴屬性進行填充

  3. 調用實現了InitializingBean的接口,實現afterPropertiesSet方法,或者調用在配置文件中同過init-method的方法。

  4.  從上面單例bean的初始化可以知道:循環依賴主要發生在第一、二步,也就是構造器循環依賴和field循環依賴。那麼我們要解決循環引用也應該從初始化過程着手,對於單例來說,在Spring容器整個生命週期內,有且只有一個對象,所以很容易想到這個對象應該存在Cache中,Spring爲了解決單例的循環依賴問題,使用了三級緩存。

        這裏會涉及到在spring內部所使用的兩個內部屬性,singletonFactories和earlySingletonObjects,這兩個屬性在類DefaultSingletonBeanRegistry中被定義,定義如下:

下面我們就看下spring創建單例bean的步驟吧

第一次創建ServiceA的步驟如下

org.springframework.beans.factory.support.AbstractBeanFactory#getBean方法在進入

由於是首次創建ServiceA在,singletonFactories和earlySingletonObjects和singletonFactories都不存在。所以返回了null。

然後通過反設創建了一個serviceA的BeanWrapper,然後在調用org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory這時候我們singletonFactories就存在serviceA了

緊接着就是調用populateBean填充ServiceA的屬性了,由於ServiceA有屬性ServiceB,此時我們的ServiceB還沒有被加載,然後ServiceA有需要ServiceB於是spring又去調用了創建ServiceB的方法

於是我們接着繼續填充ServiceB的屬性,由於ServiceB又依賴ServiceA,於是再去創建ServiceA由於此時ServiceA已經在singletonFactories裏面了,然後我們就返回了一個是ServiceA(此時的屬性serviceB是爲null的),並且ServiceA從singletonFactories裏面移除掉加入到earlySingletonObjects。

這樣做有什麼好處呢?讓我們來分析一下“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三級緩存的前提是執行了構造器,所以構造器的循環依賴沒法解決。

 

參考:https://blog.csdn.net/u010853261/article/details/77940767

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