<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          記一次線上偶現(xiàn)的Spring循環(huán)依賴問題

          共 4727字,需瀏覽 10分鐘

           ·

          2022-05-11 04:18


          點擊藍色“程序員黃小斜”關(guān)注我喲

          加個“星標”,每天和你一起多進步一點點


          前情回顧


          一探

            Spring 的循環(huán)依賴,源碼詳細分析 → 真的非要三級緩存嗎?中講到了循環(huán)依賴問題

            同樣說明了?Spring?只能解決?setter?方式的循環(huán)依賴,不能解決構(gòu)造方法的循環(huán)依賴

            重點介紹了?Spring?是如何解決?setter?方式的循環(huán)依賴,感興趣的可以去看下

          二探

            既然?Spring?不能解決構(gòu)造方法的循環(huán)依賴,那么它是如何甄別構(gòu)造方法循環(huán)依賴的了?

            所以進行了二探:再探循環(huán)依賴 → Spring 是如何判定原型循環(huán)依賴和構(gòu)造方法循環(huán)依賴的?

            從源碼的角度講述了?Spring?是如何判定構(gòu)造方法循環(huán)依賴、原型循環(huán)依賴的

            感興趣的可以去看下

            大家跟源碼的時候,一定要注意版本!!!


          項目模擬


            自認為經(jīng)過了前兩探,對?Spring?循環(huán)依賴的問題已了若指掌,可面對線上突如其來的循環(huán)依賴問題,樓主竟然沒能一眼看出來!!!

            這樓主能忍?于是樓主又跟起了?Spring?源碼,看看問題到底出在哪?

            ?SpringBoot?版本是?2.0.3.RELEASE?

            線上服務采用?k8s?部署,本地環(huán)境未采用?k8s?部署

            本地啟動從未出現(xiàn)循環(huán)依賴問題,線上環(huán)境也只是偶發(fā)的?pod?啟動失敗(提示信息直指循環(huán)依賴)

            問題偶發(fā),而非必現(xiàn),很是頭疼,但問題還是得解決,從提示信息著手唄

            根據(jù)錯誤提示信息,樓主模擬出了一個簡化的工程,方便我們進行問題排查

          ?

            非常簡單,完整地址:spring-other-circular-reference

            我們來看下類圖

            ?MyListener?、?MyService?、?MyManager?很常規(guī),特殊的是?MyConfig?和?MySender?

          問題復現(xiàn)

            如果按上述工程結(jié)構(gòu),本地很難復現(xiàn)問題 ,反正樓主是沒復現(xiàn)出來

            我們稍做調(diào)整,將?MySender?前置,如下

            啟動失敗,錯誤信息如下:

          org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myConfig': Unsatisfied dependency expressed through field 'myListener'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myListener': Unsatisfied dependency expressed through field 'myService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myServiceImpl': Unsatisfied dependency expressed through field 'myManager'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'myManager': Unsatisfied dependency expressed through field 'mySender'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'mySender': Requested bean is currently in creation: Is there an unresolvable circular reference?

            此刻的?Is there an unresolvable circular reference??讓樓主感到了陌生


          問題分析

            我們從以下幾個方面來分析

          BeanDefinition 掃描

            目前?XML?方式的?Bean?定義越來越少,除了一些遺留的老項目,基本看不到?XML?方式的?Bean?定義了

            所以我們只關(guān)注注解方式的?Bean?定義的掃描

            文件夾的掃描順序與文件夾名字的升序一致,文件的順序與文件名的升序一致,如下所示

            有興趣的可以去跟下?ConfigurationClassParser?類中?doProcessConfigurationClass?方法;樓主做了下簡單的總結(jié)

            ?@ComponentScan?的處理早于?@Bean?

            ?BeanDefinition?掃描過程中,會按掃描順序會往?DefaultListableBeanFactory?的?beanDefinitionMap?中添加?BeanDefinition?,往?beanDefinitionNames?添加?BeanName?

            我們來跟下源碼,看是不是如上所說


            先被掃描的?BeanDefinition?的?BeanName?會被先添加到?beanDefinitionNames?

          BeanDefinition 覆蓋

            ?MyConfig?中通過?@Bean?定義了?MySender?,而?MySender?類上又用了?@Component?進行修飾

            那創(chuàng)建?MySender?實例的時候到底調(diào)用的哪個構(gòu)造方法?(有參還是無參?)

            關(guān)于 Spring Boot 中創(chuàng)建對象的疑慮 → @Bean 與 @Component 同時作用同一個類,會怎么樣?從源碼的角度分析了這個問題

            結(jié)論是:?SpringBoot 2.0.3.RELEASE?中,?@Configuration + @Bean?修飾的?BeanDefinition?會覆蓋掉?@Component?修飾的?BeanDefinition?

            也就說?MySender?類上的?@Component?其實沒用,加不加效果是一樣的,這里說的?沒用效果?僅僅指的是?MySender?的?BeanDefinition?

          Bean 實例化順序

            ?BeanDefinition?用來構(gòu)建實例,那么?MySender?上的?@Component?就有作用了,它決定了?MySender?的實例化順序

            是先于?MyConfig?、?MyListener?、?MyServiceImpl?、?MyManager?實例化的

            我們來看下?Bean?的實例化順序



            理論上來講,先被掃描的?Bean?會先被實例化;?Bean?實例化的過程中會填充屬性,可能會導致后被掃描的?Bean?提前被實例化

            如果?Bean?之間沒有依賴,那么會嚴格按照?Bean?的掃描順序?qū)嵗?/p>

          再看問題

            我們再回到前面的問題

            這種情況下,我們分析下?Is there an unresolvable circular reference??是如何產(chǎn)生的

            相較于?MyConfig?、?MyListener?、?MyManager?、?MyServiceImpl?,?MySender?是最先被掃描到的,所以它最先被實例化

            因為?MyConfig?中通過?@Bean?修飾了?MySender?的?BeanDefinition?

            會覆蓋掉?MySender?自身的無參?BeanDefinition?

            所以會通過?MySender?的有參構(gòu)造方法來創(chuàng)建?MySender?實例

            因為有參構(gòu)造方法依賴?myListener?,所以去?Spring?容器中找?MyListener?實例,沒有找到則創(chuàng)建,然后填充?MyListener?實例的屬性

            以此類推,實例的創(chuàng)建過程如下所示:

            ?Is there an unresolvable circular reference??就此產(chǎn)生

            相當于是變種的構(gòu)造方法循環(huán)依賴

          最初狀態(tài)

            我們還原?MySender?位置

            此時最先實例化的是?MyConfig?,實例化過程如下

            對象是都可以正常實例化、初始化的

            這種情況理論上來講是不會出現(xiàn)?Is there an unresolvable circular reference??

          線上問題

            一通分析下來,還是沒能找到線上?Is there an unresolvable circular reference??的原因

            很是尷尬,但是我萌生了這樣的想法:是不是在?k8s?部署過程中,?BeanDefinition?的掃描會有偶發(fā)的隨機性?


          問題修復


            雖然我們沒能找到線上問題的確切原因,但還是有辦法去根治這個問題的

            ?Spring?不能處理構(gòu)造方法循環(huán)依賴,那我們就去規(guī)避它

            刪掉?MyConfig?,?MySender?改成

          ?

            或?MySender?改成

          ?

          ?  還有?@PostConstruct?等,方式有很多,只要不產(chǎn)生構(gòu)造方法循環(huán)依賴就好


          總結(jié)


            1、?BeanDefinition?掃描順序

              如果我們?nèi)ジ创a就會發(fā)現(xiàn),以啟動類為起點,掃描啟動類同級目錄下的所有文件夾?

              按文件夾名升序順序進行掃描,會遞歸掃描每個文件夾

              文件掃描也是按文件名升序順序進行

              從線上問題來看,對這個掃描順序,樓主是持懷疑態(tài)度的:是?Spring?會偶發(fā)的隨機掃描,還是?pod?會導致偶發(fā)的隨機掃描

            2、?BeanDefinition?覆蓋

              只要我們讀了源碼,了解?Spring?對各個注解的掃描順序,就清楚它們的替換關(guān)系了

              ?BeanDefinition?覆蓋并不會影響?BeanDefinition?的掃描順序

              也就是不會改變?BeanName?在?beanDefinitionNames?中的位置,即不會影響?Bean?的示例化順序

            3、?Bean?實例化順序

              理論上來講,先被掃描到的就先被實例化,但實例化過程中的屬性填充會打亂這個順序,會將被依賴的對象提前實例化

            4、?Spring?版本

          ?    一定要結(jié)合版本來看問題

              版本不同,底層實現(xiàn)可能會不同

          轉(zhuǎn)自:青石路

          鏈接:?https://www.cnblogs.com/youzhibing/p/15835048.html?

          CTO 說了,不懂 @Autowired 和 @Resource 區(qū)別的人可以領盒飯了


          國家出手,開始整治互聯(lián)網(wǎng)加班了


          京東裁員殺紅眼了!說要給n+1,員工簽字后,公司又反悔了!


          —?【 THE END 】—
          公眾號[程序員黃小斜]全部博文已整理成一個目錄,請在公眾號里回復「m」獲取!

          最近面試BAT,整理一份面試資料Java面試BATJ通關(guān)手冊,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。

          獲取方式:點“在看”,關(guān)注公眾號并回復 PDF?領取,更多內(nèi)容陸續(xù)奉上。

          文章有幫助的話,在看,轉(zhuǎn)發(fā)吧。

          謝謝支持喲 (*^__^*)

          瀏覽 82
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美日韩高清性爱在线观看 | 国产伦精品一区二区三区免费下载 | 日本一级操逼视频 | 色五月综合| 国产精品久久久久久久搜平安片 |