国产亚洲精品福利在线无卡一,国产精久久一区二区三区,亚洲精品无码国模,精品久久久久久无码专区不卡

當前位置: 首頁 > news >正文

wordpress自動登錄ftp家庭優(yōu)化大師免費下載

wordpress自動登錄ftp,家庭優(yōu)化大師免費下載,wordpress 文章發(fā)布到指定頁面,如何做優(yōu)惠券網(wǎng)站目錄 一、概念1、三級緩存的作用2、循環(huán)依賴的含義 二、代碼1、代碼下載2、文件功能介紹3、源碼分析3.1、找到獲取A對象的位置,打斷點進行debug操作3.2、一步步找到在A對象中注入B對象的位置3.3、一步步找到B對象注入A對象的位置3.4、往下找到通過三級緩存解決循環(huán)依…

目錄

  • 一、概念
    • 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注解,所以lazyLookupfalse,因此走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ù)allowEarlyReferencetrue,因此方法會繼續(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】
http://aloenet.com.cn/news/28193.html

相關(guān)文章:

  • Asp.net 手機網(wǎng)站制作優(yōu)化快速排序
  • 手機版網(wǎng)站制作應(yīng)用淘寶如何刷關(guān)鍵詞增加權(quán)重
  • 深深圳市建設(shè)局網(wǎng)站百度競價培訓(xùn)
  • 投資手機網(wǎng)站源碼廣告聯(lián)盟平臺排名
  • 順德企業(yè)手機網(wǎng)站建設(shè)怎么讓百度搜出自己
  • wordpress首頁不加載圖片小紅書seo排名優(yōu)化
  • 重點建設(shè)政協(xié)網(wǎng)站百度推廣公司
  • 轉(zhuǎn)播網(wǎng)站如何做鄭州seo顧問熱狗
  • 佛山做外貿(mào)網(wǎng)站哪家好商旅平臺app下載
  • 建行的官方網(wǎng)站武漢seo首頁優(yōu)化報價
  • aws搭建wordpress優(yōu)化網(wǎng)站建設(shè)seo
  • 北京模板開發(fā)建站網(wǎng)站如何做關(guān)鍵詞優(yōu)化
  • 網(wǎng)站建設(shè)包括哪些技術(shù)營業(yè)推廣策劃方案
  • 二手交易平臺的網(wǎng)站怎么做營銷平臺
  • 廣州白云做網(wǎng)站的公司網(wǎng)絡(luò)銷售怎么聊客戶
  • 邢臺哪兒做wap網(wǎng)站好建站軟件
  • 網(wǎng)站首頁只顯示域名seo關(guān)鍵詞優(yōu)化提高網(wǎng)站排名
  • 天津西青區(qū)疫情最新消息今天手機網(wǎng)站優(yōu)化排名
  • 簡述網(wǎng)站開發(fā)的幾個步驟搜索熱度和搜索人氣
  • 有做車身拉花的網(wǎng)站嗎seo學(xué)校
  • 做網(wǎng)站買什么筆記本好外鏈發(fā)布工具下載
  • 咖啡網(wǎng)站開發(fā)背景怎么寫微博指數(shù)查詢
  • 自己電腦做服務(wù)器發(fā)布網(wǎng)站電商營銷推廣有哪些?
  • 我自己做的網(wǎng)站打開很慢鄭州seo哪家好
  • 網(wǎng)站備案 視頻百度引流免費推廣怎么做
  • 設(shè)計一個網(wǎng)站西安發(fā)布最新通知
  • 網(wǎng)站開發(fā)發(fā)展前景seo推廣和百度推廣的區(qū)別
  • wordpress模板怎么添加菜單百度seo排名優(yōu)化教程
  • 如何做新政府網(wǎng)站欄目網(wǎng)站建設(shè)制作流程
  • 湖南省建設(shè)安監(jiān)局官網(wǎng)站朋友圈推廣文案