国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【Spring框架】一篇文章帶你徹底搞懂Spring解決循環(huán)依賴的底層原理

這篇具有很好參考價(jià)值的文章主要介紹了【Spring框架】一篇文章帶你徹底搞懂Spring解決循環(huán)依賴的底層原理。希望對大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

目錄

一、前言

二、什么是循環(huán)依賴

三、Spring Bean 的循環(huán)依賴問題

3.1 Bean 的創(chuàng)建步驟

3.2 為什么 Spring Bean 會產(chǎn)生循環(huán)依賴問題?

3.3 什么情況下循環(huán)依賴可以被處理?

四、Spring 如何解決循環(huán)依賴問題?

4.0 什么是三級緩存

4.1 簡單的循環(huán)依賴(沒有AOP)

4.1.0 創(chuàng)建Bean的前期流程源碼分析

4.1.1 創(chuàng)建A:調(diào)用doGetBean()

4.1.1.1 調(diào)用getSingleton(beanName):調(diào)用的第一個(gè)名為getSingleton的方法,嘗試從緩存中獲取Bean

4.1.1.2 調(diào)用getSingleton(beanName, singletonFactory):調(diào)用的第二個(gè)名為getSingleton的方法,去創(chuàng)建Bean

4.1.1.2.1 createBean()方法:創(chuàng)建Bean

4.1.1.2.1.1 調(diào)用addSingletonFactory方法:這個(gè)方法就是解決循環(huán)依賴問題的關(guān)鍵

4.1.2 創(chuàng)建B

4.1.2.1 調(diào)用第一個(gè)getSingleton()方法實(shí)現(xiàn)對B注入A

4.1.2.1.1 調(diào)用getEarlyBeanReference()方法:用來從singletonFactory工廠類中返回Bean

4.1.3 創(chuàng)建A和B的流程總結(jié)

4.2 結(jié)合了AOP的循環(huán)依賴

1、在給B注入的時(shí)候?yàn)槭裁匆⑷胍粋€(gè)代理對象?

2、明明在創(chuàng)建A的時(shí)候,到初始化這一步完成的時(shí)候仍然還是原始的A對象,那么如果對A有AOP增強(qiáng)的話,為什么最終在Spring容器中取出的A是代理增強(qiáng)的對象呢?Spring是在什么時(shí)候?qū)⒋韺ο蠓湃氲饺萜髦械哪兀?/p>

3、初始化的時(shí)候是對A對象本身進(jìn)行初始化(初始化之前也都是對原始A對象進(jìn)行的處理),而添加到Spring容器中以及注入到B中的都是代理對象,這樣不會有問題嗎?

4、三級緩存為什么要使用工廠而不是直接使用引用?換而言之,為什么需要這個(gè)三級緩存,直接通過二級緩存暴露一個(gè)引用不行嗎?

4.3 Spring 解決 Bean 的循環(huán)依賴的流程總結(jié)

4.4 三級緩存真的提高了效率了嗎?

五、循環(huán)依賴問題的解決方案

5.1 重新設(shè)計(jì)

5.2 使用 Setter/Field 注入

5.3 使用@Lazy注解

5.4 使用 @PostConstruct注解

5.5 實(shí)現(xiàn)ApplicationContextAware 和 InitializingBean接口

5.6 思考題:為什么在下表中的第三種情況的循環(huán)依賴能被解決,而第四種情況不能被解決呢?

第三種情況

第四種情況

六、什么樣的循環(huán)依賴無法處理?

七、面試題

7.1 Spring是如何解決的循環(huán)依賴?

7.2 為什么不直接使用一級緩存來解決循環(huán)依賴

7.3 為什么要使用三級緩存呢?直接使用一級緩存和二級緩存能解決循環(huán)依賴嗎?


一、前言

Spring中的循環(huán)依賴一直是Spring中一個(gè)很重要的話題,一方面是因?yàn)樵创a中為了解決循環(huán)依賴做了很多處理,另外一方面是因?yàn)槊嬖嚨臅r(shí)候,如果問到Spring中比較高階的問題,那么循環(huán)依賴必定逃不掉。如果你回答得好,那么這就是你的必殺技,反正,那就是面試官的必殺技,這也是取這個(gè)標(biāo)題的原因,當(dāng)然,本文的目的是為了讓你在之后的所有面試中能多一個(gè)必殺技,專門用來絕殺面試官!

本文的核心思想就是,當(dāng)面試官問:

“請講一講Spring中的循環(huán)依賴?!?/strong>的時(shí)候,我們到底該怎么回答?

主要分下面幾點(diǎn):

  1. 什么是循環(huán)依賴?
  2. 什么情況下循環(huán)依賴可以被處理?
  3. Spring是如何解決的循環(huán)依賴?

同時(shí)本文希望糾正幾個(gè)目前業(yè)界內(nèi)經(jīng)常出現(xiàn)的幾個(gè)關(guān)于循環(huán)依賴的錯(cuò)誤的說法:

  • 只有在setter方式注入的情況下,循環(huán)依賴才能解決(錯(cuò))
  • 使用第三級緩存的目的是為了提高效率(錯(cuò))

二、什么是循環(huán)依賴

通俗來講,循環(huán)依賴指的是一個(gè)實(shí)例或多個(gè)實(shí)例存在相互依賴的關(guān)系(類之間循環(huán)嵌套引用)。

舉個(gè)例子

@Component

public class AService {

? ? // A中注入了B

? ? @Autowired

? ? private BService bService;

}

?

@Component

public class BService {

? ? // B中也注入了A

? ? @Autowired

? ? private AService aService;

}

?

上述例子中?AService?依賴了?BServiceBService?也依賴了?AService,這就是兩個(gè)對象之間的相互依賴。

為什么重新打一次包就不會循環(huán)依賴,Java,后端框架,# Spring,Spring,循環(huán)依賴,IoC,面試,三級緩存

當(dāng)然循環(huán)依賴還包括?自身依賴、多個(gè)實(shí)例之間相互依賴(A依賴于B,B依賴于C,C又依賴于A)。

// 自己依賴自己

@Component

public class A {

? ? // A中注入了A

? ? @Autowired

? ? private A a;

}

?

?

如果我們在普通Java環(huán)境下正常運(yùn)行上面的代碼調(diào)用?AService?對象并不會出現(xiàn)問題,也就是說普通對象就算出現(xiàn)循環(huán)依賴也不會存在問題,因?yàn)閷ο笾g存在依賴關(guān)系是很常見的,那么為什么被 Spring 容器管理后的對象有循環(huán)依賴的情況會出現(xiàn)問題呢?

三、Spring Bean 的循環(huán)依賴問題

被 Spring 容器管理的對象叫做 Bean,為什么 Bean 會存在循環(huán)依賴問題呢?

想要了解 Bean 的循環(huán)依賴問題,首先需要了解 Bean 是如何創(chuàng)建的(需要了解Bean的生命周期)。

3.1 Bean 的創(chuàng)建步驟

為了能更好的展示出現(xiàn)循環(huán)依賴問題的環(huán)節(jié),所以這里的 Bean 創(chuàng)建步驟做了簡化:

  1. 在創(chuàng)建 Bean 之前,Spring 會通過掃描獲取 BeanDefinition。
  2. BeanDefinition就緒后會讀取 BeanDefinition 中所對應(yīng)的 class 來加載類。
  3. 實(shí)例化階段:根據(jù)構(gòu)造函數(shù)來完成實(shí)例化 (未屬性注入以及初始化的對象 這里簡稱為 原始對象
  4. 屬性注入階段:對 Bean 的屬性進(jìn)行依賴注入 (這里就是發(fā)生循環(huán)依賴問題的環(huán)節(jié)
  5. 如果 Bean 的某個(gè)方法有AOP操作,則需要根據(jù)原始對象生成代理對象。
  6. 最后把代理對象放入單例池(一級緩存singletonObjects)中。

兩點(diǎn)說明:

  • 上面的 Bean 創(chuàng)建步驟是對于 單例(singleton) 作用域的 Bean。
  • Spring 的 AOP 代理就是作為 BeanPostProcessor 實(shí)現(xiàn)的,而 BeanPostProcessor 是發(fā)生在屬性注入階段后的,所以 AOP 是在 屬性注入 后執(zhí)行的。

3.2 為什么 Spring Bean 會產(chǎn)生循環(huán)依賴問題?

通過上面的 Bean 創(chuàng)建步驟可知:實(shí)例化 Bean 后會進(jìn)行 屬性注入(依賴注入)。

如最初舉例的 AService 和 BService 的依賴關(guān)系,當(dāng) AService 創(chuàng)建時(shí),會先對 AService 進(jìn)行實(shí)例化生成一個(gè)原始對象,然后在進(jìn)行屬性注入時(shí)發(fā)現(xiàn)了需要 BService 對應(yīng)的 Bean,此時(shí)就會去為 BService 進(jìn)行創(chuàng)建,在 BService 實(shí)例化后生成一個(gè)原始對象后進(jìn)行屬性注入,此時(shí)會發(fā)現(xiàn)也需要 AService 對應(yīng)的 Bean。

為什么重新打一次包就不會循環(huán)依賴,Java,后端框架,# Spring,Spring,循環(huán)依賴,IoC,面試,三級緩存

這樣就會造成?AService?和?BService?的 Bean 都無法創(chuàng)建,就會產(chǎn)生?循環(huán)依賴?問題。

而這種情況只會在將Bean交給Spring管理的時(shí)候才會出現(xiàn),因?yàn)樯厦娴倪@些屬性注入操作都是Spring去做的,如果只是我們自己在Java中創(chuàng)建對象可以不去注入屬性,讓成員屬性為NULL也可以正常執(zhí)行的,這樣也就不會出現(xiàn)循環(huán)依賴的問題了。

3.3 什么情況下循環(huán)依賴可以被處理?

Spring 并不能解決所有 Bean 的循環(huán)依賴問題,接下來通過例子來看看哪些場景下的循環(huán)依賴問題是可以被解決的。

Spring中循環(huán)依賴場景有:?

(1)構(gòu)造器的循環(huán)依賴?

(2)field屬性的循環(huán)依賴。

在回答什么情況下循環(huán)依賴問題可以被解決前,首先要明確一點(diǎn),Spring解決循環(huán)依賴是有前置條件的:

  • 出現(xiàn)循環(huán)依賴的Bean必須要是單例
  • 依賴注入的方式不能全是構(gòu)造器注入的方式(很多博客上說,只能解決setter方法的循環(huán)依賴,這是錯(cuò)誤的)

其中第一點(diǎn)應(yīng)該很好理解,如果原型的Bean出現(xiàn)循環(huán)依賴,Spring會直接報(bào)錯(cuò),Spring 無法解決?原型作用域?出現(xiàn)的循環(huán)依賴問題。因?yàn)?Spring 不會緩存?原型?作用域的 Bean,而 Spring 依靠?緩存?來解決循環(huán)依賴問題,所以 Spring 無法解決?原型?作用域的 Bean。Spring Bean默認(rèn)都是單例的。

第二點(diǎn):不能全是構(gòu)造器注入是什么意思呢?我們還是用代碼說話:

@Component

public class A {

? ? public A(B b) {

? ? }

}

@Component

public class B {

? ? public B(A a){

? ? }

}

在上面的例子中,A中注入B的方式是通過構(gòu)造器,B中注入A的方式也是通過構(gòu)造器,這個(gè)時(shí)候循環(huán)依賴是無法被解決,如果你的項(xiàng)目中有兩個(gè)這樣相互依賴的Bean,在啟動時(shí)就會報(bào)出以下錯(cuò)誤:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?

?

以上報(bào)錯(cuò)說明 Spring 無法解決?構(gòu)造器注入?出現(xiàn)的循環(huán)依賴問題。因?yàn)?構(gòu)造器注入?發(fā)生在?實(shí)例化階段,而 Spring 解決循環(huán)依賴問題依靠的?三級緩存?在?屬性注入階段,也就是說調(diào)用構(gòu)造函數(shù)時(shí)還未能放入三級緩存中,所以無法解決?構(gòu)造器注入?的循環(huán)依賴問題。

?

?

為了測試循環(huán)依賴的解決情況跟注入方式的關(guān)系,我們做如下四種情況的測試

依賴情況

依賴注入方式

循環(huán)依賴是否被解決

AB相互依賴(循環(huán)依賴)

均采用setter方法注入

AB相互依賴(循環(huán)依賴)

均采用構(gòu)造器注入

AB相互依賴(循環(huán)依賴)

A中注入B的方式為setter方法,B中注入A的方式為構(gòu)造器

AB相互依賴(循環(huán)依賴)

B中注入A的方式為setter方法,A中注入B的方式為構(gòu)造器

具體的測試代碼跟簡單,我就不放了。從上面的測試結(jié)果我們可以看到,不是只有在setter方法注入的情況下循環(huán)依賴才能被解決(setter注入可以利用三級緩存解決循環(huán)依賴問題),即使存在構(gòu)造器注入的場景下,循環(huán)依賴依然被可以被正常處理掉。

那么到底是為什么呢?Spring到底是怎么處理的循環(huán)依賴呢?不要急,我們接著往下看。

四、Spring 如何解決循環(huán)依賴問題?

