什么是循環(huán)依賴?
????之前提到了在進(jìn)行創(chuàng)建單例Bean的時候有個類參數(shù)singletonCurrentlyInCreation,這個參數(shù)是用來記錄當(dāng)前正在進(jìn)行實例化的beanName。此外還定義了三個存放不同實例的緩存:
? ? 一級緩存:singletonObjects,用來存放以beanName為key,對應(yīng)已完成bean生命周期的bean實例作為value;
? ? 二級緩存:earlySingletonObjects,里面存放的都是提前暴露的單例實例,此時bean的生命周期還未結(jié)束;
? ? 三級緩存:singletonFactories:用來存放以beanName為key,對應(yīng)的BeanFactory為value;
? ? 我們先創(chuàng)建如圖的兩個類,除了各自引用外再添加一個參數(shù),用來跟蹤這個bean的生命周期進(jìn)行到哪一步了,并重寫toString()方法,如圖1、圖2:


? ? 執(zhí)行結(jié)果如圖3,但是按照我們寫的自定義Spring容器,會出現(xiàn)A和B不斷的相互引用,最終耗盡了服務(wù)器資源,這就是循環(huán)依賴,Spring就是利用上面幾個參數(shù)來解決循環(huán)依賴的問題。

Spring如何解決循環(huán)依賴?
? ? 1、在getBean操作的時候,進(jìn)入doGetBean方法,執(zhí)行g(shù)etSingleton(beanName)方法,來嘗試從三個緩存中拿到對應(yīng)的實例,A_BeanName剛進(jìn)來的時候三個緩存中都沒有A_BeanName的值,在singletonCurrentlyInCreation中也沒有正在實例化的beanName,說明目前這個bean還未被創(chuàng)建,如圖4:

? ? 2、由于循環(huán)依賴只發(fā)生在單例的情況,那么直接看圖5:

????先關(guān)注下getSingleton操作,和上面獲取實例的只是個同名方法而已,進(jìn)來一共可以分成四部分,如圖6:”beforeSingletonCreation(創(chuàng)建實例之前的操作)、getObject(創(chuàng)建實例)、afterSingletonCreation(創(chuàng)建實例之后的操作)、addSingleton(如果是新生成的單例,把他加進(jìn)緩存)。beforeSingletonCreation會把當(dāng)前的beanName放進(jìn)singletonCurrentlyInCreation中,如圖7。在調(diào)用getObject的時候會調(diào)用到匿名類的createBean方法來創(chuàng)建實例。


? ? 3、關(guān)注doCreateBean,首先調(diào)用createBeanInstance,本次采用的是無參構(gòu)造方法instantiateBean,把A包裝成A_BeanWrapper,具體的封裝情況參考上節(jié)的getBean流程。通過斷點可以看到此時A_BeanWrapper已經(jīng)包含了B,但是沒有A的其他屬性,B屬性也為null,說明A并沒有創(chuàng)建完成,此時B進(jìn)行未進(jìn)行依賴注入,如圖8:

? ? 4、earlySingletonExposure參數(shù)代表是否能進(jìn)行提前暴露,也可以從這看到提前暴露的條件:單例、允許循環(huán)依賴、當(dāng)前beanName正處于正在創(chuàng)建Bean的列表中。如圖9:

????此時的getEarlyBeanReference方法是用來收集BeanFactory實例的,從實現(xiàn)方法里的getCacheKey中可以看到,工廠Bean前綴特有的&標(biāo)識,如圖10和圖11:


????然后調(diào)用addSingletonFactory往三級緩存中添加A_BeanName和A_BeanFactory,如圖12:

? ? 5、通過populateBean進(jìn)行依賴注入,此時又是一個BeanPostProcessor的應(yīng)用,postProcessProperties方法對@Autowired方法進(jìn)行掃描,如圖13:

????findAutowiringMetadata返回的InjectionMetadata參數(shù)中的injectedElements參數(shù)就是B,如圖14:

????繼續(xù)調(diào)用其inject方法,關(guān)注element.inject方法,由于@Autowired注解是放在參數(shù)上的,因此訪問AutowiredFieldElement中的inject方法,調(diào)用resolveFieldValue,到beanFactory.resolveDependency,到doResolveDependency,到findAutowireCandidates方法獲取到持有@Autowired注解的B,除了往autowiredBeanNames中添加對應(yīng)的beanName外,調(diào)用descripter.resolveCandidate方法,進(jìn)去發(fā)現(xiàn)是一個B_BeanName的getBean操作,如圖15、16:


? ? 6:、和A一樣,重復(fù)之前的實例化流程,在singletonCurrentlyInCreation中添加B_BeanName,通過addSingletonFactory往三級緩存中添加B_BeanName和B_BeanFactory,然后通過populateBean操作,最終通過getBean方法對A進(jìn)行實例化。
? ? 7、又再次來到了第一步的getSingleton方法,此時singletonCurrentlyInCreation中已經(jīng)有A_BeanName,雖然在一二級緩存中沒有對應(yīng)的實例,但是三次緩存中有A_BeanFactory,可以通過getObject拿到提前暴露的實例,然后把它放進(jìn)二級緩存,打斷點可以看到此時的A_SingletonObject中的B類屬性為null,如圖17::

????但是此時走的就不是else的路線了,關(guān)注getObjectForBeanInstance方法,最終也是返回一個對象實例,如果是工廠實例,則會調(diào)用其getObject方法生成對象實例返回,期間并沒有對其余屬性進(jìn)行諸如,我們在doGetBean的return的返回值中可以看到,如圖18:

? ? 8、步驟2進(jìn)行的getSingleton方法在getObject的方法終于有返回了,如圖19,返回的是B的實例,可以看到生成的B實例中已經(jīng)包含了testB屬性,以及提前暴露的A屬性。

????然后再進(jìn)行afterSingletonCreation方法,把B_BeanName從singletonCurrentlyInCreation中移除,如圖20:

????并通過addSingleton,把B_BeanName和B實例放入一級緩存后返回,此時A屬性中的B屬性依舊是null,如圖21:

? ? 9、從上面的流程看,B實例化是由A的實例化中B屬性的依賴注入觸發(fā)getBean完成的,現(xiàn)在B已經(jīng)被實例化了,因此A的B屬性也可以完成依賴注入了,這樣A中的B屬性也有值。由于B的屬性指向了A實例的堆空間,所以B類中的A屬性也被賦予值了,如圖22:

? ? 至此,Spring循環(huán)依賴的流程已經(jīng)結(jié)束了。
? ? 自定義Spring容器代碼地址:https://github.com/LuoChen1996/identitify_spring.git
? ? Spring源碼測試代碼地址:https://github.com/LuoChen1996/my_spring.git