成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Spring源碼分析:Spring的循環(huán)依賴分析

Cheng_Gang / 3448人閱讀

摘要:我們知道為我們完全實例化好一個一定會經(jīng)過一下三步實例化,其實也就是調(diào)用對象的構(gòu)造方法實例化對象。循環(huán)依賴的產(chǎn)生定會發(fā)生在步驟和中,因為是利用構(gòu)造方法,是利用屬性賦值。

引言

基于Spring5+

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

循環(huán)依賴有幾種?

Spring可以解決哪幾種,為什么不能解決這幾種?

Spring是如何判斷存在循環(huán)依賴的?

什么是循環(huán)依賴?
什么是循環(huán)依賴?我們都知道Spring最大的作用就是來替我們管理Bean的,當然也包括Bean的創(chuàng)建以及整個生命周期,但是有這么一種情況,假設(shè)有三個類A、B、C需要交給Spring來管理,但A實例的創(chuàng)建需要先有B實例,而B實例的創(chuàng)建需要先有C實例,C實例的創(chuàng)建需要先有A實例,這樣三個類就自然形成了一個環(huán)狀結(jié)構(gòu),如果用代碼來表示,如下:
public class TestA {
    TestB testB;
    get;
    set;
}

public class TestB {
    TestC testC;
    get;
    set;
}

public class TestC {
    TestA testA;
    get;
    set;
}
這樣,三個類就彼此形成了一個環(huán)狀,那么Spring是如何來處理這樣的狀況呢?
循環(huán)依賴有幾種?
有三種情況:

基于構(gòu)造方法的循環(huán)依賴

基于setter構(gòu)造的循環(huán)依賴(網(wǎng)上也叫field屬性依賴)

基于prototype范圍的依賴

Spring可以解決哪些循環(huán)依賴,為什么?

首先說一下結(jié)論:除了第二種Spring可以幫我們解決,其它兩種都不能解決。我們知道Spring為我們完全實例化好一個Bean一定會經(jīng)過一下三步:

createBeanInstance:實例化,其實也就是調(diào)用對象的構(gòu)造方法實例化對象。

populateBean:填充屬性,這一步主要是多bean的依賴屬性進行填充。

initializeBean:調(diào)用默認的或者自定義的init方法。

循環(huán)依賴的產(chǎn)生定會發(fā)生在步驟1和2中,因為1是利用構(gòu)造方法,2是利用屬性賦值。

基于構(gòu)造方法的循環(huán)依賴
先說結(jié)論基于構(gòu)造器的循環(huán)依賴Spring是無法解決的,是因為沒有加入提前曝光的集合中,加入集合的條件是已經(jīng)創(chuàng)建了Bean的包裝對象,而構(gòu)造注入的時候,并沒有完成對象的創(chuàng)建,下面會有代碼說明。

測試用例:

xml文件:


    



    



    

測試類:

/**
 * description:測試通過有參構(gòu)造方式注入產(chǎn)生的循環(huán)依賴問題
 * @author 70KG
 * @date 2018/12/21
 */
public class Test02 {

    @Test
    public void m1() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("test02.xml");
    }

}

分析上面代碼:

Spring容器創(chuàng)建testA的Bean實例,首先去"當前創(chuàng)建Bean池",查找是否當前Bean正在創(chuàng)建,如果沒發(fā)現(xiàn),則繼續(xù)準備其需要的構(gòu)造器參數(shù)testB,并將testA標識符放到"當前創(chuàng)建Bean池"。

Spring容器創(chuàng)建testB的Bean實例,首先去"當前創(chuàng)建Bean池",查找是否當前Bean正在創(chuàng)建,如果沒發(fā)現(xiàn),則繼續(xù)準備其需要的構(gòu)造器參數(shù)testC,并將testB標識符放到"當前創(chuàng)建Bean池"。

Spring容器創(chuàng)建testC的Bean實例,首先去"當前創(chuàng)建Bean池",查找是否當前Bean正在創(chuàng)建,如果沒發(fā)現(xiàn),則繼續(xù)準備其需要的構(gòu)造器參數(shù)testA,并將testC標識符放到"當前創(chuàng)建Bean池"。

到此為止Spring容器要去創(chuàng)建testA,但發(fā)現(xiàn)該Bean的標志符在"當前創(chuàng)建Bean池"中,表示了循環(huán)依賴,于是拋出BeanCurrentlyInCreationException異常。