Spring的循環(huán)依賴的理論依據(jù)其實(shí)是基于Java的引用傳遞,當(dāng)我們獲取到對象的引用時(shí),對象的field或則屬性是可以延后設(shè)置的(但是構(gòu)造器必須是在獲取引用之前)。

Spring 是靠?三級緩存?來解決循環(huán)依賴問題的,接下來了解一下?什么是三級緩存?以及?解決循環(huán)依賴問題的具體流程

4.0 什么是三級緩存

那么Spring如何解決的循環(huán)依賴問題呢,對于單例來說,在Spring容器整個(gè)生命周期內(nèi),有且只有一個(gè)對象,所以很容易想到這個(gè)對象應(yīng)該存在Cache中,Spring為了解決單例的循環(huán)依賴問題,使用了三級緩存。

這三個(gè)緩存都是定義在DefaultSingletonBeanRegistry類中的:

/** Cache of singleton objects: bean name --> bean instance */

// 單例對象的cache:一級緩存

private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(64);

?

/** Cache of early singleton objects: bean name --> bean instance */

// 提前暴光的單例對象的Cache:二級緩存

private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

?

/** Cache of singleton factories: bean name --> ObjectFactory */

// 單例對象工廠的cache:三級緩存

private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

?

循環(huán)依賴主要的三級緩存分別是 :

  1. singletonObbjects:一級緩存單例池,主要存放最終形態(tài)的單例bean;我們一般獲取一個(gè)bean都是從這個(gè)緩存中獲?。恍枰f明的是并不是所有單例bean都存在這個(gè)緩存當(dāng)中,有些特殊的單例bean不存在這個(gè)緩存當(dāng)中
  1. earlySingletonObjects:二級緩存,主要存放的是過渡bean(原始對象/原始對象的代理對象);也就是從三級緩存當(dāng)中產(chǎn)生出來的對象;它的作用是防止在多級循環(huán)依賴的情況下重復(fù)從三級緩存當(dāng)中創(chuàng)建對象,其他對象都可以直接從二級緩存中獲取到原始對象(的代理對象);因?yàn)槿壘彺娈?dāng)中創(chuàng)建對象是需要犧牲一定得性能;有了這個(gè)緩存可以一定程度上提高效率(但是提高的效率并不明顯)。只有在調(diào)用了三級緩存中的ObjectFactorygetObject() 方法獲取原始對象(的代理對象)時(shí),才會將原始對象(的代理對象)放入二級緩存,而調(diào)用三級緩存中的ObjectFactorygetObject() 方法獲取原始對象(的代理對象)這種情況只會發(fā)生在有循環(huán)依賴的時(shí)候,所以,二級緩存在沒有循環(huán)依賴的情況下不會被使用到。二級緩存是為了提前暴露 Bean 來解決循環(huán)依賴問題,此時(shí)的 Bean 可能還沒有進(jìn)行屬性注入,只有等完成了屬性注入、初始化后的 Bean 才會上移到一級緩存(單例池)中。
  1. singletonFactories 三級緩存,用于存放原始對象對應(yīng)的ObjectFactory,它的作用主要是為了產(chǎn)生一個(gè)對象;每生成一個(gè)原始對象,都會將這個(gè)原始對象對應(yīng)的ObjectFactory放到三級緩存中,通過調(diào)用ObjectFactory的getObject() 方法,就能夠在需要?jiǎng)討B(tài)代理的情況下為原始對象生成代理對象并返回,否則返回原始對象,以此來處理循環(huán)依賴時(shí)還需要?jiǎng)討B(tài)代理的情況。

因?yàn)檫@個(gè)緩存當(dāng)中存到的是一個(gè)工廠;可以產(chǎn)生特定對象;程序員可以去擴(kuò)展BeanPostProcessor來定制這個(gè)工廠產(chǎn)生對象的過程;比如AOP就是擴(kuò)展了這個(gè)工廠的產(chǎn)生過程;從而完成完整的aop功能;如果沒有這個(gè)緩存那么極端情況下會出現(xiàn)循環(huán)依賴注入的bean不是一個(gè)完整的bean,或者說是一個(gè)錯(cuò)誤的bean。

為什么會存在三級緩存,主要原因就是:延遲代理對象的創(chuàng)建。設(shè)想一下,如果在實(shí)例化出一個(gè)原始對象的時(shí)候,就直接將這個(gè)原始對象的代理對象創(chuàng)建出來(如果需要?jiǎng)?chuàng)建的話),然后就放在二級緩存中,似乎感覺三級緩存就沒有存在的必要了對吧,但是請打住,這里存在的問題就是,如果真這么做了,那么每一個(gè)對象在實(shí)例化出原始對象后,就都會去創(chuàng)建代理對象,Spring的原始設(shè)計(jì)中,代理對象的創(chuàng)建應(yīng)該是由AnnotationAwareAspectJAutoProxyCreator這個(gè)后置處理器的postProcessAfterInitialization() 來完成,也就是:在對象初始化完畢后,再去創(chuàng)建代理對象。如果真的只用兩個(gè)緩存來解決循環(huán)依賴,那么就會打破SpringAOP的一個(gè)設(shè)計(jì)思想。

下面我們就通過分析源碼,來講解Spring是如何解決循環(huán)依賴為題的。關(guān)于循環(huán)依賴的解決方式應(yīng)該要分兩種情況來討論:

  1. 簡單的循環(huán)依賴(沒有AOP)
  2. 結(jié)合了AOP的循環(huán)依賴

4.1 簡單的循環(huán)依賴(沒有AOP)

我們先來分析一個(gè)最簡單的例子,就是上面提到的那個(gè)demo

@Component

public class A {

? ? // A中注入了B

? ? @Autowired

? ? private B b;

}

?

@Component

public class B {

? ? // B中也注入了A

? ? @Autowired

? ? private A a;

}

通過上文我們已經(jīng)知道了這種情況下的循環(huán)依賴是能夠被解決的,那么具體的流程是什么呢?我們一步步分析。

首先,我們要知道Spring在創(chuàng)建Bean的時(shí)候默認(rèn)是按照自然排序來進(jìn)行創(chuàng)建的(按照Bean名稱字典序,比如A類就要早于B類被創(chuàng)建),所以第一步Spring會去創(chuàng)建A。

與此同時(shí),我們應(yīng)該知道,Spring在創(chuàng)建Bean的過程中分為三步:

  1. 實(shí)例化,其實(shí)也就是調(diào)用對象的構(gòu)造方法實(shí)例化對象。對應(yīng)方法:AbstractAutowireCapableBeanFactory中的createBeanInstance方法
  2. 屬性注入,這一步主要是對bean的依賴屬性進(jìn)行填充。對應(yīng)方法:AbstractAutowireCapableBeanFactory的populateBean方法
  3. 初始化,在屬性注入之后,Spring會調(diào)用設(shè)置的init()方法來進(jìn)行初始化,對Bean進(jìn)一步進(jìn)行處理完善。對應(yīng)方法:AbstractAutowireCapableBeanFactory的initializeBean方法

為什么重新打一次包就不會循環(huán)依賴,Java,后端框架,# Spring,Spring,循環(huán)依賴,IoC,面試,三級緩存

這些方法在之前源碼分析的文章中都做過詳細(xì)的解讀了,如果你之前沒看過我的文章,那么你只需要知道:

  1. 實(shí)例化,簡單理解就是new了一個(gè)對象
  2. 屬性注入,為實(shí)例化中new出來的對象填充屬性
  3. 初始化,執(zhí)行aware接口中的方法,初始化方法,完成AOP代理

其實(shí)我們簡單的思考一下就發(fā)現(xiàn),出現(xiàn)循環(huán)依賴的問題主要在 (1)和 (2)兩個(gè)步驟上,也就是也就是(1)實(shí)例化階段會造成構(gòu)造器循環(huán)依賴和(2)屬性注入階段會造成field循環(huán)依賴。

4.1.0 創(chuàng)建Bean的前期流程源碼分析

我們先講一下創(chuàng)建Bean的前期流程,這些內(nèi)容其實(shí)在之前IOC源碼筆記中已經(jīng)講過了,這里我們就簡要復(fù)習(xí)一下。

Spring中,如果基于XML配置bean,那么使用的容器為ClassPathXmlApplicationContext,如果是基于注解配置bean,則使用的容器為AnnotationConfigApplicationContext。以AnnotationConfigApplicationContext為例,其構(gòu)造函數(shù)如下所示:

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {

? ? this();

? ? register(componentClasses);

? ? // 初始化容器

? ? refresh();

}

?

AnnotationConfigApplicationContext的構(gòu)造函數(shù)中會調(diào)用到AbstractApplicationContextrefresh() 方法,實(shí)際上無論是基于XML配置bean,還是基于注解配置bean,亦或者是Springboot中,在初始化容器時(shí)都會調(diào)用到AbstractApplicationContextrefresh() 方法中。下面看一下refresh() 方法:

public void refresh() throws BeansException, IllegalStateException {

? ? synchronized (this.startupShutdownMonitor) {

? ? ? ? StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

? ? ? ? // ......

? ? ? ? try {

? ? ? ? ? ?

? ? ? ? ? ? // ......

? ? ? ? ? ? // 初始化所有非延時(shí)加載的單例bean

? ? ? ? ? ? finishBeanFactoryInitialization(beanFactory);

? ? ? ? ? ? // ......

? ? ? ? }

? ? ? ? catch (BeansException ex) {

? ? ? ? ? ?

? ? ? ? ? ? // ......

? ? ? ? ? ? throw ex;

? ? ? ? }

? ? ? ? finally {

? ? ? ? ? ? resetCommonCaches();

? ? ? ? ? ? contextRefresh.end();

? ? ? ? }

? ? }

}

重點(diǎn)關(guān)心refresh()?方法中調(diào)用的finishBeanFactoryInitialization()?方法,該方法會初始化所有非延時(shí)加載的單例bean(也就是提前將非延遲加載的單例Bean裝配到Spring容器中),其實(shí)現(xiàn)如下:

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {

? ? if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&

? ? ? ? ? ? beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {

? ? ? ? beanFactory.setConversionService(

? ? ? ? ? ? ? ? beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));

? ? }

? ?

? ? if (!beanFactory.hasEmbeddedValueResolver()) {

? ? ? ? beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));

? ? }

? ? String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);

? ? for (String weaverAwareName : weaverAwareNames) {

? ? ? ? getBean(weaverAwareName);

? ? }

? ? beanFactory.setTempClassLoader(null);

? ? beanFactory.freezeConfiguration();

? ? // 初始化所有非延時(shí)加載的單例bean

? ? beanFactory.preInstantiateSingletons();

}

finishBeanFactoryInitialization()?方法中會調(diào)用到DefaultListableBeanFactorypreInstantiateSingletons()?方法,如下所示:

public void preInstantiateSingletons() throws BeansException {

? ? // ......

? ? List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

? ? // 在這個(gè)循環(huán)中通過getBean()方法初始化bean

? ? for (String beanName : beanNames) {

? ? ? ? RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);

? ? ? ? if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {

? ? ? ? ? ? // 判斷是否是FactoryBean

? ? ? ? ? ? if (isFactoryBean(beanName)) {

? ? ? ? ? ? ? ? // ......

? ? ? ? ? ? }

? ? ? ? ? ? else {

? ? ? ? ? ? ? ? // 不是FactoryBean,則通過getBean()方法來初始化bean

? ? ? ? ? ? ? ? getBean(beanName);

? ? ? ? ? ? }

? ? ? ? }

? ? }

? ? // ......

}

到了這里我們就了解到Spring中初始化bean,是通過調(diào)用容器的getBean()?方法來完成,在getBean()?方法中如果獲取不到bean,此時(shí)就會初始化這個(gè)bean。所以開始創(chuàng)建Bean的流程是從AbstractBeanFactorygetBean()?方法開始的,這也是我們講解循環(huán)依賴的重點(diǎn)。AbstractBeanFactory的getBean()?方法的實(shí)現(xiàn)如下:

public Object getBean(String name) throws BeansException {

? ? // 有三種情況會調(diào)用到這里

? ? // 1. 容器啟動的時(shí)候初始化A,所以調(diào)用到這里以進(jìn)行A的初始化

? ? // 2. 初始化A的時(shí)候要屬性注入B,所以調(diào)用到這里以進(jìn)行B的初始化

? ? // 3. 初始化B的時(shí)候要屬性注入A,所以調(diào)用到這里以獲取Abean

? ? return doGetBean(name, null, null, false); // 最終會調(diào)用doGetBean()

}

基于上面的知識,我們開始解讀整個(gè)循環(huán)依賴處理的過程,整個(gè)流程應(yīng)該是以A的創(chuàng)建為起點(diǎn),前文也說了,第一步就是創(chuàng)建A嘛!

為什么重新打一次包就不會循環(huán)依賴,Java,后端框架,# Spring,Spring,循環(huán)依賴,IoC,面試,三級緩存

創(chuàng)建A的過程實(shí)際上就是調(diào)用getBean方法,這個(gè)方法有兩層含義:

  1. 創(chuàng)建一個(gè)新的Bean
  2. 從緩存中獲取到已經(jīng)被創(chuàng)建的對象

