spring--解決循環依賴


    首先看下spring建立一個bean的簡單流程,假如beanA引用beanB,beanB引用beanA,spring在初始化beanA的時候會形成循環依賴(這裏講的是單例,spring底層只對單例循環依賴進行解決)。

       在記錄以前我寫了兩個測試類進還原循環依賴,一個是ClassA,裏面的引用了ClassB,同時ClassB也引用了ClassA。這樣子ClassA和ClassB造成了循環依賴。



         spring容器底層在建立ClassB Bean會調用getSingleton先去從一級緩存singletonObjects中拿,若是一級緩存沒有,則去二級緩存earlySingletonObjects中拿,二級緩存中沒有,則去三級緩存singletonFactories中拿,若是都沒有,則調用createBean方法開始建立這個ClassB這個Bean。

       在調用 createbean以前spring會調用isPrototypeCurrentlyInCreation方法來判斷當前這個bean存在正在建立中的緩存中(prototypesCurrentlyInCreation),若是不是則放入該緩存中,用於後面循環依賴的解決。由於ClassB是第一次被spring容器加載,因此確定是空,這時候被放入正在建立中的緩存中。

        接着createbean會調用doCreateBean方法,這個真正執行建立bean的方法。該方法調用createBeanInstance(beanName, mbd, args)經過後置處理器判斷調用ClassB的構造方法並建立返回ClassB的實例對象,此時對象的ClassA的引用確定是空,由於ClassB的默認構造方法,並無對ClassA賦值。接着spring會將這個早期對象放入三級緩存singletonFactories中。

      放入三級緩存singletonFactories結束後,調用populateBean方法進行屬性賦值。populateBean中會去判斷ClassB這個Bean有哪些屬性以及屬性的類型並選擇調用哪一個方法來進行賦值,若是是引用類型則調用resolveReference方法進行屬性賦值。

       進入resolveReference方法,spring會先去判斷當前容器是否有父容器,若是有則從父容器中獲取引用對象ClassA,若是沒有則從當前容器中獲取引用對象ClassA。(spring容許子容器使用父容器的bean,就是在這裏體現出來,好比springmvc)這時候程序會調用this.beanFactory.getBean(resolvedName)。從容器中再次獲取ClassA這個引用對象。此時getBean會調用doGetBean重新走剛纔建立ClassB對象流程。



      一樣spring在建立ClassB的引用對象ClassA時,也會去解析ClassA的引用對象。此時ClassA的引用對象是ClassB(此時ClassB對象是暴露在三級緩存中的),這是時候通用調用當前容器的getBean--->doGetBean,在doGetBean中調用getSingleton(beanName)方法。在這裏咱們會看到spring在從二級或三級緩存中獲取對象是有條件的,條件即便這個對象正在建立中。經過isSingletonCurrentlyInCreation(beanName)這個方法去判斷的。

       確實剛建立ClassB的時候記錄了ClassB正在建立中,這時候從一級緩存中獲取(確定沒有由於ClassB還沒徹底實例化完),接着繼續從二級緩存中獲取也沒有(因從剛纔分析下來,ClassB只單單暴露在三級緩存中),這時候調用singletonObject = singletonFactory.getObject()這個方法,從三級緩存中拿到了早期對象。singletonFactory.getObject()會進一步調用getObject()方法中的getEarlyBeanReference,這裏面調用了spring的後置處理器,給開發者對早期對象可以進行提早的修改。好比ClassB 對象裏有String str="";能夠提早對str進行操做。因此爲何spring有二級緩存就能夠解決循環依賴。還要用到三級緩存。spring在三級緩存給開發提供了給早期對象擴展的功能。此時將擴展完的早期對象ClassB放入二級緩存,移除三級緩存,並將這個對象返回出去。

      此時ClassA對象ClassB的引用已經拿到值了,因此建立一直往下走,走到addSingleton(beanName, singletonObject)這個方法,將ClassA放入一級緩存singletonObjects,從二級緩存和三級緩存中移除。由於ClassA的bean已經建立完成了,二級緩存和三級緩存已經沒用了。此時完整的ClassA的bean返回出去,ClassB的屬性ClassA也獲得了賦值,ClassB的bean能夠繼續建立了。

     注意:spring在經過構造器給屬性賦值是沒法解決循環依賴的,從上面分析來看spring執行createBeanInstance方法去判斷調用當前bean的構造方法,此時還未放入三級緩存中。若是是bean做用域是原型也是沒法解決循環依賴,由於原型對象並無放在緩存中。

spring

相關文章
相關標籤/搜索