日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

Spring循環(huán)依賴及解決方式

 印度阿三17 2020-03-15

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

循環(huán)依賴其實就是循環(huán)引用,也就是兩個或者兩個以上的bean互相持有對方,最終形成閉環(huán)。比如A依賴于B,B依賴于C,C又依賴于A。如下圖:注意,這里不是函數(shù)的循環(huán)調(diào)用,是對象的相互依賴關(guān)系。循環(huán)調(diào)用其實就是一個死循環(huán),除非有終結(jié)條件。 Spring中循環(huán)依賴場景有:  (1)構(gòu)造器的循環(huán)依賴  (2)field屬性的循環(huán)依賴 其中,構(gòu)造器的循環(huán)依賴問題無法解決,只能拋出BeanCurrentlyInCreationException異常,在解決屬性循環(huán)依賴時,spring采用的是提前暴露對象的方法。

2. 怎么檢測是否存在循環(huán)依賴

檢測循環(huán)依賴相對比較容易,Bean在創(chuàng)建的時候可以給該Bean打標(biāo),如果遞歸調(diào)用回來發(fā)現(xiàn)正在創(chuàng)建中的話,即說明了循環(huán)依賴了。

3. Spring怎么解決循環(huán)依賴

Spring的循環(huán)依賴的理論依據(jù)基于Java的引用傳遞,當(dāng)獲得對象的引用時,對象的屬性是可以延后設(shè)置的。(但是構(gòu)造器必須是在獲取引用之前) Spring的單例對象的初始化主要分為三步:   (1)createBeanInstance:實例化,其實也就是調(diào)用對象的構(gòu)造方法實例化對象 (2)populateBean:填充屬性,這一步主要是多bean的依賴屬性進行填充 (3)initializeBean:調(diào)用spring xml中的init 方法。 從上面單例bean的初始化可以知道:循環(huán)依賴主要發(fā)生在第一、二步,也就是構(gòu)造器循環(huán)依賴和field循環(huán)依賴。那么我們要解決循環(huán)引用也應(yīng)該從初始化過程著手,對于單例來說,在Spring容器整個生命周期內(nèi),有且只有一個對象,所以很容易想到這個對象應(yīng)該存在Cache中,Spring為了解決單例的循環(huán)依賴問題,使用了三級緩存。 這三級緩存分別指:  singletonFactories : 單例對象工廠的cache  earlySingletonObjects :提前暴光的單例對象的Cache  singletonObjects:單例對象的cache 在創(chuàng)建bean的時候,首先想到的是從cache中獲取這個單例的bean,這個緩存就是singletonObjects。如果獲取不到,并且對象正在創(chuàng)建中,就再從二級緩存earlySingletonObjects中獲取。如果還是獲取不到且允許singletonFactories通過getObject()獲取,就從三級緩存singletonFactory.getObject()(三級緩存)獲取,如果獲取到了則:從singletonFactories中移除,并放入earlySingletonObjects中。其實也就是從三級緩存移動到了二級緩存。 從上面三級緩存的分析,我們可以知道,Spring解決循環(huán)依賴的訣竅就在于singletonFactories這個三級cache。這個cache的類型是ObjectFactory。這里就是解決循環(huán)依賴的關(guān)鍵,發(fā)生在createBeanInstance之后,也就是說單例對象此時已經(jīng)被創(chuàng)建出來(調(diào)用了構(gòu)造器)。這個對象已經(jīng)被生產(chǎn)出來了,雖然還不完美(還沒有進行初始化的第二步和第三步),但是已經(jīng)能被人認(rèn)出來了(根據(jù)對象引用能定位到堆中的對象),所以Spring此時將這個對象提前曝光出來讓大家認(rèn)識,讓大家使用。 這樣做有什么好處呢?讓我們來分析一下“A的某個field或者setter依賴了B的實例對象,同時B的某個field或者setter依賴了A的實例對象”這種循環(huán)依賴的情況。A首先完成了初始化的第一步,并且將自己提前曝光到singletonFactories中,此時進行初始化的第二步,發(fā)現(xiàn)自己依賴對象B,此時就嘗試去get(B),發(fā)現(xiàn)B還沒有被create,所以走create流程,B在初始化第一步的時候發(fā)現(xiàn)自己依賴了對象A,于是嘗試get(A),嘗試一級緩存singletonObjects(肯定沒有,因為A還沒初始化完全),嘗試二級緩存earlySingletonObjects(也沒有),嘗試三級緩存singletonFactories,由于A通過ObjectFactory將自己提前曝光了,所以B能夠通過ObjectFactory.getObject拿到A對象(雖然A還沒有初始化完全,但是總比沒有好呀),B拿到A對象后順利完成了初始化階段1、2、3,完全初始化之后將自己放入到一級緩存singletonObjects中。此時返回A中,A此時能拿到B的對象順利完成自己的初始化階段2、3,最終A也完成了初始化,進去了一級緩存singletonObjects中,而且更加幸運的是,由于B拿到了A的對象引用,所以B現(xiàn)在hold住的A對象完成了初始化。 知道了這個原理時候,肯定就知道為啥Spring不能解決“A的構(gòu)造方法中依賴了B的實例對象,同時B的構(gòu)造方法中依賴了A的實例對象”這類問題了!因為加入singletonFactories三級緩存的前提是執(zhí)行了構(gòu)造器,所以構(gòu)造器的循環(huán)依賴沒法解決。

4.基于構(gòu)造器的循環(huán)依賴

Spring容器會將每一個正在創(chuàng)建的Bean 標(biāo)識符放在一個“當(dāng)前創(chuàng)建Bean池”中,Bean標(biāo)識符在創(chuàng)建過程中將一直保持在這個池中,因此如果在創(chuàng)建Bean過程中發(fā)現(xiàn)自己已經(jīng)在“當(dāng)前創(chuàng)建Bean池”里時將拋出BeanCurrentlyInCreationException異常表示循環(huán)依賴;而對于創(chuàng)建完畢的Bean將從“當(dāng)前創(chuàng)建Bean池”中清除掉。 Spring容器先創(chuàng)建單例A,A依賴B,然后將A放在“當(dāng)前創(chuàng)建Bean池”中,此時創(chuàng)建B,B依賴C ,然后將B放在“當(dāng)前創(chuàng)建Bean池”中,此時創(chuàng)建C,C又依賴A, 但是,此時A已經(jīng)在池中,所以會報錯,,因為在池中的Bean都是未初始化完的,所以會依賴錯誤 ,(初始化完的Bean會從池中移除)

5.基于setter屬性的循環(huán)依賴

我們結(jié)合上面那張圖看,Spring先是用構(gòu)造實例化Bean對象 ,創(chuàng)建成功后,Spring會通過以下代碼提前將對象暴露出來,此時的對象A還沒有完成屬性注入,屬于早期對象,此時Spring會將這個實例化結(jié)束的對象放到一個Map中,并且Spring提供了獲取這個未設(shè)置屬性的實例化對象引用的方法。 結(jié)合我們的實例來看,當(dāng)Spring實例化了A、B、C后,緊接著會去設(shè)置對象的屬性,此時A依賴B,就會去Map中取出存在里面的單例B對象,以此類推,不會出來循環(huán)的問題嘍

6.結(jié)束語

不要使用基于構(gòu)造函數(shù)的依賴注入,可以通過以下方式解決: 1.在字段上使用@Autowired注解,讓Spring決定在合適的時機注入 2.用基于setter方法的依賴注入。   參考文章:https://blog.csdn.net/chejinqiang/article/details/80003868來源:https://www./content-4-659801.html

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多