我們現(xiàn)在分析的是第一層含義,因?yàn)檫@個(gè)時(shí)候緩存中還沒有A嘛!

下面我們來從源碼的層面,梳理 Spring 解決 Bean 的循環(huán)依賴的整個(gè)流程。

4.1.1 創(chuàng)建A調(diào)用doGetBean()

上面已經(jīng)分析過了,創(chuàng)建A的流程從 AbstractApplicationContext 的 refresh() 方法出發(fā),進(jìn)入 finishBeanFactoryInitialization() 方法再進(jìn)入 preInstantiateSingletons() 方法再進(jìn)入 getBean() 方法再進(jìn)入 doGetBean() 方法。

下面我們來看看?doGetBean()?方法:

AbstractBeanFactory.java

@SuppressWarnings("unchecked")

? ? protected <T> T doGetBean(

? ? ? ? ? ? String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)

? ? ? ? ? ? throws BeansException {

? ? String beanName = transformedBeanName(name);

? ? Object bean;

? ? // Eagerly check singleton cache for manually registered singletons.

? ? // 1、第一個(gè)getSingleton()方法,判斷此時(shí)緩存中是否有想要獲取的Bean了,如果有了直接從緩存中獲取。如果沒有則在后面的第二個(gè)調(diào)用的getSingleton()方法中去創(chuàng)建該Bean

? ? Object sharedInstance = getSingleton(beanName);

? ? if (sharedInstance != null && args == null) {

? ? ? ? ...

? ? }

? ? else {

? ? ? ? // Fail if we're already creating this bean instance:

? ? ? ? // We're assumably within a circular reference.

?// 非單例bean是無法支持循環(huán)依賴的,所以這里判斷是否是非單例bean的循環(huán)依賴場景,如果是則拋出異常

? ? ? ? if (isPrototypeCurrentlyInCreation(beanName)) {

? ? ? ? ? ? throw new BeanCurrentlyInCreationException(beanName);

? ? ? ? }

? ? ? ? // Check if bean definition exists in this factory.

? ? ? ? BeanFactory parentBeanFactory = getParentBeanFactory();

? ? ? ? if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {

? ? ? ? ? ? ...

? ? ? ? }

? ? ? ? if (!typeCheckOnly) {

? ? ? ? ? ? markBeanAsCreated(beanName);

? ? ? ? }

? ? ? ? try {

? ? ? ? ? ? RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

? ? ? ? ? ? checkMergedBeanDefinition(mbd, beanName, args);

? ? ? ? ? ? // Guarantee initialization of beans that the current bean depends on.

? ? ? ? ? ? String[] dependsOn = mbd.getDependsOn();

? ? ? ? ? ? if (dependsOn != null) {

? ? ? ? ? ? ? ? ...

? ? ? ? ? ? }

? ? ? ? ? ? // Create bean instance.

? ? ? ? ? ? if (mbd.isSingleton()) {

? ? ? ? ? ? ? ? // 2、第二個(gè)getSingleton()方法,當(dāng)從三個(gè)緩存中都沒有找到這個(gè)Bean的話,就會執(zhí)行第二個(gè)getSingleton(),在getSingleton()方法中會去調(diào)用createBean() 創(chuàng)建一個(gè) Bean 對象出來。

? ? ? ? ? ? ? ? sharedInstance = getSingleton(beanName, () -> {

? ? ? ? ? ? ? ? ? ? try {

??? // 在上面的getSingleton()方法中會調(diào)用到createBean()方法

? ? ? ? ? ? ? ? ? ? ? ? return createBean(beanName, mbd, args);

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? catch (BeansException ex) {

? ? ? ? ? ? ? ? ? ? ? ? // Explicitly remove instance from singleton cache: It might have been put there

? ? ? ? ? ? ? ? ? ? ? ? // eagerly by the creation process, to allow for circular reference resolution.

? ? ? ? ? ? ? ? ? ? ? ? // Also remove any beans that received a temporary reference to the bean.

? ? ? ? ? ? ? ? ? ? ? ? destroySingleton(beanName);

? ? ? ? ? ? ? ? ? ? ? ? throw ex;

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? });

? ? ? ? ? ? ? ? bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);

? ? ? ? ? ? }

? ? .....

? ?

? ? return (T) bean;

}

其中的第一個(gè)?getSingleton(beanName)?是判斷?三個(gè)緩存?中是否有創(chuàng)建好的 Bean 對象,下面看它的源碼。

4.1.1.1 調(diào)用getSingleton(beanName):調(diào)用的第一個(gè)名為getSingleton的方法,嘗試從緩存中獲取Bean

首先調(diào)用getSingleton(a)方法,這個(gè)方法又會調(diào)用getSingleton(beanName, true),在上圖中我省略了這一步。

DefaultSingletonBeanRegistry.java

public Object getSingleton(String beanName) {

? ? return getSingleton(beanName, true);

}

DefaultSingletonBeanRegistry.java

@Nullable

// allowEarlyReference表示是否允許提前引用,如果為true,那么在Bean創(chuàng)建過程中,就可以通過第三級緩存獲取到該Bean,否則只能在Bean創(chuàng)建完成后才能獲取到該Bean。這個(gè)參數(shù)的作用在后面會講到。

protected Object getSingleton(String beanName, boolean allowEarlyReference) {

? ? // Quick check for existing instance without full singleton lock

? ? // 嘗試從一級緩存singletonObjects中獲取該Bean

? ? Object singletonObject = this.singletonObjects.get(beanName);

? ? // 如果一級緩存中沒有該Bean,并且該Bean正在被創(chuàng)建(只要這個(gè)beanNameSingletonCurrentlyInCreation集合中,就表明這個(gè)Bean正在被創(chuàng)建)

? ? if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {

? ? ? ? // 如果一級緩存中沒有該Bean,并且該Bean正在被創(chuàng)建,那么嘗試從二級緩存earlySingletonObjects中獲取該Bean

? ? ? ? singletonObject = this.earlySingletonObjects.get(beanName);

? ? ? ? // 如果二級緩存中沒有該Bean,并且當(dāng)前允許Bean可以被提前引用(不用等Bean完全創(chuàng)建完成),那么嘗試從三級緩存singletonFactories中獲取該Bean

? ? ? ? if (singletonObject == null && allowEarlyReference) {

? ? ? ? ? ? synchronized (this.singletonObjects) {

? ? ? ? ? ? ? ? // Consistent creation of early reference within full singleton lock

? ? ? ? ? ? ? ? singletonObject = this.singletonObjects.get(beanName);

? ? ? ? ? ? ? ? if (singletonObject == null) {

? ? ? ? ? ? ? ? ? ? singletonObject = this.earlySingletonObjects.get(beanName);

? ? ? ? ? ? ? ? ? ? if (singletonObject == null) {

? ? ? ? ? ? ? ? ? ? ? ? // 從三級緩存singletonFactories中獲取該BeanBeanFactory

? ? ? ? ? ? ? ? ? ? ? ? ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);

? ? ? ? ? ? ? ? ? ? ? ? if (singletonFactory != null) {

??????????????????????????????????????????????????? // 通過三級緩存中這個(gè)Bean的工廠類來獲取這個(gè)Bean的實(shí)例化對象(此時(shí)返回的只是完成實(shí)例化的Bean,并沒有完成屬性注入和初始化,所以并不是一個(gè)完整的Bean),具體的源碼細(xì)節(jié)會在后面創(chuàng)建B類的時(shí)候講解

? ? ? ? ? ? ? ? ? ? ? ? ? ? singletonObject = singletonFactory.getObject();

??????????????????????????? // 通過singletonFactory工廠類獲取到的bean添加到二級緩存中(如果這個(gè)Bean沒有AOP增強(qiáng),那么加入到二級緩存的就是這個(gè)Bean原始的實(shí)例化對象;如果這個(gè)BeanAOP增強(qiáng),那么工廠類返回的就是這個(gè)Bean的代理對象,就會將代理對象添加到二級緩存中)

? ? ? ? ? ? ? ? ? ? ? ? ? ? this.earlySingletonObjects.put(beanName, singletonObject);

??????????????????????????????????????????????????? // 將三級緩存中這個(gè)Bean的工廠類移除

? ? ? ? ? ? ? ? ? ? ? ? ? ? this.singletonFactories.remove(beanName);

? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? }

? ? return singletonObject;

}

getSingleton(beanName, true)這個(gè)方法實(shí)際上就是到緩存中嘗試去獲取Bean,整個(gè)緩存分為三級(這三個(gè)緩存其實(shí)就相當(dāng)于一個(gè)Map,key就是BeanName,value就是各自要存儲的對象):

  1. singletonObjects,一級緩存,存儲的是所有創(chuàng)建好了的單例Bean
  2. earlySingletonObjects,完成實(shí)例化,但是還未進(jìn)行屬性注入及初始化的對象
  3. singletonFactories,提前暴露的一個(gè)單例工廠,二級緩存中存儲的就是從這個(gè)工廠中獲取到的對象

通過上面的源碼可以看到這里分別去每一級的緩存中取數(shù)據(jù),依次從第一級開始取數(shù)據(jù),如果取得到則直接返回,取不到則往下一級查找,步驟如下:

  1. Spring首先從一級緩存singletonObjects中獲取。
  2. 如果獲取不到,并且對象正在創(chuàng)建中,就再從二級緩存earlySingletonObjects中獲取。
  3. 如果還是獲取不到且允許singletonFactories通過getObject()獲取,就從三級緩存singletonFactory.getObject()(三級緩存)獲取。
  4. 如果從三級緩存中獲取后,就將這個(gè)Bean放入earlySingletonObjects中,并將這個(gè)singletonFactory從singletonFactories中移除。其實(shí)也就是從三級緩存移動到了二級緩存。

通過源碼可以看到在第三級緩存中調(diào)用了 singletonFactories.get(beanName) 按照上文所說的會觸發(fā)執(zhí)行有 AOP 操作返回代理對象,沒有AOP操作就直接返回原始對象,并且在這里會判斷是否成功從三級緩存中取出了singletonFactory對象,如果成功獲取則通過singletonFactory獲取Bean,然后將這個(gè)半成品Bean添加到二級緩存中并刪除三級緩存中這個(gè)Bean的singletonFactory數(shù)據(jù)。

從三級緩存的分析來看,Spring解決循環(huán)依賴的訣竅就在于singletonFactories這個(gè)第三級cache。這個(gè)cache的類型是ObjectFactory,定義如下:

/**

?* 定義一個(gè)可以返回對象實(shí)例的工廠

?* @param <T>

?*/

public interface ObjectFactory<T> {

? ? T getObject() throws BeansException;

}

?

因?yàn)锳是第一次被創(chuàng)建,所以不管哪個(gè)緩存中必然都是沒有的,因此會進(jìn)入getSingleton的另外一個(gè)重載方法getSingleton(beanName, singletonFactory)。第二個(gè)?getSingleton()?其實(shí)就是去執(zhí)行傳入的singletonFactory的lambda表達(dá)式中的?createBean()?來創(chuàng)建一個(gè) Bean 對象出來。

4.1.1.2 調(diào)用getSingleton(beanName, singletonFactory):調(diào)用的第二個(gè)名為getSingleton的方法,去創(chuàng)建Bean

這個(gè)方法傳入的ObjectFactory<?>實(shí)際是一個(gè)Lambdas表達(dá)式,所以調(diào)用傳入?yún)?shù)singletonFactory的getObject()?方法,就會調(diào)用到createBean()?方法來創(chuàng)建Bean,創(chuàng)建好的Bean會加入到一級緩存中。其源碼如下:

DefaultSingletonBeanRegistry.java

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {

? ? Assert.notNull(beanName, "Bean name must not be null");

? ? synchronized (this.singletonObjects) {

? ? ? ? Object singletonObject = this.singletonObjects.get(beanName);

? ? ? ? if (singletonObject == null) {

? ? ? ? ? ? // ....

? ? ? ? ? ? // 省略異常處理及日志

? ? ? ? ? ? // ....

? ? ? ? ? ? // 在單例對象創(chuàng)建前先做一個(gè)標(biāo)記

? ? ? ? ? ? // beanName放入到singletonsCurrentlyInCreation這個(gè)集合中(這個(gè)集合相當(dāng)于一個(gè)Set集合)

? ? ? ? ? ? // 標(biāo)志著這個(gè)單例Bean正在創(chuàng)建

? ? ? ? ? ? // 如果同一個(gè)單例Bean多次被創(chuàng)建,這里會拋出異常

? ? ? ? ? ? // 加入到這個(gè)集合的BeanName就表示這個(gè)Bean正在被創(chuàng)建,這一個(gè)標(biāo)記很關(guān)鍵,后面還會用到

? ? ? ? ? ? beforeSingletonCreation(beanName);

? ? ? ? ? ? boolean newSingleton = false;

? ? ? ? ? ? boolean recordSuppressedExceptions = (this.suppressedExceptions == null);

? ? ? ? ? ? if (recordSuppressedExceptions) {

? ? ? ? ? ? ? ? this.suppressedExceptions = new LinkedHashSet<>();

? ? ? ? ? ? }

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? // 上游傳入的lambda(也就是一個(gè)工廠類singletonFactory)在這里會被執(zhí)行,調(diào)用singletonFactory.getObject()方法會觸發(fā)執(zhí)行在lambda表達(dá)式中調(diào)用的createBean方法,去創(chuàng)建一個(gè)Bean后返回

? ? ? ? ? ? ? ? // 此時(shí)這個(gè)Bean相當(dāng)于通過createBean()方法被實(shí)例化出來了,實(shí)例化對象賦值給了singletonObject

?? // 這里得到的singletonObject就是初始化后得到的bean(或者代理bean)

? ? ? ? ? ? ? ? singletonObject = singletonFactory.getObject();

? ? ? ? ? ? ? ? newSingleton = true;

? ? ? ? ? ? }

? ? ? ? ? ? // ...

? ? ? ? ? ? // 省略catch異常處理

? ? ? ? ? ? // ...

? ? ? ? ? ? finally {

? ? ? ? ? ? ? ? if (recordSuppressedExceptions) {

? ? ? ? ? ? ? ? ? ? this.suppressedExceptions = null;

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? // 創(chuàng)建完成后將對應(yīng)的beanNamesingletonsCurrentlyInCreation移除(表示這個(gè)Bean已經(jīng)創(chuàng)建完成了,不再是創(chuàng)建中的狀態(tài)了)

? ? ? ? ? ? ? ? afterSingletonCreation(beanName);

? ? ? ? ? ? }

? ? ? ? ? ? if (newSingleton) {

? ? ? ? ? ? ? ? // 創(chuàng)建好的Bean,會添加到一級緩存singletonObjects中,同時(shí)將這個(gè)Bean從二級緩存和三級緩存中移除。

? ? ? ? ? ? ? ? addSingleton(beanName, singletonObject);

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? // 返回創(chuàng)建好的Bean

? ? ? ? return singletonObject;

? ? }

}

?

// 將完全創(chuàng)建好的Bean添加到一級緩存中

protected void addSingleton(String beanName, Object singletonObject) {

? ? synchronized (this.singletonObjects) {

? ? ? ? // 將完全創(chuàng)建完成的Bean添加到一級緩存中

? ? ? ? this.singletonObjects.put(beanName, singletonObject);

? ? ? ? // Bean從三級緩存中移除

? ? ? ? this.singletonFactories.remove(beanName);

? ? ? ? // Bean從二級緩存中移除

? ? ? ? this.earlySingletonObjects.remove(beanName);

? ? ? ? // 將完成創(chuàng)建的Bean添加到已注冊的單例Bean集合中

? ? ? ? this.registeredSingletons.add(beanName);

? ? }

}

?

上面getSingleton()的代碼我們主要抓住一點(diǎn),通過createBean方法返回的創(chuàng)建完成的Bean最終通過addSingleton()方法添加到了一級緩存,也就是單例池中。

那么到這里我們可以得出一個(gè)結(jié)論:一級緩存中存儲的是已經(jīng)完全創(chuàng)建好了的單例Bean。

?

4.1.1.2.1 createBean()方法:創(chuàng)建Bean

我們再展開講一下創(chuàng)建Bean的過程,創(chuàng)建Bean本質(zhì)是通過執(zhí)行?createBean()?方法中的?doCreateBean()?方法來創(chuàng)建的。源碼如下:

AbstractAutowireCapableBeanFactory.java

@Override

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)

? ? ? ? throws BeanCreationException {

? ? if (logger.isTraceEnabled()) {

? ? ? ? logger.trace("Creating instance of bean '" + beanName + "'");

? ? }

// 拿到BeanDefinition(Bean定義)

? ? RootBeanDefinition mbdToUse = mbd;

?

? ? // Make sure bean class is actually resolved at this point, and

? ? // clone the bean definition in case of a dynamically resolved Class

? ? // which cannot be stored in the shared merged bean definition.

? ? Class<?> resolvedClass = resolveBeanClass(mbd, beanName);

? ? if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {

? ? ? ? mbdToUse = new RootBeanDefinition(mbd);

? ? ? ? mbdToUse.setBeanClass(resolvedClass);

? ? }

? ? // Prepare method overrides.

? ? try {

? ? ? ? mbdToUse.prepareMethodOverrides();

? ? }

? ? catch (BeanDefinitionValidationException ex) {

? ? ? ? throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),

? ? ? ? ? ? ? ? beanName, "Validation of method overrides failed", ex);

? ? }

? ? try {

? ? ? ? // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.

? ? ? ? Object bean = resolveBeforeInstantiation(beanName, mbdToUse);

? ? ? ? if (bean != null) {

? ? ? ? ? ? return bean;

? ? ? ? }

? ? }

? ? catch (Throwable ex) {

? ? ? ? throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,

? ? ? ? ? ? ? ? "BeanPostProcessor before instantiation of bean failed", ex);

? ? }

? ? try {

? ? ? ? // 通過調(diào)用doCreateBean()來創(chuàng)建完整的Bean

? ? ? ? Object beanInstance = doCreateBean(beanName, mbdToUse, args);

? ? ? ? if (logger.isTraceEnabled()) {

? ? ? ? ? ? logger.trace("Finished creating instance of bean '" + beanName + "'");

? ? ? ? }

? ? ? ? return beanInstance;

? ? }

? ? catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {

? ? ? ? // A previously detected exception with proper bean creation context already,

? ? ? ? // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.

? ? ? ? throw ex;

? ? }

? ? catch (Throwable ex) {

? ? ? ? throw new BeanCreationException(

? ? ? ? ? ? ? ? mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);

? ? }

}

AbstractAutowireCapableBeanFactory.java

/**

?* 實(shí)際創(chuàng)建指定的bean的方法。 這個(gè)方法完成了 1、Bean的實(shí)例化 ? 2、Bean的屬性注入 ? 3、Bean的初始化

?* 此時(shí),預(yù)創(chuàng)建處理已經(jīng)發(fā)生,

?* 例如 檢查{@code postProcessBeforeInstantiation}回調(diào)。

?* 區(qū)分默認(rèn)bean實(shí)例化、使用工廠方法和自動裝配構(gòu)造函數(shù)。

?*/

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {

? ? // Instantiate the bean.

? ? BeanWrapper instanceWrapper = null;

? ? .....

? ?

? ? if (instanceWrapper == null) {

? ? ? ? // 1、實(shí)例化Bean,生成的對象被稱為原始對象

? ? ? ? instanceWrapper = createBeanInstance(beanName, mbd, args);

? ? }

// 這里的bean就是A或者B的原始對象,此時(shí)沒有被屬性注入,也沒有執(zhí)行初始化邏輯

? ? Object bean = instanceWrapper.getWrappedInstance();

?

? ? .....

? ? // Eagerly cache singletons to be able to resolve circular references

? ? // even when triggered by lifecycle interfaces like BeanFactoryAware.

?????? // A是單例的,mbd.isSingleton()條件滿足

// allowCircularReferences:這個(gè)變量代表是否允許循環(huán)依賴,默認(rèn)是開啟的,條件也滿足

// isSingletonCurrentlyInCreation:正在在創(chuàng)建A,也滿足

// 所以earlySingletonExposure=true

? ? boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&

? ? ? ? ? ? isSingletonCurrentlyInCreation(beanName));

? ? if (earlySingletonExposure) {

? ? ? ? if (logger.isDebugEnabled()) {

? ? ? ? ? ? logger.debug("Eagerly caching bean '" + beanName +

? ? ? ? ? ? ? ? ? ? "' to allow for resolving potential circular references");

? ? ? ? }

? ? ? ? // 將創(chuàng)建的BeanFactory添加到三級緩存中。注意此時(shí)Bean并沒有徹底創(chuàng)建完成,此時(shí)只進(jìn)行了實(shí)例化,Bean還是一個(gè)半成品

? ? ? ? addSingletonFactory(beanName, new ObjectFactory<Object>() {

?// ObjectFactory的getObejct()方法實(shí)際就會調(diào)用到getEarlyBeanReference()方法

? ? ? ? // 如果需要?jiǎng)討B(tài)代理,getEarlyBeanReference()方法會返回原始對象的代理對象

? ? ? ? // 如果不需要?jiǎng)討B(tài)代理,getEarlyBeanReference()方法會返回原始對象

? ? ? ? ? ? @Override

? ? ? ? ? ? public Object getObject() throws BeansException {

? ? ? ? ? ? ? ? return getEarlyBeanReference(beanName, mbd, bean);

? ? ? ? ? ? }

? ? ? ? });

? ? }

? ? // Initialize the bean instance.

? ? Object exposedObject = bean;

? ? try {

? ? ? ? // 2、Bean的屬性注入

? ? ? ? populateBean(beanName, mbd, instanceWrapper);

? ? ? ? if (exposedObject != null) {

? ? ? ? ? ? // 3、Bean的初始化

? ? ? ? ? ? exposedObject = initializeBean(beanName, exposedObject, mbd);

? ? ? ? }

? ? }

? ? ......

?

?// 返回創(chuàng)建完成的Bean

? ? return exposedObject;

}

注:createBeanInstance(...)該步驟會調(diào)用構(gòu)造方法,來實(shí)例化Bean

AbstractAutowireCapableBeanFactory.java

/**

?* 使用適當(dāng)?shù)膶?shí)例化策略為指定的bean創(chuàng)建一個(gè)新實(shí)例:

?* 工廠方法,構(gòu)造函數(shù)自動裝配或簡單實(shí)例化。

?* @return BeanWrapper for the new instance

?*/

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {

? ?

? ? .......

? ? // Need to determine the constructor...(確定構(gòu)造函數(shù))

? ? Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);

? ? if (ctors != null ||

? ? ? ? ? ? mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||

? ? ? ? ? ? mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) ?{

? ? ? ? return autowireConstructor(beanName, mbd, ctors, args);

? ? }

? ? // No special handling: simply use no-arg constructor.(使用默認(rèn)無參構(gòu)造器,

? ? // 編程時(shí)候要求盡量保留無參構(gòu)造器,因?yàn)槟悴恢滥膫€(gè)框架在哪會用到)

? ? return instantiateBean(beanName, mbd);

}

