在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

從源碼層面深度剖析Spring循環(huán)依賴

OSC開源社區(qū) ? 來源:OSCHINA 社區(qū) ? 2022-12-22 10:34 ? 次閱讀

以下舉例皆針對單例模式討論圖解參考:https://www.processon.com/view/link/60e3b0ae0e3e74200e2478ce?

1、Spring 如何創(chuàng)建 Bean?

對于單例 Bean 來說,在 Spring 容器整個(gè)生命周期內(nèi),有且只有一個(gè)對象。Spring 在創(chuàng)建 Bean 過程中,使用到了三級緩存,即 DefaultSingletonBeanRegistry.java 中定義的:
    /** Cache of singleton objects: bean name to bean instance. */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    /** Cache of singleton factories: bean name to ObjectFactory. */
    private final Map<String, ObjectFactory> singletonFactories = new HashMap<>(16);

    /** Cache of early singleton objects: bean name to bean instance. */
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

以 com.gyh.general 包下的 OneBean 為例,debug springboot 啟動過程,分析 spring 是如何創(chuàng)建 bean 的。

參考圖中spring 創(chuàng)建 bean的過程。其中最關(guān)鍵的幾步有:1.getSingleton(beanName, true)依次從一二三級緩存中查找 bean 對象,如果緩存中存在對象,則直接返回 (early);2.createBeanInstance(beanName, mbd, args)選一個(gè)合適的構(gòu)造函數(shù),new 實(shí)例對象 (instance),此時(shí)的 instance 中依賴的屬性還都是 null,屬于半成品;3.singletonFactories.put(beanName, oneSingletonFactory)利用上一步的 instance,構(gòu)建一個(gè) singletonFactory,并將其放到三級緩存中;4.populateBean(beanName, mbd, instanceWrapper)填充 bean:為該 bean 定義的屬性創(chuàng)建對象或賦值;5.initializeBean("one",oneInstance, mbd)初始化 bean:對 bean 進(jìn)行初始化或其他加工,如生成代理對象 (proxy);6.getSingleton(beanName, false)依次在一二級緩存中查找,檢查是否有因循環(huán)依賴導(dǎo)致提前生成的對象,有的話與初始化后的對象是否一致;

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

以 com.gyh.circular.threeCache 包下的 OneBean 和 TwoBean 為例 ,兩個(gè) Bean 相互依賴(即形成閉環(huán))。參考圖中spring 解決循環(huán)依賴的過程可知,spring 利用三級緩中的 objectFactory 生成并返回一個(gè) early 對象,提前暴露這個(gè) early 地址,供其他對象依賴注入使用,以此解決循環(huán)依賴問題。

3、Spring 不能解決哪些循環(huán)依賴?

3.1 循環(huán)中使用了@Async注解

3.1.1 為什么循環(huán)中使用了@Async 會報(bào)錯(cuò)?

以 com.gyh.circular.async 包下的 OneBean 和 TwoBean 為例,兩個(gè) bean 相互依賴,且 oneBean 中的方法使用了@Async 注解,此時(shí)啟動 spring 失敗,報(bào)錯(cuò)信息為:org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a.one': Bean with name 'a.one' has been injected into other beans [a.two] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean. This is often the result of over-eager type matching - consider using 'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.并通過 debug 代碼,發(fā)現(xiàn)報(bào)錯(cuò)位置在 AbstractAutowireCapableBeanFactory#doCreateBean 方法內(nèi),由于 earlySingletonReference != null 且 exposedObject != bean,導(dǎo)致報(bào)錯(cuò)。5a2ee708-813f-11ed-8abf-dac502259ad0.png結(jié)合流程圖中spring 解決循環(huán)依賴及上述圖片中可知:1.行 1 中 bean 為 createBeanInstance 創(chuàng)建的實(shí)例 (address1)2.行 2 中 exposedObject 為 initializeBean 后生成的代理對象 (address2)3.行 3 中 earlySingletonReference 為 getEarlyBeanReference 時(shí)創(chuàng)建的對象【此處地址同 bean (address1)】深層原因?yàn)椋合惹?TwoBean 在 populateBean 時(shí)已經(jīng)依賴了地址為 address1 的 earlySingletonReference 對象,而此時(shí) OneBean 經(jīng)過 initializeBean 之后,返回了地址為 address2 的新對象,導(dǎo)致 spring 不知道哪個(gè)才是最終版的 bean,所以報(bào)錯(cuò)。earlySingletonReference 是如何生成的,參考 getSingleton ("one", true) 過程。