其中"當前創(chuàng)建Bean池"就是一個Set集合,DefaultSingletonBeanRegistry類中beforeSingletonCreation方法,代碼如下:

protected void beforeSingletonCreation(String beanName) {
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
    }
}
然后我們來到創(chuàng)建Bean實例的地方:

AbstractAutowireCapableBeanFactory類的543行,通過這個方法返回一個這個Bean的包裝對象:

--> instanceWrapper = createBeanInstance(beanName, mbd, args);----> 進入這個方法

--> AbstractAutowireCapableBeanFactory類的1129行

// Need to determine the constructor...
// 需要確定構(gòu)造函數(shù),也就是說構(gòu)造方法的循環(huán)依賴會在這兒return
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.
// 無需特殊處理,僅使用無參構(gòu)造即可,setter的循環(huán)依賴會在這個地方return
return instantiateBean(beanName, mbd);

在上面代碼中返回Bean的包裝對象下面緊接著才是將這個對象曝光,也就是加入到SingletonFactory集合中,所以構(gòu)造方法的循環(huán)引用,Spring是無法解決的,來到AbstractAutowireCapableBeanFactory的574行。

addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
基于setter構(gòu)造的循環(huán)依賴
首先說結(jié)論:Spring是可以為我們解決這樣的依賴的,原理說白了就是用了緩存處理,也就是常說的提前曝光,為什么叫提前曝光呢?因為這個緩存中的Bean是一個還未進行賦值的Bean,僅僅是一個引用而已。

xml文件:


    



    



    

測試類:

/**
 * description:通過setter注入產(chǎn)生的循環(huán)依賴問題
 * @author 70KG
 */
public class Test03 {
    @Test
    public void m1() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("test03.xml");
    }
}

代碼分析:

Spring容器創(chuàng)建單例"loopA",首先根據(jù)無參構(gòu)造創(chuàng)建Bean,并暴露到Map(singletonFactories)中,并將"loopA"標志符放到當前創(chuàng)建正在創(chuàng)建的Bean池(singletonsCurrentlyInCreation)中,然后進行setter注入"loopB"。

Spring容器創(chuàng)建單例"loopB",首先根據(jù)無參構(gòu)造創(chuàng)建Bean,并暴露到Map(singletonFactories)中,并將"loopA"標志符放到當前創(chuàng)建正在創(chuàng)建的Bean池(singletonsCurrentlyInCreation)中,然后進行setter注入"loopC"。

Spring容器創(chuàng)建單例"loopC",首先根據(jù)無參構(gòu)造創(chuàng)建Bean,并暴露到Map(singletonFactories)中,并將"loopA"標志符放到當前創(chuàng)建正在創(chuàng)建的Bean池(singletonsCurrentlyInCreation)中,然后進行setter注入"loopA"。在注入"loopA"的時候,由于提前暴露在singletonFactories集合中了,利用它就可以取到"loopA"正在創(chuàng)建的Bean對象。

最后依賴注入"testB","testA",完成setter注入。

查看控制臺輸出日志:

// 正在創(chuàng)建testA對象
Creating shared instance of singleton bean "testA"
Creating instance of bean "testA"
// 在緩存早期引用,目的是防止循環(huán)引用問題
Eagerly caching bean "testA" to allow for resolving potential circular references
Creating shared instance of singleton bean "testB"
Creating instance of bean "testB"
Eagerly caching bean "testB" to allow for resolving potential circular references
Creating shared instance of singleton bean "testC"
Creating instance of bean "testC"
Eagerly caching bean "testC" to allow for resolving potential circular references
// 在創(chuàng)建testC的時候會去緩存中拿原來存儲的testA,并返回,但此時的testA是一個不完全的對象,也就是尚未初始化
Returning eagerly cached instance of singleton bean "testA" that is not fully initialized yet - a consequence of a circular reference
// 緊接著完成C的創(chuàng)建,順便其它的也完成了
Finished creating instance of bean "testC"
Finished creating instance of bean "testB"
Finished creating instance of bean "testA"
Returning cached instance of singleton bean "testB"
Returning cached instance of singleton bean "testC"

基于setter的循環(huán)依賴利用了提前曝光機制,這一步的關(guān)鍵代碼,在AbstractAutowireCapableBeanFactory的574行,代碼如下:

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

在加入SingletonFactory的前提是此Bean已經(jīng)創(chuàng)建出來,才能夠加入到這個Map集合中,也就是提前曝光,可以讓別的Bean在初始化的時候從中拿到。否則是沒有機會加入到Map中的。