至于其他的屬性注入populateBean()和初始化initializeBean()的源碼分析,在之前的IOC筆記中已經(jīng)講過了,這里就不再贅述了。

在populateBean()屬性注入這一步里,進(jìn)入到給A注入B的流程。

4.1.1.2.1.1 調(diào)用addSingletonFactory方法:這個(gè)方法就是解決循環(huán)依賴問題的關(guān)鍵

調(diào)用位置如下圖所示:

AbstractAutowireCapableBeanFactory..doCreateBean()

為什么重新打一次包就不會循環(huán)依賴,Java,后端框架,# Spring,Spring,循環(huán)依賴,IoC,面試,三級緩存

在完成Bean的實(shí)例化后,屬性注入之前Spring將Bean包裝成一個(gè)工廠添加進(jìn)了三級緩存中,對應(yīng)源碼如下:

DefaultSingletonBeanRegistry.java

/**

?* ?添加一個(gè)構(gòu)建指定單例對象的單例工廠

?* ?緊急注冊單例對象,用于解決解決循環(huán)依賴問題

?* To be called for eager registration of singletons, e.g. to be able to

?* resolve circular references.

?*/

// 這里傳入的參數(shù)也是一個(gè)lambda表達(dá)式,() -> getEarlyBeanReference(beanName, mbd, bean)

protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {

? ? Assert.notNull(singletonFactory, "Singleton factory must not be null");

? ? synchronized (this.singletonObjects) {

? ? ? ? // 如果在一級緩存中不存在這個(gè)Bean,那么就添加到三級緩存中

? ? ? ? if (!this.singletonObjects.containsKey(beanName)) {

? ? ? ? ? ? // 添加到三級緩存中

? ? ? ? ? ? this.singletonFactories.put(beanName, singletonFactory);

? ? ? ? ? ? // 從二級緩存中移除

? ? ? ? ? ? this.earlySingletonObjects.remove(beanName);

? ? ? ? ? ? this.registeredSingletons.add(beanName);

? ? ? ? }

? ? }

}

?

通過這個(gè)代碼的注釋我們就能知道,第三級緩存就是堅(jiān)決循環(huán)依賴問題的關(guān)鍵,而這個(gè)方法也是解決循環(huán)依賴的關(guān)鍵所在,這段代碼發(fā)生在doCreateBean(...) 方法中 createBeanInstance之后,也就是說單例對象此時(shí)已經(jīng)被實(shí)例化出來了(調(diào)用了構(gòu)造器)。這個(gè)對象已經(jīng)被生產(chǎn)出來了,雖然還不完美(還沒有進(jìn)行創(chuàng)建Bean的第二步和第三步),但是已經(jīng)能被人認(rèn)出來了(根據(jù)對象引用能定位到堆中的對象),所以Spring此時(shí)將這個(gè)對象提前曝光出來讓大家認(rèn)識,讓大家使用。