3.1.2 循環(huán)中使用了@Async 一定會報(bào)錯(cuò)嗎?

依然以 com.gyh.circular.async 包下的 OneBean 和 TwoBean 為例,兩個(gè) bean 相互依賴,使 TwoBean (非 OneBean) 中的方法使用了@Async 注解,此時(shí)啟動 spring 成功,并未報(bào)錯(cuò)。debug 代碼可知:雖然 TwoBean 使用了 @Async 注解,但其 earlySingletonReference = null; 故不會引起報(bào)錯(cuò)。5a6698f6-813f-11ed-8abf-dac502259ad0.png深層原因?yàn)椋?/span>OneBean 先被創(chuàng)建,TwoBean 后創(chuàng)建,再整條鏈路中,并未在三級緩存中查找過 TwoBean 的 objectFactory 。(OneBean 在創(chuàng)建過程中,被找過兩次,即 one-> two ->one;TwoBean 的創(chuàng)建過程中,只找過它一次,即 two ->one。)由此可得:@Async 造成循環(huán)依賴報(bào)錯(cuò)的先約條件為:1.循環(huán)依賴中的 Bean 使用了 @Async 注解2.且這個(gè) Bean,比循環(huán)內(nèi)其他 Bean 先創(chuàng)建。3.注:一個(gè) Bean 可能會同時(shí)存在于多個(gè)循環(huán)內(nèi);只要存在它是某個(gè)循環(huán)內(nèi)第一個(gè)被創(chuàng)建的 Bean,那么就會報(bào)錯(cuò)。

3.1.3 為什么循環(huán)中使用了 @Transactional 不會報(bào)錯(cuò)?

已知使用了 @Transactional 注解的 Bean,Spring 也會為其生成代理對象,但為什么這種 Bean 在循環(huán)里時(shí)不會產(chǎn)生報(bào)錯(cuò)呢?以 com.gyh.circular.transactional 包下的 OneBean 和 TwoBean 為例,兩個(gè) Bean 相互依賴,且 OneBean 中的方法使用了 @Transactional 注解,啟動 Spring 成功,并不會報(bào)錯(cuò)。debug 代碼可知,生成 OneBean 過程中,雖然 earlySingletonReference != null,但 initializeBean 之后的 exposedObject 和 原始實(shí)例的地址相同(即 initializeBean 步驟中,并未對實(shí)例生成代理),所以不會產(chǎn)生報(bào)錯(cuò)。5ad3f7c0-813f-11ed-8abf-dac502259ad0.png3.1.4 為什么同樣是代理會產(chǎn)生兩種不同的現(xiàn)象?同樣是生成代理對象,同樣是參與到循環(huán)依賴中,會產(chǎn)生不同現(xiàn)象的原因是:當(dāng)他們處在循環(huán)依賴中時(shí),生成代理的節(jié)點(diǎn)不同:1.@Transactional 在 getEarlyBeanReference 時(shí)生成代理,提前暴露出代理之后的地址(即最終地址);2.@Async 在 initializeBean 時(shí)生成代理,導(dǎo)致提前暴露出去的地址不是最終地址,造成報(bào)錯(cuò)。為什么 @Async 不能在 getEarlyBeanReference 時(shí)生成代理呢?對比下兩者執(zhí)行的代碼過程發(fā)現(xiàn):兩者都是在 AbstractAutoProxyCreator#getEarlyBeanReference 的方法對原始實(shí)例對象進(jìn)行包裝,如下圖5afa0d48-813f-11ed-8abf-dac502259ad0.png使用 @Transactional 的 Bean 在 create proxy 時(shí),獲取到一個(gè) advice ,隨即生成了代理對象 proxy.?5b3be77c-813f-11ed-8abf-dac502259ad0.png而使用 @Async 的 Bean 在 create proxy 時(shí),沒有獲取到 advice,不能被代理.5b6c1d7a-813f-11ed-8abf-dac502259ad0.png

3.1.5 為什么 @Async 在 getEarlyBeanReference 時(shí)不能返回一個(gè) advice?

