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


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

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

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

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


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

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

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


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

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

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

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


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

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

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

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

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

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

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