這樣做有什么好處呢?讓我們來分析一下“A的某個(gè)field或者setter依賴了B的實(shí)例對象,同時(shí)B的某個(gè)field或者setter依賴了A的實(shí)例對象”這種循環(huán)依賴的情況。A首先完成了創(chuàng)建的第一步(完成實(shí)例化),并且將自己提前曝光到singletonFactories中,此時(shí)進(jìn)行創(chuàng)建的第二步(屬性注入),發(fā)現(xiàn)自己依賴對象B,此時(shí)就嘗試去get(B),發(fā)現(xiàn)B還沒有被create,所以走create(B)的流程,B在創(chuàng)建第一步的時(shí)候發(fā)現(xiàn)自己依賴了對象A,于是嘗試get(A),嘗試一級緩存singletonObjects(肯定沒有,因?yàn)锳還沒初始化完全),嘗試二級緩存earlySingletonObjects(也沒有),嘗試三級緩存singletonFactories,由于A通過ObjectFactory將自己提前曝光了,所以B能夠通過ObjectFactory.getObject拿到A對象(雖然A還沒有初始化完全,但是總比沒有好呀),B拿到A對象后順利完成了創(chuàng)建的階段1、2、3,B完成創(chuàng)建后之后將自己放入到一級緩存singletonObjects中。此時(shí)返回A的創(chuàng)建流程中,A此時(shí)能拿到B的對象順利完成自己的創(chuàng)建階段2、3,最終A也完成了創(chuàng)建,將創(chuàng)建好的A添加到一級緩存singletonObjects中,而且更加幸運(yùn)的是,由于B拿到了A的對象引用,所以B現(xiàn)在持有的A對象也完成了創(chuàng)建。(簡單來說,就是spring創(chuàng)造了一個(gè)循環(huán)依賴的結(jié)束點(diǎn)標(biāo)識)

?

?

再回到這個(gè)方法源碼本身,這個(gè)方法只是添加了一個(gè)工廠,在創(chuàng)建A的流程中執(zhí)行到這個(gè)方法時(shí),A只是完成了實(shí)例化,還沒有完成屬性注入和初始化。上面的文字也講了B通過這個(gè)工廠(ObjectFactory)的getObject方法可以得到A對象,而這個(gè)A對象實(shí)際上就是通過getObject方法中再去調(diào)用getEarlyBeanReference這個(gè)方法創(chuàng)獲取的。既然singletonFactory.getObject()方法是在創(chuàng)建B的流程中調(diào)用的,下面我們就再進(jìn)入到創(chuàng)建B的流程中進(jìn)行講解。

4.1.2 創(chuàng)建B

當(dāng)A完成了實(shí)例化并添加進(jìn)了三級緩存后,就要通過方法populateBean()開始為A進(jìn)行屬性注入了,在注入時(shí)發(fā)現(xiàn)A依賴了B,那么這個(gè)時(shí)候Spring又會去getBean(b),然后反射調(diào)用setter方法完成屬性注入。

流程圖如下:

為什么重新打一次包就不會循環(huán)依賴,Java,后端框架,# Spring,Spring,循環(huán)依賴,IoC,面試,三級緩存

4.1.2.1 調(diào)用第一個(gè)getSingleton()方法實(shí)現(xiàn)對B注入A

因?yàn)锽需要注入A,所以在創(chuàng)建B的時(shí)候,又會去調(diào)用getBean(a),這個(gè)時(shí)候就又回到之前的流程了,但是不同的是,之前的getBean是為了創(chuàng)建Bean,而此時(shí)再調(diào)用getBean不是為了創(chuàng)建了,而是要從緩存中獲取,因?yàn)橹癆在實(shí)例化后已經(jīng)將其放入了三級緩存singletonFactories中,所以此時(shí)getBean(a)的流程就是這樣子了:

為什么重新打一次包就不會循環(huán)依賴,Java,后端框架,# Spring,Spring,循環(huán)依賴,IoC,面試,三級緩存

上面執(zhí)行的就是其實(shí)就是第一個(gè)getSingleton()方法,因?yàn)锳此時(shí)并不在一級和二級緩存中,而是在三級緩存中,所以最終會從三級緩存中獲取A,來將A注入給B。

@Nullable

// allowEarlyReference表示是否允許提前引用,如果為true,那么在Bean創(chuàng)建過程中,就可以通過getBean()方法獲取到該Bean,否則只能在Bean創(chuàng)建完成后才能獲取到該Bean

protected Object getSingleton(String beanName, boolean allowEarlyReference) {

? ? // Quick check for existing instance without full singleton lock

? ? // 嘗試從一級緩存singletonObjects中獲取該Bean

? ? Object singletonObject = this.singletonObjects.get(beanName);

? ? // 如果一級緩存中沒有該Bean,并且該Bean正在被創(chuàng)建(只要這個(gè)beanNameSingletonCurrentlyInCreation集合中,就表明這個(gè)Bean正在被創(chuàng)建)

? ? if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {

? ? ? ? // 如果一級緩存中沒有該Bean,并且該Bean正在被創(chuàng)建,那么嘗試從二級緩存earlySingletonObjects中獲取該Bean

? ? ? ? singletonObject = this.earlySingletonObjects.get(beanName);

? ? ? ? // 如果二級緩存中沒有該Bean,并且當(dāng)前允許Bean可以被提前引用(不用等Bean完全創(chuàng)建完成),那么嘗試從三級緩存singletonFactories中獲取該Bean

? ? ? ? if (singletonObject == null && allowEarlyReference) {

? ? ? ? ? ? synchronized (this.singletonObjects) {

? ? ? ? ? ? ? ? // Consistent creation of early reference within full singleton lock

? ? ? ? ? ? ? ? singletonObject = this.singletonObjects.get(beanName);

? ? ? ? ? ? ? ? if (singletonObject == null) {

? ? ? ? ? ? ? ? ? ? singletonObject = this.earlySingletonObjects.get(beanName);

? ? ? ? ? ? ? ? ? ? if (singletonObject == null) {

? ? ? ? ? ? ? ? ? ? ? ? // 從三級緩存singletonFactories中獲取該BeanBeanFactory

? ? ? ? ? ? ? ? ? ? ? ? ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);

? ? ? ? ? ? ? ? ? ? ? ? if (singletonFactory != null) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? // 調(diào)用工廠的getObject方法,其實(shí)本質(zhì)調(diào)用的就是從上游lambda表達(dá)式傳入的getEarlyBeanReference()方法

? ? ? ? ? ? ? ? ? ? ? ? ? ? singletonObject = singletonFactory.getObject();

????????????????????????????????????????????????? // 通過singletonFactory工廠類獲取到的bean添加到二級緩存中(如果這個(gè)Bean沒有AOP增強(qiáng),那么加入到二級緩存的就是這個(gè)Bean原始的實(shí)例化對象;如果這個(gè)BeanAOP增強(qiáng),那么工廠類返回的就是這個(gè)Bean的代理對象,就會將代理對象添加到二級緩存中)

? ? ? ? ? ? ? ? ? ? ? ? ? ? this.earlySingletonObjects.put(beanName, singletonObject);

??????????????????????????????????????????????????? // 將三級緩存中這個(gè)Bean的工廠類移除

? ? ? ? ? ? ? ? ? ? ? ? ? ? this.singletonFactories.remove(beanName);

? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? }

? ? return singletonObject;

}

4.1.2.1.1 調(diào)用getEarlyBeanReference()方法:用來從singletonFactory工廠類中返回Bean

從上面的源碼我們可以看出,注入到B中的A是通過getEarlyBeanReference方法提前暴露出去的一個(gè)對象,還不是一個(gè)完整的Bean,那么getEarlyBeanReference到底干了啥了,我們看下它的源碼:

// 這個(gè)方法名字其實(shí)就告訴了我們它的作用了,得到一個(gè)比較早的Bean引用(還沒有完全創(chuàng)建好的Bean,僅僅只是完成了實(shí)例化的Bean,還沒有完成屬性注入和初始化)

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {

? ? Object exposedObject = bean;

? ? // AOP增強(qiáng)操作

? ? if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {

? ? ? ? for (BeanPostProcessor bp : getBeanPostProcessors()) {

? ? ? ? ? ? if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {

? ? ? ? ? ? ? ? SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;

? ? ? ? ? ? ? ? exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);

? ? ? ? ? ? }

? ? ? ? }

? ? }

? ? return exposedObject;

}

?

它實(shí)際上就是調(diào)用了后置處理器的getEarlyBeanReference,而真正實(shí)現(xiàn)了這個(gè)方法的后置處理器只有一個(gè),就是通過@EnableAspectJAutoProxy注解導(dǎo)入的AnnotationAwareAspectJAutoProxyCreator。也就是說如果在不考慮AOP的情況下,上面的代碼等價(jià)于:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {

? ? Object exposedObject = bean;

? ? // 相當(dāng)于直接返回了原始Bean,并沒有進(jìn)行AOP增強(qiáng)

? ? return exposedObject;

}

也就是說這個(gè)工廠啥都沒干,直接將之前實(shí)例化階段創(chuàng)建的對象A返回了!所以說在不考慮AOP的情況下三級緩存有用嘛?講道理,真的沒什么用,我直接將這個(gè)對象放到二級緩存中不是一點(diǎn)問題都沒有嗎?所以三級緩存并沒有提高任何效率。

那么三級緩存到底有什么作用呢?不要急,我們先把整個(gè)流程走完,在下文結(jié)合AOP分析循環(huán)依賴的時(shí)候你就能體會到三級緩存的作用!

4.1.3 創(chuàng)建AB的流程總結(jié)

到這里不知道小伙伴們會不會有疑問,B中提前注入了一個(gè)沒有經(jīng)過初始化的A類型對象不會有問題嗎?

答:不會,因?yàn)槠鋵?shí)B中注入了A的引用,在后面A徹底完成創(chuàng)建之后,A的引用并不會變,所以B最終會得到一個(gè)完整的A。

這個(gè)時(shí)候我們將整個(gè)創(chuàng)建A這個(gè)Bean的流程總結(jié)一下,如下圖:

為什么重新打一次包就不會循環(huán)依賴,Java,后端框架,# Spring,Spring,循環(huán)依賴,IoC,面試,三級緩存

從上圖中我們可以看到,雖然在創(chuàng)建B時(shí)會提前給B注入了一個(gè)還未初始化的A對象,但是在創(chuàng)建A的流程中一直使用的是注入到B中的A對象的引用,之后會根據(jù)這個(gè)引用對A進(jìn)行初始化,所以這是沒有問題的。

4.2 結(jié)合了AOP的循環(huán)依賴

之前我們已經(jīng)說過了,在普通的循環(huán)依賴的情況下,三級緩存沒有任何作用。三級緩存實(shí)際上跟Spring中的AOP相關(guān),我們再來看一看getEarlyBeanReference的代碼:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {

? ? Object exposedObject = bean;

? ? // 如果不是合成的bean,且有實(shí)例化后置處理器,就說明這個(gè)BeanAOP代理增強(qiáng)操作,需要提前暴露出來

? ? if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {

? ? ? ? // 進(jìn)到這個(gè)if分支,說明開啟了AOP功能,且當(dāng)前Bean有實(shí)例化后置處理器

? ? ? ? // 遍歷當(dāng)前Bean的所有實(shí)例化后置處理器

? ? ? ? for (BeanPostProcessor bp : getBeanPostProcessors()) {

? ? ? ? ? ? // 如果當(dāng)前實(shí)例化后置處理器是SmartInstantiationAwareBeanPostProcessor類型的,則調(diào)用getEarlyBeanReference方法獲取代理增強(qiáng)后的Bean

? ? ? ? ? ? if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {

? ? ? ? ? ? ? ? SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;

? ? ? ? ? ? ? ? // 調(diào)用SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference方法,獲取代理增強(qiáng)后的Bean。調(diào)用的AnnotationAwareAspectJAutoProxyCreator的getEarlyBeanReference方法

? ? ? ? ? ? ? ? exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);

? ? ? ? ? ? }

? ? ? ? }

? ? }

? ? return exposedObject;

}

如果在開啟AOP的情況下,那么就是調(diào)用到AnnotationAwareAspectJAutoProxyCreator(SmartInstantiationAwareBeanPostProcessor 的實(shí)現(xiàn)類)的getEarlyBeanReference方法,對應(yīng)的源碼如下:

public Object getEarlyBeanReference(Object bean, String beanName) {

? ? Object cacheKey = getCacheKey(bean.getClass(), beanName);

? ? // earlyProxyReferences 存儲的是 (beanName, bean) 鍵值對,這里的 bean 指的是原始對象(剛實(shí)例化后的對象)

? ? this.earlyProxyReferences.put(cacheKey, bean);

? ? // wrapIfNecessary() 方法用于執(zhí)行 AOP 操作,生成一個(gè)代理對象(也就是說如果有 AOP 操作最后返回的是代理對象,否則返回的還是原始對象)。

? ? return wrapIfNecessary(bean, beanName, cacheKey);

}

回到上面的例子,我們對A進(jìn)行了AOP代理的話,那么此時(shí)getEarlyBeanReference將返回一個(gè)代理后的對象,而不是實(shí)例化階段創(chuàng)建的對象,這樣就意味著B中注入的A將是一個(gè)代理對象而不是A的實(shí)例化階段創(chuàng)建后的對象。

為什么重新打一次包就不會循環(huán)依賴,Java,后端框架,# Spring,Spring,循環(huán)依賴,IoC,面試,三級緩存

看到這個(gè)圖你可能會產(chǎn)生下面這些疑問:

