wordpress自動登錄ftp家庭優(yōu)化大師免費下載
目錄
- 一、概念
- 1、三級緩存的作用
- 2、循環(huán)依賴的含義
- 二、代碼
- 1、代碼下載
- 2、文件功能介紹
- 3、源碼分析
- 3.1、找到獲取A對象的位置,打斷點進行debug操作
- 3.2、一步步找到在A對象中注入B對象的位置
- 3.3、一步步找到B對象注入A對象的位置
- 3.4、往下找到通過三級緩存解決循環(huán)依賴的地方
- 3.5、完成對象B注入對象A
- 3.6、完成對象B的剩余流程,進而回到對象A注入對象B的地方
- 3.7、查看對象A不會再次執(zhí)行代理代碼的地方
- 3.8、找到往一級緩存中添加對象A的代理對象的位置
- 3.9、往對象C中注入對象A
- 三、流程分析
- 四、參考文章
一、概念
1、三級緩存的作用
- 解決循環(huán)依賴(即:三級緩存 singletonFactories的功能)
- 實現(xiàn)動態(tài)代理 (即:二級緩存 earlySingletonObjects的功能)
- 存儲最終對象 (即:一級緩存 singletonObjects的功能)
2、循環(huán)依賴的含義
N個類循環(huán)(嵌套)引用。
通俗的講就是N個Bean互相引用對方,最終形成閉環(huán)。用一副經(jīng)典的圖示可以表示成這樣(A、B、C都代表對象,虛線代表引用關(guān)系):
注意:其實可以N=1,也就是極限情況的循環(huán)依賴:自己依賴自己
另需注意:這里指的循環(huán)引用不是方法之間的循環(huán)調(diào)用,而是對象的相互依賴關(guān)系。(方法之間循環(huán)調(diào)用若有出口也是能夠正常work的)
可以設(shè)想一下這個場景:如果在日常開發(fā)中我們用new對象的方式,若構(gòu)造函數(shù)之間發(fā)生這種循環(huán)依賴的話,程序會在運行時一直循環(huán)調(diào)用最終導(dǎo)致內(nèi)存溢出,示例代碼如下:
public class Main {public static void main(String[] args) throws Exception {System.out.println(new A());}}class A {public A() {new B();}
}class B {public B() {new A();}
}
這是一個典型的循環(huán)依賴問題。Spring通過三級緩存機制巧妙的解決循環(huán)依賴問題~
二、代碼
1、代碼下載
百度網(wǎng)盤鏈接:https://pan.baidu.com/s/1Cq1kTJJesOfl0eHIU3cMIA?pwd=gyg1
提取碼:gyg1
2、文件功能介紹
- CodeStudyApplication類:主啟動類
- A類:通過IOC方式注入類B和類C的單例對象,用來測試循環(huán)依賴;并且在成員方法test()上添加@Transactional注解,用來測試動態(tài)代理
- B類:通過IOC方式注入類A的單例對象,用來測試循環(huán)依賴
- C類:通過IOC方式注入類A的單例對象,用來測試循環(huán)依賴,類C和類B的主要區(qū)別是“當B對象觸發(fā)A對象的循環(huán)依賴后,可以獲取A對象的代理對象,然后看C對象如何獲取A對象的代理對象”
- application.yml:添加一些mysql信息,大家不用修改,不會報錯
3、源碼分析
注意: 博主使用的開發(fā)工具是IDEA
,所以提到的快捷鍵都是IDEA中的
3.1、找到獲取A對象的位置,打斷點進行debug操作
當程序啟動時,最先運行的是com.atguigu.test.CodeStudyApplication
類中的main
方法。
根據(jù)代碼執(zhí)行順序,我們按著Ctrl
按鍵后點擊SpringApplication
類的靜態(tài)方法run()
:
現(xiàn)在進入了SpringApplication
類的靜態(tài)方法run()
,然后我們按著Ctrl
按鍵后點擊重載的靜態(tài)方法run()
:
繼續(xù)按著Ctrl
按鍵后點擊成員方法run()
:
現(xiàn)在進入了一個方法內(nèi)容較多的run()方法,繼續(xù)按著Ctrl
按鍵后點擊成員方法refreshContext(context);
,由于方法體內(nèi)很多內(nèi)容并非本節(jié)重點,所以不在本節(jié)講解,其中IOC的主要內(nèi)容都在refreshContext(context)
方法體現(xiàn)。
繼續(xù)按著Ctrl
按鍵后點擊成員方法refresh(context);
:
繼續(xù)按著Ctrl + Alt
按鍵后點擊applicationContext
對象的refresh()
方法
下圖中refresh
方法參數(shù)中的applicationContext
對象大家應(yīng)該很熟悉,來看一道常見面試題:“BeanFactory與ApplicationContext的區(qū)別?”,哈哈哈,想起來了吧~
上述操作之后,頁面中會出現(xiàn)三個實現(xiàn)類供我們選擇,我們選擇實現(xiàn)類ServletWebServerApplicationContext
,該實現(xiàn)類是我們常用的,至于選它的原因不是本節(jié)重點,所以不再解釋。
繼續(xù)按著Ctrl
按鍵后點擊父類方法super.refresh();
:
繼續(xù)按著Ctrl
按鍵后點擊成員方法finishBeanFactoryInitialization(beanFactory);
其中對象構(gòu)建、注入其他對象、代理、初始化等流程都在該方法中,其他代碼不是本節(jié)重點,所以不在講解。
繼續(xù)按著Ctrl + Alt
按鍵后點擊beanFactory.preInstantiateSingletons();
方法:
我們把斷點打在成員方法getBean(beanName);
上
其中遍歷的beanNames
方法來自于成員集合變量beanDefinitionNames
,該集合的值來自于@ComponentScan
掃描,以及其他注解操作的結(jié)果
3.2、一步步找到在A對象中注入B對象的位置
通過按F9
快速運行程序遍歷beanNames
,找到beanName
等于a
的情況停下:
點擊F7
進入方法getBean(beanName);
:
點擊F7
進入方法doGetBean()
方法:
點擊F8
進入下一步,到達getSingleton(beanName);
這一行:
點擊F7
進入以下方法:
再次點擊F7
進入以下方法:
此時singletonObjects
中不存在名字叫做a
的對象,所以singletonObject
等于null;由于A對象還沒有開始創(chuàng)建,所以isSingletonCurrentlyInCreation(beanName)
的結(jié)果是false,因此第1
個if方法不會執(zhí)行,所以最終得知singletonObject
的結(jié)果是null
點擊兩次F8
就可以回到Object sharedInstance = getSingleton(beanName);
:
依然在上述doGetBean
方法中,我們往下找到if (mbd.isSingleton())
這一行,然后在這一行上單擊鼠標右鍵,點擊Run toCursor
:
然后點擊F8
往下執(zhí)行一行代碼,此時就到了getSingleton
方法處,該方法有2個參數(shù),分別是bean對象名稱和一個匿名內(nèi)部類,此時點擊F7
進入getSingleton
方法;
我們首先看下beforeSingletonCreation(beanName);
,進入該方法的方法體看下,其中singletonsCurrentlyInCreation
集合記錄了正在創(chuàng)建的對象,對象A不在里面,所以會被加入到singletonsCurrentlyInCreation
集合中
然后往下可以看到singletonObject = singletonFactory.getObject();
這行代碼,真正執(zhí)行的是形參對應(yīng)的匿名內(nèi)部類中的實現(xiàn)方法,回顧上一張截圖,執(zhí)行g(shù)etObject方法其實就是執(zhí)行createBean
方法:
回顧一下上面那張截圖,我們找到createBean
方法:
點擊Ctrl + Alt
,在createBean
方法上點擊鼠標左鍵,就可以進入該方法:
繼續(xù)按著Ctrl
按鍵后點擊成員方法doCreateBean(beanName, mbdToUse, args);
:
在該方法中,往下滾動屏幕,可以看到addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
,先來分析一下addSingletonFactory
方法的方法體;現(xiàn)在回頭看下addSingletonFactory
方法的第2個參數(shù),這是一個匿名內(nèi)部類,我們后面會用到它,大家用筆記一下,后續(xù)我們還會回來的~
然后往下滑可以看到populateBean(beanName, mbd, instanceWrapper);
方法:
繼續(xù)按著Ctrl + Alt
按鍵后點擊成員方法postProcessProperties()
方法:
在A類中注入對象B使用@Resource
注解,所以我們選擇CommonAnnotationBeanPostProcessor
類
繼續(xù)按著Ctrl
按鍵后點擊成員方法metadata.inject(bean, beanName, pvs);
:
繼續(xù)按著Ctrl
按鍵后點擊成員方法element.inject(target, beanName, pvs);
:
繼續(xù)按著Ctrl + Alt
按鍵后點擊成員方法getResourceToInject()
方法,選擇CommonAnnotationBeanPostProcessor
類:
上述方法的作用就是在A對象中注入B對象
3.3、一步步找到B對象注入A對象的位置
由于在A類中引入的對象B上沒有加@Lazy
注解,所以lazyLookup
是false
,因此走getResource
方法,繼續(xù)按著Ctrl
按鍵后點擊成員方法getResource(this, requestingBeanName)
:
繼續(xù)按著Ctrl
按鍵后點擊成員方法autowireResource()
:
繼續(xù)按著Ctrl + Alt
按鍵后點擊方法resolveBeanByName()
:
可以看到代碼中調(diào)用了getBean
方法,非常熟悉吧,又回到最初的起點,繼續(xù)根據(jù)name
信息獲取Bean工廠中的對象,現(xiàn)在是從對象A中準備注入對象B
后面過程和通過對象A的名稱來調(diào)用getBean(name)
方法一樣,我們把過程走一下吧~
- 點擊Ctrl:
org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String, java.lang.Class<T>)
- 點擊Ctrl:
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
- 點擊Ctrl:
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)
- 點擊Ctrl:
org.springframework.beans.factory.support.AbstractBeanFactory#createBean
- 點擊Ctrl:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
- 點擊Ctrl:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
- 點擊Ctrl + Alt:
org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessProperties
,選擇CommonAnnotationBeanPostProcessor
類 - 點擊Ctrl:
org.springframework.beans.factory.annotation.InjectionMetadata#inject
- 點擊Ctrl:
org.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement#inject
- 我們在對象B中注入了對象A,此時執(zhí)行的
field.set(target, getResourceToInject(target, requestingBeanName));
就是獲取對象A的 - 點擊Ctrl + Alt:
org.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement#getResourceToInject
,選擇CommonAnnotationBeanPostProcessor
類 - 由于B類中注入的對象A也沒加@Lazy注解,所以走
getResource(this, requestingBeanName)
方法 - 點擊Ctrl:
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#getResource
- 點擊Ctrl:
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#autowireResource
- 點擊Ctrl + Alt:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#resolveBeanByName
- 現(xiàn)在我們來到getBean方法,終極反轉(zhuǎn)了,現(xiàn)在是從對象B中準備注入對象A
3.4、往下找到通過三級緩存解決循環(huán)依賴的地方
現(xiàn)在是B對象準備注入對象A,繼續(xù)按著Ctrl
按鍵后點擊方法getBean(name, descriptor.getDependencyType())
:
繼續(xù)按著Ctrl
按鍵后點擊方法doGetBean(name, requiredType, null, false)
:
繼續(xù)按著Ctrl
按鍵后點擊方法getSingleton(beanName)
:
繼續(xù)按著Ctrl
按鍵后點擊方法getSingleton(beanName, true)
:
然后進入方法體中,由于對象A還沒有創(chuàng)建完成,所以一級緩存singletonObjects
肯定沒有,因此singletonObject
對象是null
我們看下isSingletonCurrentlyInCreation(beanName)
方法的方法體,其中singletonsCurrentlyInCreation
集合作用在上面已經(jīng)解釋過了,就是記錄當前正在被創(chuàng)建的bean對象名稱,在處理A對象時調(diào)用getSingleton》beforeSingletonCreation
方法中已經(jīng)將a
存儲到了singletonsCurrentlyInCreation
集合中,所以此處isSingletonCurrentlyInCreation(beanName)
方法的結(jié)果肯定是true
此時代碼繼續(xù)往下走,由于此時對象A的信息存儲在三級緩存singletonFactories
中,因此也無法從earlySingletonObjects
集合中去除對象A的相關(guān)信息,所以singletonObject = this.earlySingletonObjects.get(beanName);
的結(jié)果也是null
由于上面調(diào)用getSingleton()
方法時傳入的參數(shù)allowEarlyReference
是true
,因此方法會繼續(xù)向下走
為解決并發(fā)創(chuàng)建對象問題,所以需要將一級對象singletonObjects
鎖起來,在同步代碼塊中嘗試從一級緩存singletonObjects
和二級緩存earlySingletonObjects
中得到對象A,但是此時對象A存儲在三級緩存singletonFactories
中,因此依然獲取不到
此時準備從三級緩存singletonFactories
中獲取對象A,由于之前在往對象A進行依賴注入之前往三級緩存中填充過值,所以此時singletonFactory
是有值的,來給大家回憶一下填充值的位置
此時singletonFactory
的值就是上圖框中的匿名內(nèi)部類,所屬的接口是一個函數(shù)式接口,此時代碼繼續(xù)往下執(zhí)行:
當執(zhí)行singletonFactory.getObject()
的時候,其實執(zhí)行的是匿名內(nèi)部類中的方法,我們把斷點打到org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#getEarlyBeanReference
方法中的if
判斷上
繼續(xù)按著
Ctrl + Alt
按鍵后點擊方法bp.getEarlyBeanReference(exposedObject, beanName)
:
此時將沒有被包裝過的對象放到earlyProxyReferences
集合中,該集合作用是記錄已經(jīng)執(zhí)行過代理方法的對象信息,即執(zhí)行過wrapIfNecessary(bean, beanName, cacheKey)
方法的對象信息;由于代理這個動作只能執(zhí)行一次,下次不能在執(zhí)行了,所以將已經(jīng)執(zhí)行過代理的對象信息記錄在earlyProxyReferences
集合中,后續(xù)在需要執(zhí)行代理的時候,我們判斷下該集合中是否已經(jīng)包含,進而決定是否還要執(zhí)行wrapIfNecessary
方法,這個我們后續(xù)會聊到的,這里不在展開~
大家注意現(xiàn)在情況是往對象B
中注入對象A
,目前處于一個獲取對象A的過程中,由于A
類的test()
方法上添加了@Transactional
注解,所以在執(zhí)行wrapIfNecessary
方法之后將會生成A類的代理對象,既然到這里了,我們跟進去看下
繼續(xù)按著Ctrl
按鍵后點擊方法wrapIfNecessary(bean, beanName, cacheKey)
:
當getAdvicesAndAdvisorsForBean
方法執(zhí)行之后,就可以知道代理信息,如下:
繼續(xù)按著Ctrl
按鍵后點擊方法createProxy()
:
進入該方法往下就可以找到真正執(zhí)行代理的地方,即:proxyFactory.getProxy(classLoader)
,繼續(xù)按著Ctrl
按鍵后點擊方法proxyFactory.getProxy(classLoader)
:
繼續(xù)按著Ctrl
按鍵后點擊方法createAopProxy()
:
繼續(xù)按著Ctrl + Alt
按鍵后點擊方法createAopProxy(this)
:
可以非常清晰的看到使用CGLib還是JDK動態(tài)代理方式
最終一層層返回代理結(jié)果,那么getEarlyBeanReference()
方法的返回值就是A類的代理對象了,可以非常明顯的看到代理對象是CGLib類型的
我們此時依然在B對象中注入A對象的過程中,目前準備獲取對象A或者A類的代理對象,上面方法是在執(zhí)行singletonFactory.getObject()
方法,也就是那個匿名內(nèi)部類中的方法,得到的結(jié)果singletonObject
是一個A類的CGLib代理對象,此時將代理對象裝在二級緩存earlySingletonObjects
中
此時就可以完成B類中的對象A注入了
3.5、完成對象B注入對象A
上面已經(jīng)獲取到對象A的代理對象了,我們一部分返回到注入對象A的地方:
- org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
- org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String)
- org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
- org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String, java.lang.Class)
- org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#resolveBeanByName
- org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#autowireResource
- org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#getResource
- org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.ResourceElement#getResourceToInject
- org.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement#inject
繼續(xù)往上,回到對象B完成注入對象A之后,執(zhí)行初始化代碼的地方:
- org.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement#inject
- org.springframework.beans.factory.annotation.InjectionMetadata#inject
- org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessProperties
- org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
3.6、完成對象B的剩余流程,進而回到對象A注入對象B的地方
初始化流程就是執(zhí)行一些初始化方法的過程,由于對象B的初始化流程不是本節(jié)重點,所以不在講解,直接略過
- org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
- org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])
- org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
- org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, org.springframework.beans.factory.ObjectFactory<?>)
- org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean
- org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String, java.lang.Class)
- org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#resolveBeanByName
- org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#autowireResource
- org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#getResource
- org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.ResourceElement#getResourceToInject
- org.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement#inject
現(xiàn)在回到l對象A注入對象B的地方
然后一步步回到對象A的初始化方法代碼處:
- org.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement#inject
- org.springframework.beans.factory.annotation.InjectionMetadata#inject
- org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessProperties
- org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
3.7、查看對象A不會再次執(zhí)行代理代碼的地方
從上圖可見,我們進入initializeBean
方法,主要看方法體中基本快結(jié)尾的地方,這個里面隱藏著代理類的生成邏輯:
按著Ctrl點擊進去:
我們一次又一次的debug進去,終于發(fā)現(xiàn)了生成代理對象的地方,大家熟悉earlyProxyReferences
嗎?
在B對象中注入對象A時,已經(jīng)將對象A的信息塞在earlyProxyReferences
集合中了,具體作用是說明對象A的代理對象已經(jīng)生成并放在二級緩存earlySingletonObjects
中了,所以if
判斷結(jié)果是false
,因此wrapIfNecessary(bean, beanName, cacheKey)
不會在執(zhí)行了,這樣可以避免該方法執(zhí)行2次,并且可以避免無法再bean工廠中生成一致的bean對象
如果沒有發(fā)生循環(huán)依賴,那么org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#getEarlyBeanReference
方法不會執(zhí)行,因此earlyProxyReferences
集合中就不存在鍵為a
的情況了,如果B類中沒有注入對象A就可以達到。但是A類和B類產(chǎn)生了循環(huán)依賴,所以earlyProxyReferences
中存儲鍵為a
的情況了
3.8、找到往一級緩存中添加對象A的代理對象的位置
對象A的實例化已經(jīng)完成,代碼繼續(xù)往下執(zhí)行,可以看到:
從二級緩存中獲得對象A的代理對象:
然后將結(jié)果一路返回:
- org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
- org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])
當匿名內(nèi)部類中的方法執(zhí)行完畢后,將回到singletonObject = singletonFactory.getObject();
執(zhí)行完成的狀態(tài)
往下繼續(xù)看方法addSingleton()
方法,目前singleonObject是一個:
往一級緩存中放入對象A的代理對象信息
3.9、往對象C中注入對象A
由于對象A已經(jīng)在一級緩存中了,所以對象C通過getSingleton
方法可以直接獲取對象A的代理對象
三、流程分析
在上述示例代碼中,對象A中注入對象B、對象C,而對象B和對象C中也會注入對象A,這就造成了循環(huán)依賴
-
Spring根據(jù)
@SpringBootApplication
注解中@ComponentScan
掃描到了A類(被@Component
注解修飾) -
Spring先去Bean容器中獲取對象A,但是發(fā)現(xiàn)對象A不在Spring容器中,那就來創(chuàng)建對象A;首先根據(jù)A類的構(gòu)造函數(shù)創(chuàng)建對象A,但是對象A不會被放在三級緩存中
-
將匿名內(nèi)部類放到第3級緩存
singletonFactories
中,在后面解決循環(huán)依賴時可以用到
-
在對象A中注入對象B
-
Spring就去Bean容器中獲取對象B,但是發(fā)現(xiàn)對象B不在Spring容器中,那就來創(chuàng)建對象B;和創(chuàng)建對象A一樣,也是根據(jù)A類的構(gòu)造函數(shù)創(chuàng)建對象A,然后準備往對象B中注入對象A
-
Spring就去Bean容器中獲取對象A,我們在上面將包含對象A信息的匿名內(nèi)部類放到了第3級緩存
singletonFactories
中,現(xiàn)在我們可以把它取出來,之后調(diào)用它的getObject()
方法,其實就是執(zhí)行匿名內(nèi)部類中的方法
-
我們來看下匿名內(nèi)部類中的方法,圖片下半部分是方法開始的地方,然后在往上看就可以;大家看下標號①,我們將生成代理對象后的原始對象存在
earlyProxyReferences
集合中,后續(xù)當對象A完成初始化方法后通過earlyProxyReferences
集合進行判斷,然后決定是否執(zhí)行生成代理對象的方法,避免其他對象中注入的代理對象和Bean工廠中存儲的代理對象不一致;大家再看下標號②,在A類中有一個test()
方法上添加了@Transactional
注解,當執(zhí)行wrapIfNecessary()
方法之后就會通過CGlib生成一個代理對象A;而原始對象A將被存儲在earlyProxyReferences
集合中
-
當代理對象A生成完成,相當于
singletonFactory.getObject()
執(zhí)行完畢,此時singletonObject
的值就是代理對象A,然后將代理對象A放到第2級緩存earlySingletonObjects
中,并刪除第3級緩存singletonFactories
中存儲的信息
-
上面通過
getSingleton()
方法獲取到了代理對象A,然后交給對象B做注入,最終完成對象B的創(chuàng)建工作,然后將創(chuàng)建完成的對象B返回給對象A做注入,當對象B被注入到對象A之后,那就完成了populateBean()
方法
-
然后初始化也是對象創(chuàng)建道路上的關(guān)鍵一環(huán),現(xiàn)在來執(zhí)行
initializeBean()
方法,生成代理對象的位置在該方法末尾的地方,即執(zhí)行applyBeanPostProcessorsAfterInitialization()
方法的地方,其中方法會執(zhí)行到這個地方;在解決循環(huán)依賴(對象A和B相互依賴)的時候,我們把原始對象A已經(jīng)放到了earlyProxyReferences
集合中,所以此時可以從循環(huán)依賴中取到原始對象A,所以if判斷的結(jié)果是false
,因此就不會執(zhí)行生成代理對象A的過程了,大家是否還記得,我們之前已經(jīng)把代理對象A放到了第2級緩存earlySingletonObjects
中了,我們在后續(xù)步驟中會從二級緩存中去除代理對象A,然后放到一級緩存中,往下慢慢看
-
此時
exposedObject
依然是原始對象A,然后往下執(zhí)行getSingleton()
方法來獲取代理對象A
-
在解決循環(huán)依賴的時候,我們將代理對象放到了2級緩存
earlySingletonObjects
集合中,現(xiàn)在我們?nèi)〕龃韺ο筚x值給earlySingletonReference
-
然后
doCreateBean()
方法將代理對象A返回,然后createBean()
方法在將代理對象A返回,這也標志著紅框中匿名內(nèi)部類中方法的執(zhí)行結(jié)束,而匿名內(nèi)部類方法的調(diào)用位置在getSingleton()
方法中,也就是getObject()
方法,此時singletonObject
的值就是代理對象A
-
最后調(diào)用
addSingleton()
方法將代理對象A放入1級緩存中
-
Spring根據(jù)
@SpringBootApplication
注解中@ComponentScan
掃描到了C類(被@Component
注解修飾) -
C類中注入了對象A,所以需要獲取對象A,此時對象A已經(jīng)放到了1級緩存中,所以可以直接取到代理對象A,從而在對象C中注入代理對象A
四、參考文章
- 一文告訴你Spring是如何利用"三級緩存"巧妙解決Bean的循環(huán)依賴問題的【享學(xué)Spring】