在 AbstractAutoProxyCreator#getAdvicesAndAdvisorsForBean 方法內(nèi),其主要做的事情有:1.找到當(dāng)前 spring 容器中所有的 Advisor2.返回適配當(dāng)前 bean 的所有 Advisor第一步返回的 Advisor 有 BeanFactoryCacheOperationSourceAdvisor 和 BeanFactoryTransactionAttributeSourceAdvisor,并無處理 Async 相關(guān)的 Advisor.刨根問底,追查為什么第一步不會返回處理 Async 相關(guān)的 Advisor?已知使用 @Async @Transactional @Cacheable 需要提前進(jìn)行開啟,即提前標(biāo)注 @EnableAsync、@EnableTransactionManagement、@EnableCaching 。以 @EnableTransactionManagement、@EnableCaching 為例,在其注解定義中,引入了 Selector 類,Selector 中又引入了 Configuration 類,在 Configuration 類中,創(chuàng)建了對應(yīng) Advisor 并放到了 spring 容器中,所以第一步才能得到這兩個(gè) Advisor.而 @EnableAsync 的定義中引入的 Configuration 類,創(chuàng)建的是 AsyncAnnotationBeanPostProcessor 并非一個(gè) Advisor,所以第一步不會得到它,所以 @Async 的 bean 不會在這一步被代理。

3.2 構(gòu)造函數(shù)引起的循環(huán)依賴

以 com.gyh.circular.constructor 包下的 OneBean 和 TwoBean 為例,兩個(gè)類的構(gòu)造函數(shù)中各自依賴對方,啟動 spring,報(bào)錯(cuò):org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'c.one': Requested bean is currently in creation: Is there an unresolvable circular reference?debug 代碼可知,兩個(gè) bean 在根據(jù)構(gòu)造函數(shù) new instance 時(shí),就已經(jīng)陷入的死循環(huán),無法提前暴露可用的地址,所以只能報(bào)錯(cuò)。

4、如何解決以上循環(huán)依賴報(bào)錯(cuò)?

1.不用 @Async,將需要異步操作的方法,放到線程池中執(zhí)行。(推薦)2.提出 @Async 標(biāo)注的方法。(推薦)3.將使用 @Async 的方法提出到單獨(dú)的類中,該類只做異步處理,不做其他業(yè)務(wù)依賴,即避免形成循環(huán)依賴,從而解決報(bào)錯(cuò)問題。參考 com.gyh.circular.async.extract 包。4.盡量不使用構(gòu)造函數(shù)依賴對象。(推薦)5.破壞循環(huán)(不推薦)即不形成閉環(huán),在開發(fā)之前,規(guī)劃好對象依賴,方法調(diào)用鏈,盡量做到不使用循環(huán)依賴。(較難,隨著迭代開發(fā)不斷變化,很可能產(chǎn)生循環(huán))6.破壞創(chuàng)建順序(不推薦)7.由于使用 @Async 注解的所在類,比循環(huán)依賴內(nèi)其他類先創(chuàng)建時(shí)才會報(bào)錯(cuò),那么想辦法使該類不先于其他類先創(chuàng)建,也可解決該問題,如:@DependsOn、@Lazy

審核編輯 :李倩


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 源碼
    +關(guān)注

    關(guān)注

    8

    文章

    668

    瀏覽量

    30203
  • spring
    +關(guān)注

    關(guān)注

    0

    文章

    340

    瀏覽量

    14928

原文標(biāo)題:從源碼層面深度剖析 Spring 循環(huán)依賴