1、在給B注入的時(shí)候?yàn)槭裁匆⑷胍粋€(gè)代理對象?

答:當(dāng)我們對A進(jìn)行了AOP代理時(shí),說明我們希望從容器中獲取到的就是A代理后的對象而不是A本身,因此把A當(dāng)作依賴進(jìn)行注入時(shí)也要注入它的代理對象。

2、明明在創(chuàng)建A的時(shí)候,到初始化這一步完成的時(shí)候仍然還是原始的A對象,那么如果對AAOP增強(qiáng)的話,為什么最終在Spring容器中取出的A是代理增強(qiáng)的對象呢?Spring是在什么時(shí)候?qū)⒋韺ο蠓湃氲饺萜髦械哪兀?/strong>

由上圖可見,創(chuàng)建A的流程中,在完成對A的初始化后,此時(shí)仍然還是原始的A對象。我們知道在A實(shí)例化之后,會將A的BeanFactory加入到第三級緩存中,這個(gè)BeanFactory可以返回AOP代理增強(qiáng)之后的A對象,但是此時(shí)在創(chuàng)建A的流程中,一直操作的是A的原始對象,并沒有通過BeanFactory獲取A的代理增強(qiáng)對象。只不過是在創(chuàng)建A所依賴的B時(shí),因?yàn)锽也同樣依賴A,而根據(jù)自然順序(按照BeanName的字典序)B在A之后創(chuàng)建,所以在對B注入A的時(shí)候三級緩存中已經(jīng)存在了A的BeanFactory,所以B注入的A是通過BeanFactory返回的A的代理增強(qiáng)后的對象。但是針對A本身的創(chuàng)建流程來說,在A初始化后,操作的仍然是A的原始對象。

那么問題來了,在我們最終通過Spring容器獲取A對象的時(shí)候一定是代理增強(qiáng)之后的A對象,那么究竟是什么時(shí)候Spring將A的代理對象加入到容器中的呢?我們還是要通過源碼來找到答案。

我們回到創(chuàng)建A的流程,定位到doCreateBean()方法,我們看當(dāng)完成對A的初始化之后,后面又再一次調(diào)用了上面講的第一個(gè)getSingleton()方法,這個(gè)方法是嘗試從緩存中獲取Bean。這里相當(dāng)于通過getSingleton(A)從緩存中獲取A。

/**

?* 實(shí)際創(chuàng)建指定的bean的方法。 這個(gè)方法完成了 1、Bean的實(shí)例化 ? 2、Bean的屬性注入 ? 3、Bean的初始化

?* 此時(shí),預(yù)創(chuàng)建處理已經(jīng)發(fā)生,

?* 例如 檢查{@code postProcessBeforeInstantiation}回調(diào)。

?* 區(qū)分默認(rèn)bean實(shí)例化、使用工廠方法和自動裝配構(gòu)造函數(shù)。

?*/

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {

? ? // Instantiate the bean.

? ? BeanWrapper instanceWrapper = null;

? ? .....

? ?

? ? if (instanceWrapper == null) {

? ? ? ? // 1、實(shí)例化Bean

? ? ? ? instanceWrapper = createBeanInstance(beanName, mbd, args);

? ? }

? ? .....

? ? // Eagerly cache singletons to be able to resolve circular references

? ? // even when triggered by lifecycle interfaces like BeanFactoryAware.

? ? boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&

? ? ? ? ? ? isSingletonCurrentlyInCreation(beanName));

? ? if (earlySingletonExposure) {

? ? ? ? if (logger.isDebugEnabled()) {

? ? ? ? ? ? logger.debug("Eagerly caching bean '" + beanName +

? ? ? ? ? ? ? ? ? ? "' to allow for resolving potential circular references");

? ? ? ? }

? ? ? ? // 將創(chuàng)建的BeanFactory添加到三級緩存中。注意此時(shí)Bean并沒有徹底創(chuàng)建完成,此時(shí)只進(jìn)行了實(shí)例化,Bean還是一個(gè)半成品

? ? ? ? addSingletonFactory(beanName, new ObjectFactory<Object>() {

? ? ? ? ? ? @Override

? ? ? ? ? ? public Object getObject() throws BeansException {

? ? ? ? ? ? ? ? return getEarlyBeanReference(beanName, mbd, bean);

? ? ? ? ? ? }

? ? ? ? });

? ? }

? ? // Initialize the bean instance.

? ? Object exposedObject = bean;

? ? try {

? ? ? ? // 2Bean的屬性注入

? ? ? ? populateBean(beanName, mbd, instanceWrapper);

? ? ? ? if (exposedObject != null) {

? ? ? ? ? ? // 3、Bean的初始化

? ? ? ? ? ? exposedObject = initializeBean(beanName, exposedObject, mbd);

? ? ? ? }

? ? }

? ? ......

? ? // exposedObject就是最終創(chuàng)建完成的Bean

? ? if (earlySingletonExposure) {

? ? ? ? // 在完成了Bean的初始化后,再一次調(diào)用了上面講過的第一個(gè)getSingleton()方法,區(qū)別就是這次傳入的第二個(gè)參數(shù)是false

? ? ? ? // 這次調(diào)用實(shí)現(xiàn)了從二級緩存中獲取到代理后的Bean

? ? ? ? Object earlySingletonReference = getSingleton(beanName, false);

? ? ? ? if (earlySingletonReference != null) {

? ? ? ? ? ? if (exposedObject == bean) {

? ? ? ? ? ? ? ? // 將最終完成的Bean替換成了代理對象,在上游方法中向后執(zhí)行addSingleton()方法時(shí)會將代理對象添加到一級緩存中

? ? ? ? ? ? ? ? exposedObject = earlySingletonReference;

? ? ? ? ? ? }

? ? ? ? ? ?

? ? ? ? ? ? ...

? ? ? ? }

? ? }

? ? ......

? ? // 返回最終創(chuàng)建完成的Bean

? ? return exposedObject;

}

由源碼可知Spring又調(diào)用了一次getSingleton方法,但這一次傳入的參數(shù)又不一樣了,第二個(gè)參數(shù)傳入的是false,false可以理解為禁用第三級緩存,前面圖中已經(jīng)提到過了,在為B中注入A后,就將A的BeanFactory返回的代理對象加到了二級緩存中,并就將A的BeanFactory從三級緩存中的移除。此時(shí)A的BeanFactory已經(jīng)不在三級緩存中了,并且在本次調(diào)用getSingleton方法是傳入的參數(shù)已經(jīng)保證了禁用第三級緩存了,所以這里的這個(gè)getSingleton方法做的實(shí)際就是從二級緩存中獲取到這個(gè)代理后的A對象。exposedObject == bean可以認(rèn)為是一定成立的,除非你非要在初始化階段的后置處理器中替換掉正常流程中的Bean,例如增加一個(gè)后置處理器:

@Component

public class MyPostProcessor implements BeanPostProcessor {

? ? @Override

? ? public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

? ? ? ? if (beanName.equals("a")) {

? ? ? ? ? ? // 換成別的Bean

? ? ? ? ? ? return new A();

? ? ? ? }

? ? ? ? return bean;

? ? }

}

不過,請不要做這種騷操作,徒增煩惱!

至此,在A的創(chuàng)建流程中,就已經(jīng)將原始Bean換成了經(jīng)過AOP增強(qiáng)的Bean了。在前面講過的創(chuàng)建A的流程中addSingleton()方法中,也就將這個(gè)徹底創(chuàng)建完成的代理增強(qiáng)的Bean加入到了一級緩存中,并且在最后返回給了Spring容器。

3、初始化的時(shí)候是對A對象本身進(jìn)行初始化(初始化之前也都是對原始A對象進(jìn)行的處理),而添加到Spring容器中以及注入到B中的都是代理對象,這樣不會有問題嗎?

答:不會,這是因?yàn)椴还苁莄glib代理還是jdk動態(tài)代理生成的代理類,內(nèi)部都持有一個(gè)目標(biāo)類的引用,當(dāng)調(diào)用代理對象的方法時(shí),實(shí)際會去調(diào)用目標(biāo)對象的方法,A原始類完成初始化相當(dāng)于代理對象自身也完成了初始化。

4、三級緩存為什么要使用工廠而不是直接使用引用?換而言之,為什么需要這個(gè)三級緩存,直接通過二級緩存暴露一個(gè)引用不行嗎?

答:這個(gè)工廠的目的在于延遲對實(shí)例化階段生成的對象的代理,只有真正發(fā)生循環(huán)依賴的時(shí)候,才去提前生成代理對象,否則只會創(chuàng)建一個(gè)工廠并將其放入到三級緩存中,但是不會去通過這個(gè)工廠去真正創(chuàng)建對象。

我們思考一種簡單的情況,就以單獨(dú)創(chuàng)建A為例,假設(shè)AB之間現(xiàn)在沒有依賴關(guān)系,但是A被代理了,這個(gè)時(shí)候創(chuàng)建A會進(jìn)入到doCreateBean()方法中,當(dāng)A完成實(shí)例化后還是會繼續(xù)向下執(zhí)行這段代碼:

AbstractAutowireCapableBeanFactory..doCreateBean()

......

// A是單例的,mbd.isSingleton()條件滿足

// allowCircularReferences:這個(gè)變量代表是否允許循環(huán)依賴,默認(rèn)是開啟的,條件也滿足

// isSingletonCurrentlyInCreation:正在在創(chuàng)建A,也滿足

// 所以earlySingletonExposure=true

boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? isSingletonCurrentlyInCreation(beanName));

// 還是會進(jìn)入到這段代碼中

if (earlySingletonExposure) {

? ? // 還是會通過三級緩存提前暴露一個(gè)工廠對象

? ? addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));

}

......

通過源碼我們就能發(fā)現(xiàn),即使沒有循環(huán)依賴,也會將其添加到三級緩存中,而且是不得不添加到三級緩存中,因?yàn)榈侥壳盀橹惯€沒有到對A進(jìn)行屬性注入的階段,Spring也不能確定這個(gè)Bean有沒有跟別的Bean出現(xiàn)循環(huán)依賴。

假設(shè)我們在這里直接使用二級緩存的話,那么意味著所有的Bean在這一步都要完成AOP代理。這樣做有必要嗎?

不僅沒有必要,而且違背了Spring在結(jié)合AOP和Bean的生命周期的設(shè)計(jì)原則!Spring結(jié)合AOP和Bean的生命周期本身就是通過AnnotationAwareAspectJAutoProxyCreator這個(gè)后置處理器來完成的(上面講過的返回AOP代理的getEarlyBeanReference()方法就是這個(gè)后置處理器提供的),在這個(gè)后置處理器的postProcessAfterInitialization方法會在初始化后被調(diào)用,完成對Bean的AOP代理。如果出現(xiàn)了循環(huán)依賴,那沒有辦法,只有給Bean先創(chuàng)建代理;但是沒有出現(xiàn)循環(huán)依賴的情況下,Spring的源碼設(shè)計(jì)就是在Bean完成初始化之后,調(diào)用執(zhí)行postProcessAfterInitialization方法完成AOP代理,也就是在生命周期的最后一步完成代理而不是在實(shí)例化后就立馬完成代理。所以如果只使用二級緩存的話,那么完成代理的步驟就會被提到實(shí)例化之后了,和Spring的底層源碼流程不符。在下一節(jié)我們會展示使用三級緩存和使用二級緩存的Bean創(chuàng)建流程的差異,就能很直觀地看出來了。

4.3 Spring 解決 Bean 的循環(huán)依賴的流程總結(jié)

還是以?AService?和?BService?的循環(huán)依賴為例,我們來總結(jié)一下 Spring 是如何解決 Bean 的循環(huán)依賴問題。

梳理整個(gè)流程:

  1. 首先會獲取 AService 對應(yīng)的 Bean 對象。
  2. 先是調(diào)用 doGetBean() 中的第一個(gè) getSingleton(beanName) 判斷是否有該 Bean 的實(shí)例,有就直接返回了。(顯然這里沒有)
  3. 然后調(diào)用 doGetBean() 中的第二個(gè) getSingleton() 方法來執(zhí)行 doCreateBean() 方法。
  4. 先進(jìn)行實(shí)例化操作(也就是利用構(gòu)造函數(shù)實(shí)例化),此時(shí)實(shí)例化后生成的是原始對象。
  5. 將原始對象通過 lambda表達(dá)式 進(jìn)行封裝成 ObjectFactory 對象,通過 addSingletonFactory 加入三級緩存中。
  6. 然后再進(jìn)行屬性注入,此時(shí)發(fā)現(xiàn)需要注入 BService 的 Bean,會通過 doGetBean() 去獲取 BService 對應(yīng)的 Bean。
  7. 同樣調(diào)用 doGetBean() 中的第一個(gè) getSingleton(beanName) 判斷是否有該 Bean 的實(shí)例,顯然這里也是不會有 BService 的 Bean 的。
  8. 然后只能調(diào)用 doGetBean() 中的第二個(gè) getSingleton() 方法來執(zhí)行 doCreateBean() 方法來創(chuàng)建一個(gè) BService 的 Bean。
  9. 同樣地先進(jìn)行實(shí)例化操作,生成原始對象后封裝成 ObjectFactory 對象放入三級緩存中。
  10. 然后進(jìn)行屬性注入,此時(shí)發(fā)現(xiàn)需要注入 AService 的 Bean,此時(shí)調(diào)用調(diào)用 doGetBean() 中的第一個(gè) getSingleton(beanName) 查找是否有 AService 的 Bean。此時(shí)會觸發(fā)三級緩存,也就是調(diào)用 singletonFactories.get(beanName)。
  11. 因?yàn)槿壘彺嬷杏?AService 的原始對象封裝的 ObjectFactory 對象,所以可以獲取到的代理對象或原始對象,并且上移到二級緩存中,提前暴露給 BService 調(diào)用。
  12. 所以 BService 可以完成屬性注入,然后進(jìn)行初始化后,將 Bean 放入一級緩存,這樣 AService 也可以完成創(chuàng)建。