基于prototype范圍的依賴
首先說結(jié)論,對于多例情況下的循環(huán)依賴,是無法解決的,因為Spring容器不進行緩存,更無法提前暴露。

測試用例:

xml文件:


    



    



    

測試類:

/**
 * description:通過setter注入產(chǎn)生的循環(huán)依賴問題
 * @author 70KG
 */
public class Test03 {

    @Test
    public void m1() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("test03.xml");
        LoopA loopA = context.getBean(LoopA.class);
        System.out.println(loopA);
    }

}

會拋出BeanCurrentlyInCreationException異常。

Spring是如何檢測循環(huán)依賴

來到AbstractBeanFactory的246行,代碼如下:

Object sharedInstance = getSingleton(beanName);

這一步是從緩存中獲取以前創(chuàng)建的實例,如果發(fā)現(xiàn)存在,那么就存在循環(huán)依賴。

到此,全文完,自我感覺比其他的整理還算詳細,如有疑問,請留言。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/73177.html

相關(guān)文章

  • Spring IOC 容器源碼分析 - 循環(huán)依賴解決辦法

    摘要:實例化時,發(fā)現(xiàn)又依賴于。一些緩存的介紹在進行源碼分析前,我們先來看一組緩存的定義??墒强赐暝创a后,我們似乎仍然不知道這些源碼是如何解決循環(huán)依賴問題的。 1. 簡介 本文,我們來看一下 Spring 是如何解決循環(huán)依賴問題的。在本篇文章中,我會首先向大家介紹一下什么是循環(huán)依賴。然后,進入源碼分析階段。為了更好的說明 Spring 解決循環(huán)依賴的辦法,我將會從獲取 bean 的方法getB...

    aikin 評論0 收藏0
  • Spring IOC 容器源碼分析 - 獲取單例 bean

    摘要:簡介為了寫容器源碼分析系列的文章,我特地寫了一篇容器的導讀文章。在做完必要的準備工作后,從本文開始,正式開始進入源碼分析的階段。從緩存中獲取單例。返回以上就是和兩個方法的分析。 1. 簡介 為了寫 Spring IOC 容器源碼分析系列的文章,我特地寫了一篇 Spring IOC 容器的導讀文章。在導讀一文中,我介紹了 Spring 的一些特性以及閱讀 Spring 源碼的一些建議。在...

    lufficc 評論0 收藏0
  • Spring IOC 容器源碼分析系列文章導讀

    摘要:本文是容器源碼分析系列文章的第一篇文章,將會著重介紹的一些使用方法和特性,為后續(xù)的源碼分析文章做鋪墊。我們可以通過這兩個別名獲取到這個實例,比如下面的測試代碼測試結(jié)果如下本小節(jié),我們來了解一下這個特性。 1. 簡介 Spring 是一個輕量級的企業(yè)級應(yīng)用開發(fā)框架,于 2004 年由 Rod Johnson 發(fā)布了 1.0 版本。經(jīng)過十幾年的迭代,現(xiàn)在的 Spring 框架已經(jīng)非常成熟了...

    NSFish 評論0 收藏0
  • Spring IOC 容器源碼分析 - 創(chuàng)建單例 bean 過程

    摘要:關(guān)于創(chuàng)建實例的過程,我將會分幾篇文章進行分析。源碼分析創(chuàng)建實例的入口在正式分析方法前,我們先來看看方法是在哪里被調(diào)用的。時,表明方法不存在,此時拋出異常。該變量用于表示是否提前暴露單例,用于解決循環(huán)依賴。 1. 簡介 在上一篇文章中,我比較詳細的分析了獲取 bean 的方法,也就是getBean(String)的實現(xiàn)邏輯。對于已實例化好的單例 bean,getBean(String) ...

    mochixuan 評論0 收藏0
  • Spring-bean幾種循環(huán)依賴方式

    摘要:如下圖注意,這里不是函數(shù)的循環(huán)調(diào)用,是對象的相互依賴關(guān)系。因此如果在創(chuàng)建過程中發(fā)現(xiàn)自己已經(jīng)在當前創(chuàng)建池里時將拋出異常表示循環(huán)依賴而對于創(chuàng)建完畢的將從當前創(chuàng)建池中清除掉。 showImg(https://segmentfault.com/img/bVbs5kw?w=339&h=193); 什么是循環(huán)依賴? 循環(huán)依賴其實就是循環(huán)引用,也就是兩個或則兩個以上的bean互相持有對方,最終形成閉...

    notebin 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<