文章出處:【微信號:OSC開源社區(qū),微信公眾號:OSC開源社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦
    熱點(diǎn)推薦

    接口到架構(gòu):工控一體機(jī)定制化的深度技術(shù)剖析

    在工業(yè)4.0與數(shù)字化轉(zhuǎn)型的浪潮中,工控一體機(jī)作為工業(yè)自動化與信息化融合的核心載體,正通過深度定制化技術(shù)重構(gòu)工業(yè)控制系統(tǒng)的底層邏輯。硬件接口的靈活配置到系統(tǒng)架構(gòu)的模塊化設(shè)計(jì),定制化已成為解決復(fù)雜
    的頭像 發(fā)表于 06-17 16:47 ?91次閱讀

    BNC 連接器接線原理深度剖析

    使用和維護(hù)相關(guān)設(shè)備、保障系統(tǒng)的穩(wěn)定運(yùn)行至關(guān)重要。接下來,本文將從德索工程師的專業(yè)視角出發(fā),為大家詳細(xì)剖析 BNC 連接器結(jié)構(gòu)到信號傳輸?shù)脑怼?/div>
    的頭像 發(fā)表于 02-28 08:45 ?502次閱讀
    BNC 連接器接線原理<b class='flag-5'>深度</b><b class='flag-5'>剖析</b>

    深入剖析半導(dǎo)體濕法刻蝕過程中殘留物形成的機(jī)理

    半導(dǎo)體濕法刻蝕過程中殘留物的形成,其背后的機(jī)制涵蓋了化學(xué)反應(yīng)、表面交互作用以及側(cè)壁防護(hù)等多個(gè)層面,下面是對這些機(jī)制的深入剖析: 化學(xué)反應(yīng)層面 1 刻蝕劑與半導(dǎo)體材料的交互:濕法刻蝕技術(shù)依賴
    的頭像 發(fā)表于 01-08 16:57 ?860次閱讀

    剖析 400V 變 660V 升壓變壓器助力光伏發(fā)電

    利器。 原理層面深度剖析,這款變壓器依托電磁感應(yīng)原理運(yùn)行。其核心部件鐵芯,選用高導(dǎo)磁率的優(yōu)質(zhì)硅鋼片疊成,有效減少磁滯與渦流損耗,為高效的能量轉(zhuǎn)換筑牢根基。當(dāng) 400V 交流電接入初級
    的頭像 發(fā)表于 12-27 10:23 ?441次閱讀
    <b class='flag-5'>剖析</b> 400V 變 660V 升壓變壓器助力光伏發(fā)電

    校園點(diǎn)餐訂餐外賣跑腿Java源碼

    ://start.spring.io/)來生成一個(gè)基礎(chǔ)的Spring Boot項(xiàng)目,選擇以下依賴: - Spring Web - Spring
    的頭像 發(fā)表于 12-24 14:55 ?555次閱讀
    校園點(diǎn)餐訂餐外賣跑腿Java<b class='flag-5'>源碼</b>

    SSM框架的源碼解析與理解

    的核心是控制反轉(zhuǎn)(IoC)和面向切面編程(AOP)。 源碼解析: Spring源碼主要分為以下幾個(gè)部分: Bean容器: 負(fù)責(zé)實(shí)例化、配置和組裝對象。核心接口是 B
    的頭像 發(fā)表于 12-17 09:20 ?916次閱讀

    Spring事務(wù)實(shí)現(xiàn)原理

    作者:京東零售 范錫軍 1、引言 springspring-tx模塊提供了對事務(wù)管理支持,使用spring事務(wù)可以讓我們復(fù)雜的事務(wù)處理中得到解脫,無需要去處理獲得連接、關(guān)閉連接、事
    的頭像 發(fā)表于 11-08 10:10 ?1130次閱讀
    <b class='flag-5'>Spring</b>事務(wù)實(shí)現(xiàn)原理

    安全加速新選擇:深度剖析境外SOCKS5代理的優(yōu)勢與應(yīng)用

    在數(shù)字化時(shí)代,跨境網(wǎng)絡(luò)訪問已成為日常生活和工作中不可或缺的一部分。然而,網(wǎng)絡(luò)限制、延遲和安全問題常常阻礙著我們的順暢體驗(yàn)。境外SOCKS5代理作為一種高效、安全的網(wǎng)絡(luò)解決方案,正逐漸成為許多用戶的新選擇。以下是對其優(yōu)勢與應(yīng)用的深度剖析
    的頭像 發(fā)表于 09-25 08:12 ?524次閱讀

    設(shè)計(jì)到實(shí)施:樓宇自控系統(tǒng)建設(shè)流程的深度剖析

    設(shè)計(jì)到實(shí)施:樓宇自控系統(tǒng)建設(shè)流程的深度剖析 在探索現(xiàn)代建筑智能化的征途中,樓宇自控系統(tǒng)(BAS)無疑是引領(lǐng)變革的關(guān)鍵力量。它不僅深刻改變了建筑的管理模式,還極大地提升了建筑的運(yùn)營效率與居住體驗(yàn)。
    的頭像 發(fā)表于 08-27 16:37 ?557次閱讀

    Spring Cloud Gateway網(wǎng)關(guān)框架

    Spring Cloud Gateway網(wǎng)關(guān)框架 本軟件微服務(wù)架構(gòu)中采用Spring Cloud Gateway網(wǎng)關(guān)控制框架,Spring Cloud Gateway是Spring C
    的頭像 發(fā)表于 08-22 09:58 ?696次閱讀
    <b class='flag-5'>Spring</b> Cloud Gateway網(wǎng)關(guān)框架

    MEMS 可編程振蕩器的卓越代表:SiT9121 系列(1 to 220 MHZ)深度剖析

    MEMS 可編程振蕩器的卓越代表:SiT9121 系列(1 to 220 MHZ)深度剖析
    的頭像 發(fā)表于 08-13 10:56 ?946次閱讀
    MEMS 可編程振蕩器的卓越代表:SiT9121 系列(1 to 220 MHZ)<b class='flag-5'>深度</b><b class='flag-5'>剖析</b>

    探索巔峰性能 | 迅為RK3588開發(fā)板深度剖析

    探索巔峰性能 | 迅為RK3588開發(fā)板深度剖析
    的頭像 發(fā)表于 08-12 14:07 ?1507次閱讀
    探索巔峰性能 | 迅為RK3588開發(fā)板<b class='flag-5'>深度</b><b class='flag-5'>剖析</b>

    表面貼裝低相位噪音晶體振蕩器 DSO531SHH 深度剖析

    表面貼裝低相位噪音晶體振蕩器 DSO531SHH 深度剖析
    的頭像 發(fā)表于 07-26 14:12 ?660次閱讀
    表面貼裝低相位噪音晶體振蕩器 DSO531SHH <b class='flag-5'>深度</b><b class='flag-5'>剖析</b>

    Adafruit Huzzah無法深度睡眠中醒來怎么辦?

    我有一個(gè)問題,Huzzah 沒有深度睡眠中醒來。 GPIO16 跳線到 Reset 引腳,GPIO0 和 GPIO2 都有 10k 上拉電阻到 V3.3。 如果我使用重置按鈕重置它,我會
    發(fā)表于 07-19 15:04

    玩轉(zhuǎn)Spring狀態(tài)機(jī)

    說起Spring狀態(tài)機(jī),大家很容易聯(lián)想到這個(gè)狀態(tài)機(jī)和設(shè)計(jì)模式中狀態(tài)模式的區(qū)別是啥呢?沒錯(cuò),Spring狀態(tài)機(jī)就是狀態(tài)模式的一種實(shí)現(xiàn),在介紹Spring狀態(tài)機(jī)之前,讓我們來看看設(shè)計(jì)模式中的狀態(tài)模式
    的頭像 發(fā)表于 06-25 14:21 ?1284次閱讀
    玩轉(zhuǎn)<b class='flag-5'>Spring</b>狀態(tài)機(jī)
    主站蜘蛛池模板: 大量喷潮free | 丁香5月婷婷 | 女人张腿让男桶免费视频观看 | 国产yw.8825.c免费 | 中文字幕卡二和卡三的视频 | 欧美一级黄色片 | 韩国最新三级网站在线播放 | 亚洲hh| 人人做人人澡人人人爽 | 丁香婷婷色综合 | 午夜影视在线 | 四虎国产在线观看 | 久久香蕉国产精品一区二区三 | 五月婷综合 | 在线a网| va天堂| 日本级毛片免费观看 | 国产亚洲精品仙踪林在线播放 | 夜夜春宵翁熄性放纵古代 | 亚洲最新视频 | 亚洲视频一 | 国产色吧 | 一区二区三区四区在线不卡高清 | 成人欧美精品大91在线 | 亚洲青草视频 | 国产经典三级 | 黄网站视频 | 福利在线看片 | 久久精品99无色码中文字幕 | 五月天婷婷综合 | 99久在线| 欧美不卡一区 | 男女视频在线观看免费 | 日韩免费三级 | 美女免费视频一区二区三区 | 欧亚精品卡一卡二卡三 | 五月天丁香婷 | 精品国产三级a∨在线 | 日日日干干干 | 色综合成人网 | 手机看片福利盒子久久青 |