以上就是 Spring 解決 Bean 的循環(huán)依賴問題的整個(gè)流程了。

帶著調(diào)用方法的流程圖:

為什么重新打一次包就不會循環(huán)依賴,Java,后端框架,# Spring,Spring,循環(huán)依賴,IoC,面試,三級緩存

4.4 三級緩存真的提高了效率了嗎?

現(xiàn)在我們已經(jīng)知道了第三級緩存的真正作用,但是這個(gè)答案可能還無法說服你,所以我們再最后總結(jié)分析一波,三級緩存真的提高了效率了嗎?分為兩點(diǎn)討論:

  1. 沒有進(jìn)行AOPBean間的循環(huán)依賴

從上文分析可以看出,這種情況下第三級緩存根本沒用!所以不會存在什么提高了效率的說法

  1. 進(jìn)行了AOPBean間的循環(huán)依賴

就以我們上的A、B為例,其中A被AOP代理,我們先分析下使用了第三級緩存的情況下,A、B的創(chuàng)建流程

為什么重新打一次包就不會循環(huán)依賴,Java,后端框架,# Spring,Spring,循環(huán)依賴,IoC,面試,三級緩存

假設(shè)不使用第三級緩存,直接將代理對象放到二級緩存中

為什么重新打一次包就不會循環(huán)依賴,Java,后端框架,# Spring,Spring,循環(huán)依賴,IoC,面試,三級緩存

上面兩個(gè)流程的唯一區(qū)別在于為A對象創(chuàng)建代理的時(shí)機(jī)不同,在使用了三級緩存的情況下為A創(chuàng)建代理的時(shí)機(jī)是在B中需要注入A的時(shí)候,而不使用三級緩存的話在A實(shí)例化后就需要馬上為A創(chuàng)建代理然后放入到二級緩存中去。對于整個(gè)A、B的創(chuàng)建過程而言,消耗的時(shí)間是一樣的

綜上,不管是哪種情況,三級緩存提高了效率這種說法都是錯(cuò)誤的!

五、循環(huán)依賴問題的解決方案

解決循環(huán)依賴問題有一些比較常用的解決方案,下面來依次講解。

5.1 重新設(shè)計(jì)

當(dāng)你面臨一個(gè)循環(huán)依賴問題時(shí),有可能是你對JavaBean的設(shè)計(jì)有問題,沒有將各自的依賴做到很好的分離。你應(yīng)該盡量正確地重新設(shè)計(jì)JavaBean,以保證它們的層次是精心設(shè)計(jì)的,避免沒有必要的循環(huán)依賴。

如果不能重新設(shè)計(jì)組件(可能有很多的原因:遺留代碼,已經(jīng)被測試并不能修改代碼,沒有足夠的時(shí)間或資源來完全重新設(shè)計(jì)......),但有一些變通方法可以解決這個(gè)問題。

5.2 使用 Setter/Field 注入

最流行的解決循環(huán)依賴的方法,就是Spring文檔中建議的,設(shè)計(jì)Bean的時(shí)候都使用setter注入。簡單地說,你對你須要注入的bean是使用setter注入(或字段注入),而不是構(gòu)造函數(shù)注入。通過這種注入方式產(chǎn)生循環(huán)依賴的Bean,Spring框架自身就能解決,也就是上面我們用了這么大篇幅講解的內(nèi)容。

5.3 使用@Lazy注解

如果產(chǎn)生循環(huán)依賴的Bean,都是通過構(gòu)造方法來注入彼此的,那么這種情況是沒有辦法通過Spring框架自身來解決的。

解決這種情況的一個(gè)簡單方法就是對一個(gè)Bean使用延時(shí)加載。也就是說:通過在構(gòu)造器參數(shù)中標(biāo)識@Lazy注解,Spring 生成并返回了一個(gè)代理對象,因此給CircularDependencyA注入的CircularDependencyB并非真實(shí)對象而是其代理。

舉一個(gè)具體的例子:

@Component

public class CircularDependencyA {

?

? ? private CircularDependencyB circB;

?

? ? @Autowired

? ? public CircularDependencyA(CircularDependencyB circB) {

? ? ? ? this.circB = circB;

? ? }

}

?

@Component

public class CircularDependencyB {

?

? ? private CircularDependencyA circA;

?

? ? @Autowired

? ? public CircularDependencyB(CircularDependencyA circA) {

? ? ? ? this.circA = circA;

? ? }

}

我們對CircularDependencyA 進(jìn)行修改,結(jié)果如下:

@Component

public class CircularDependencyA {

?

? ? private CircularDependencyB circB;

?

? ? @Autowired

? ? // 對傳入的B實(shí)行延遲加載,即可解決循環(huán)依賴的問題

? ? public CircularDependencyA(@Lazy CircularDependencyB circB) {

? ? ? ? this.circB = circB;

? ? }

}

如果你現(xiàn)在運(yùn)行測試,你會發(fā)現(xiàn)之前的循環(huán)依賴錯(cuò)誤不存在了。

結(jié)論:

Spring構(gòu)造器注入循環(huán)依賴的解決方案是@Lazy,其基本思路是:對于強(qiáng)依賴的對象,一開始并不注入對象本身,而是注入其代理對象,以便順利完成實(shí)例的構(gòu)造,形成一個(gè)完整的對象,這樣與其它應(yīng)用層對象就不會形成互相依賴的關(guān)系;當(dāng)需要調(diào)用真實(shí)對象的方法時(shí),通過TargetSource去拿到真實(shí)的對象(DefaultListableBeanFactory#doResolveDependency),然后通過反射完成調(diào)用

5.4 使用 @PostConstruct注解

打破循環(huán)的另一種方式是,在要注入的屬性(該屬性是一個(gè)bean)上使用 @Autowired,并使用@PostConstruct 標(biāo)注在另一個(gè)方法,且該方法里設(shè)置對其他的依賴。

我們的Bean將修改成下面的代碼:

@Component

public class CircularDependencyA {

?

? ? @Autowired

? ? private CircularDependencyB circB;

? ?

? ? // A的構(gòu)造方法完成后執(zhí)行

? ? @PostConstruct

? ? public void init() {

? ? ? ? // 通過setterB注入A

? ? ? ? circB.setCircA(this);

? ? }

?

? ? public CircularDependencyB getCircB() {

? ? ? ? return circB;

? ? }

}

?

@Component

public class CircularDependencyB {

?

? ? private CircularDependencyA circA;

? ? ?

? ? private String message = "Hi!";

?

? ? public void setCircA(CircularDependencyA circA) {

? ? ? ? this.circA = circA;

? ? }

? ? ?

? ? public String getMessage() {

? ? ? ? return message;

? ? }

}

現(xiàn)在我們運(yùn)行我們修改后的代碼,發(fā)現(xiàn)并沒有拋出異常,并且依賴正確注入進(jìn)來。

5.5 實(shí)現(xiàn)ApplicationContextAware 和 InitializingBean接口

如果一個(gè)Bean實(shí)現(xiàn)了ApplicationContextAware,該Bean可以訪問Spring上下文,并可以從那里獲取到其他的bean。實(shí)現(xiàn)InitializingBean接口,表明這個(gè)bean在所有的屬性設(shè)置完后做一些后置處理操作(調(diào)用的順序?yàn)閕nit-method后調(diào)用)。在這種情況下,我們需要手動設(shè)置依賴。

@Component

public class CircularDependencyA implements ApplicationContextAware, InitializingBean {

?

? ? private CircularDependencyB circB;

?

? ? private ApplicationContext context;

?

? ? public CircularDependencyB getCircB() {

? ? ? ? return circB;

? ? }

?

? ? @Override

? ? public void afterPropertiesSet() throws Exception {

? ? ? ? circB = context.getBean(CircularDependencyB.class);

? ? }

?

? ? @Override

? ? public void setApplicationContext(final ApplicationContext ctx) throws BeansException {

? ? ? ? context = ctx;

? ? }

}

public class CircularDependencyB {

?

? ? private CircularDependencyA circA;

?

? ? private String message = "Hi!";

?

? ? @Autowired

? ? public void setCircA(CircularDependencyA circA) {

? ? ? ? this.circA = circA;

? ? }

?

? ? public String getMessage() {

? ? ? ? return message;

? ? }

}

同樣,發(fā)現(xiàn)沒有異常拋出,程序結(jié)果是我們所期望的那樣。

5.6 思考題:為什么在下表中的第三種情況的循環(huán)依賴能被解決,而第四種情況不能被解決呢?

提示:Spring在創(chuàng)建Bean時(shí)默認(rèn)會根據(jù)自然排序進(jìn)行創(chuàng)建,所以A會先于B進(jìn)行創(chuàng)建

依賴情況

依賴注入方式

循環(huán)依賴是否被解決

AB相互依賴(循環(huán)依賴)

均采用setter方法注入

AB相互依賴(循環(huán)依賴)

均采用構(gòu)造器注入

AB相互依賴(循環(huán)依賴)

A中注入B的方式為setter方法,B中注入A的方式為構(gòu)造器

AB相互依賴(循環(huán)依賴)

B中注入A的方式為setter方法,A中注入B的方式為構(gòu)造器

其實(shí)這個(gè)原因很好理解。通過上面對Spring解決循環(huán)依賴問題原理的講解,我們已經(jīng)對它底層代碼有了一定了解。我們再拿過來這個(gè)流程圖,對著圖來進(jìn)行講解:

為什么重新打一次包就不會循環(huán)依賴,Java,后端框架,# Spring,Spring,循環(huán)依賴,IoC,面試,三級緩存

第三種情況

第三種情況的循環(huán)依賴是可以被Spring自身解決的。我們按照上面的流程圖梳理一下就能明白了。

Spring根據(jù)beanName字典序來進(jìn)行創(chuàng)建,所以先創(chuàng)建A。A是通過setter方法注入B的,所以A可以正常執(zhí)行完createBeanInstance()方法完成實(shí)例化,在執(zhí)行完實(shí)例化方法之后,就會執(zhí)行addSingletonFactory()方法來講A的beanFactory加入到第三級緩存中,在后面對A進(jìn)行屬性注入時(shí),發(fā)現(xiàn)它依賴了B,就進(jìn)入到了創(chuàng)建B的流程,而B也依賴了A,是通過構(gòu)造方法注入的A,所以B在執(zhí)行到實(shí)例化階段時(shí)候,在構(gòu)造方法中就開始嘗試注入A了,會通過前面已經(jīng)加入到第三級緩存的beanFactory來獲取到A,注入給B,所以B就順利注入了A,完成了整個(gè)創(chuàng)建過程。然后A也就能順利注入B,最終完成了創(chuàng)建過程。至此,A和B都完成了創(chuàng)建。

第四種情況

這個(gè)情況的循環(huán)依賴是沒辦法通過Spring自身解決的。

依然是先創(chuàng)建A,但是A是通過構(gòu)造方法注入的B,也就是說A在注入B的時(shí)候,仍然還在實(shí)例化階段,實(shí)例化方法還沒有執(zhí)行完。通過上面我們知道,將A的beanFactory加入到三級緩存的addSingletonFactory()方法是在完成實(shí)例化方法createBeanInstance()之后執(zhí)行的,但是此時(shí)仍然在實(shí)例化的過程中,三級緩存中還沒有A的beanFactory,這個(gè)時(shí)候就去注入B,在進(jìn)入到創(chuàng)建B的流程中時(shí),又會對B來注入A,但此時(shí)三級緩存中獲取不到對應(yīng)的beanFactory,也就無法得到A,無法完成對B的創(chuàng)建,后續(xù)的流程也就無法推進(jìn)下去,這樣就直接報(bào)錯(cuò)循環(huán)依賴問題了。

六、什么樣的循環(huán)依賴無法處理?

1、因?yàn)榧尤雜ingletonFactories三級緩存的前提是執(zhí)行了構(gòu)造器來創(chuàng)建半成品的對象,所以構(gòu)造器的循環(huán)依賴沒法解決(指的是沒辦法通過Spring自身解決,但是可以通過程序員自己使用@Lazy注解來解決)。因此Spring不能解決“A的構(gòu)造方法中依賴了B的實(shí)例對象,同時(shí)B的構(gòu)造方法中依賴了A的實(shí)例對象”這類問題了!

2、spring不支持原型(prototype)bean屬性注入循環(huán)依賴,不同于構(gòu)造器注入循環(huán)依賴會在創(chuàng)建spring容器context時(shí)報(bào)錯(cuò),它會在用戶執(zhí)行代碼如context.getBean()時(shí)拋出異常。因?yàn)閷τ谠蚥ean,spring容器會在每一次使用它的時(shí)候創(chuàng)建一個(gè)全新的Bean。

因?yàn)閟pring容器不緩存prototype類型的bean,使得無法提前暴露出一個(gè)創(chuàng)建中的bean。spring容器在獲取prototype類型的bean時(shí),如果因?yàn)檠h(huán)依賴的存在,檢測到當(dāng)前線程已經(jīng)正在處理該bean時(shí),就會拋出異常。核心代碼:

public abstract class AbstractBeanFactory{

? ? /** Names of beans that are currently in creation */

? ? private final ThreadLocal<Object> prototypesCurrentlyInCreation =

? ? ? ? ? ? new NamedThreadLocal<>("Prototype beans currently in creation");

? ? ? ? ? ?

? ? protected boolean isPrototypeCurrentlyInCreation(String beanName) {

? ? ? ? Object curVal = this.prototypesCurrentlyInCreation.get();

? ? ? ? // 如果beanName已經(jīng)存在于正在處理中的prototype類型的bean集中,后面會拋出異常

? ? ? ? return (curVal != null &&

? ? ? ? ? ? ? ? (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));

? ? }

}

七、面試題

7.1 Spring是如何解決的循環(huán)依賴?

答:Spring通過三級緩存解決了循環(huán)依賴,其中一級緩存為單例池(singletonObjects),二級緩存為早期曝光對象earlySingletonObjects,三級緩存為早期曝光對象工廠(singletonFactories)。當(dāng)A、B兩個(gè)類發(fā)生循環(huán)引用時(shí),在A完成實(shí)例化后,就使用實(shí)例化后的對象去創(chuàng)建一個(gè)對象工廠,并添加到三級緩存中,如果A被AOP代理,那么通過這個(gè)工廠獲取到的就是A代理后的對象,如果A沒有被AOP代理,那么通過這個(gè)工廠獲取到的就是A實(shí)例化的對象。當(dāng)A進(jìn)行屬性注入時(shí),會去創(chuàng)建B,同時(shí)B又依賴了A,所以創(chuàng)建B的同時(shí)又會去調(diào)用getBean(a)來獲取需要的依賴,此時(shí)的getBean(a)會從緩存中獲取,第一步,先獲取到三級緩存中的工廠;第二步,調(diào)用對象工工廠的getObject方法來獲取到對應(yīng)的對象,得到這個(gè)對象后將其注入到B中。緊接著B會走完它的生命周期流程,包括初始化、后置處理器等。當(dāng)B創(chuàng)建完后,會將B再注入到A中,此時(shí)A再完成它的整個(gè)生命周期。至此,循環(huán)依賴結(jié)束!

簡單點(diǎn)說,Spring解決循環(huán)依賴的思路就是:當(dāng)A的bean需要B的bean的時(shí)候,提前將A的bean放在緩存中(實(shí)際是將A的ObjectFactory放到三級緩存),然后再去創(chuàng)建B的bean,但是B的bean也需要A的bean,那么這個(gè)時(shí)候就去緩存中拿A的bean,B的bean創(chuàng)建完畢后,再回來繼續(xù)創(chuàng)建A的bean,最終完成循環(huán)依賴的解決。Spring 利用?三級緩存?巧妙地將出現(xiàn)?循環(huán)依賴?時(shí)的?AOP 操作?提前到了?屬性注入?之前(通過第三級緩存實(shí)現(xiàn)的),解決了循環(huán)依賴問題。

7.2 為什么不直接使用一級緩存來解決循環(huán)依賴

答:一級緩存中預(yù)期存放的是一個(gè)正常完整的bean,而如果只用一級緩存來解決循環(huán)依賴,那么一級緩存中會在某個(gè)時(shí)間段存在不完整的bean,這是不安全的。

7.3 為什么要使用三級緩存呢?直接使用一級緩存和二級緩存能解決循環(huán)依賴嗎?

答:這個(gè)問題需要結(jié)合為什么引入三級緩存來分析。引用前面的論述,使用一級緩存和二級緩存確實(shí)可以解決循環(huán)依賴,但是這要求每個(gè)原始對象創(chuàng)建出來后就立即生成動態(tài)代理對象(如果有的AOP代理增強(qiáng)話),然后將這個(gè)動態(tài)代理對象放入二級緩存,這就打破了SpringAOP的設(shè)計(jì)原則,即:在對象初始化完畢后,再去創(chuàng)建代理對象。所以引入三級緩存,并且在三級緩存中存放一個(gè)對象的ObjectFactory,目的就是:延遲代理對象的創(chuàng)建,這里延遲到啥時(shí)候創(chuàng)建呢,有兩種情況:第一種就是確實(shí)存在循環(huán)依賴,那么沒辦法,只能在需要的時(shí)候就創(chuàng)建出來代理對象然后放到二級緩存中,第二種就是不存在循環(huán)依賴,那就應(yīng)該正常地在初始化的后置處理器中創(chuàng)建。

因此不直接使用一級緩存和二級緩存來解決循環(huán)依賴的原因就是:希望在不存在循環(huán)依賴的情況下不破壞Spring對AOP的設(shè)計(jì)原則。

所以總結(jié)來說,如果要使用二級緩存解決循環(huán)依賴,意味著所有Bean在實(shí)例化后就要完成AOP代理,這樣違背了Spring設(shè)計(jì)的原則,Spring在設(shè)計(jì)之初就是通過AnnotationAwareAspectJAutoProxyCreator這個(gè)后置處理器來在Bean生命周期的最后一步來完成AOP代理,而不是在實(shí)例化后就立馬進(jìn)行AOP代理。


參考資料:https://www.cnblogs.com/daimzh/p/13256413.html#!comments文章來源地址http://www.zghlxwxcb.cn/news/detail-854521.html

到了這里,關(guān)于【Spring框架】一篇文章帶你徹底搞懂Spring解決循環(huán)依賴的底層原理的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 一篇文章帶你搞懂前端Cookie

    一篇文章帶你搞懂前端Cookie

    瀏覽器Cookie相信各位點(diǎn)進(jìn)這篇文章的小伙伴應(yīng)該不陌生了,它是前端領(lǐng)域中一個(gè)非常重要的內(nèi)容,當(dāng)然也是面試的一個(gè)考點(diǎn),不知道各位小伙伴是否真正掌握了Cookie呢?當(dāng)然沒有掌握也是沒有關(guān)系的,可以跟著小編的腳步一起來學(xué)習(xí)一下前端Cookie,沒有熟練掌握的小伙伴看完這

    2024年02月04日
    瀏覽(31)
  • 一篇文章帶你搞懂stm32工程文件

    一篇文章帶你搞懂stm32工程文件

    本文以stm32f4為例,講解stm32標(biāo)準(zhǔn)庫工程中各個(gè)文件的作用,學(xué)藝不精,如有錯(cuò)誤,望大家私信或評論指出。 先看思維導(dǎo)圖 startup_stm32f427xx.s? 該文件是stm32的啟動文件,由匯編語言編寫,主要是做stm32上電時(shí)的配置設(shè)置(如堆棧指針,時(shí)鐘數(shù))并跳轉(zhuǎn)到main函數(shù)中,執(zhí)行c代碼。

    2024年02月21日
    瀏覽(34)
  • 一篇文章帶你搞懂GIT、Github、Gitee

    一篇文章帶你搞懂GIT、Github、Gitee

    本文介紹了GIt,GitHub,Gitee的使用,與IDEA的Git配置,跟著文章來做你很快就能學(xué)會操作Git,利用其進(jìn)行版本控制與代碼托管,學(xué)習(xí)Git的使用、Git常用命令、Git分支,分支是團(tuán)隊(duì)協(xié)作的基礎(chǔ),介紹了團(tuán)隊(duì)內(nèi),外協(xié)作和Github遠(yuǎn)程倉庫的操作、使用IDEA中的Git、IDEA中GIt的使用、在I

    2023年04月19日
    瀏覽(26)
  • 一篇文章帶你徹底弄懂Java的==符號

    一篇文章帶你徹底弄懂Java的==符號

    本篇文章6735字,大概閱讀時(shí)間20分鐘。本文中使用到的JDK版本為1.8.0_301 目錄 ==符號的定義 基本類型中==符號的判斷 String類型中==符號的判斷 ? ? ? ? 在Java中==符號的作用分為兩類: ? ? ? ? 1:==符號在八種基本類型的作用是比較對應(yīng)基本類型的 數(shù)值是否相等 ? ? ? ? 2:

    2024年02月08日
    瀏覽(25)
  • 假期算法提升(一篇文章帶你徹底學(xué)會雙指針)

    假期算法提升(一篇文章帶你徹底學(xué)會雙指針)

    呀哈嘍,我是結(jié)衣。 對于要參加程序設(shè)計(jì)比賽的人來說,算法永遠(yuǎn)都是一道繞不開的坎,你必須的去了解他才可以更好的去解決問題。非形式地說,算法就是任何良地計(jì)算過程,我們可以把算法看作是用于求良說明地計(jì)算問題地工具。那么今天我們學(xué)到的就是其中最基礎(chǔ)的一

    2024年02月19日
    瀏覽(26)
  • 一篇文章帶你搞懂微信小程序的開發(fā)過程

    一篇文章帶你搞懂微信小程序的開發(fā)過程

    小程序想必大家應(yīng)該都不陌生了吧,今天小編帶大家一起來學(xué)習(xí)下微信小程序的開發(fā)過程吧。 這個(gè)不一一介紹,網(wǎng)上有教程,申請成功后打開后臺,我們找到小程序,下載微信開發(fā)者工具,如圖: 這里我們選擇普通小程序開發(fā)工具,點(diǎn)擊微信開發(fā)者工具,如圖: 然后選擇相

    2024年02月09日
    瀏覽(26)
  • 一篇文章帶你搞懂動態(tài)規(guī)劃(由暴力遞歸到動態(tài)規(guī)劃)

    一篇文章帶你搞懂動態(tài)規(guī)劃(由暴力遞歸到動態(tài)規(guī)劃)

    ”動態(tài)規(guī)劃“ 的過程相當(dāng)于 記憶化搜索 , 即在普通 遞歸 的過程中用二維數(shù)組進(jìn)行記憶化存儲一些狀態(tài)結(jié)果, 從而避免重復(fù)的計(jì)算(剪枝)。 舉一個(gè)簡單的例子:在 遞歸法 求解 斐波那契 數(shù)列的過程中, 就進(jìn)行了多次的 重復(fù)計(jì)算 , 而動態(tài)規(guī)劃相當(dāng)于是對已經(jīng)計(jì)算的狀態(tài)

    2024年02月20日
    瀏覽(24)
  • 【數(shù)據(jù)結(jié)構(gòu)】一篇文章帶你徹底學(xué)會《后綴表達(dá)式》

    【數(shù)據(jù)結(jié)構(gòu)】一篇文章帶你徹底學(xué)會《后綴表達(dá)式》

    創(chuàng)作不易,本篇文章如果幫助到了你,還請點(diǎn)贊 關(guān)注支持一下???)!! 主頁專欄有更多知識,如有疑問歡迎大家指正討論,共同進(jìn)步! ??c語言系列專欄:c語言之路重點(diǎn)知識整合 ?? 給大家跳段街舞感謝支持!? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 后綴表

    2024年02月05日
    瀏覽(36)
  • 顛覆世界的“數(shù)字孿生”到底是什么?這篇文章帶你搞懂全部內(nèi)涵!

    顛覆世界的“數(shù)字孿生”到底是什么?這篇文章帶你搞懂全部內(nèi)涵!

    在春節(jié)很火的電影《流浪地球2》中,已經(jīng)去世的小女孩圖丫丫,被她的父親重新將其個(gè)人的信息模型導(dǎo)入最強(qiáng)大的計(jì)算機(jī)而“復(fù)活”了。屏幕中的丫丫就是一個(gè)數(shù)字孿生體。我們可以看到她的一顰一笑,聽到她跟你的對話,看到她做出反應(yīng)。這就是數(shù)字孿生的另一特色,數(shù)字

    2024年02月01日
    瀏覽(43)
  • 【操作系統(tǒng)】一篇文章帶你快速搞懂用戶態(tài)和內(nèi)核態(tài)

    【操作系統(tǒng)】一篇文章帶你快速搞懂用戶態(tài)和內(nèi)核態(tài)

    目錄 一、指令劃分 二、特權(quán)級別 三、操作系統(tǒng)需要兩種CPU狀態(tài) 四、CPU狀態(tài)之間的轉(zhuǎn)換 4.1 CPU狀態(tài)轉(zhuǎn)換的途徑 4.2 CPU狀態(tài)轉(zhuǎn)化流程 4.3 什么情況會導(dǎo)致用戶態(tài)到內(nèi)核態(tài)切換 通常來說,以下三種情況會導(dǎo)致用戶態(tài)到內(nèi)核態(tài)的切換 1、系統(tǒng)調(diào)用 2、異常 3、外圍設(shè)備的中斷 五、為什

    2024年02月05日
    瀏覽(26)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包