RM新时代网站-首页

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

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

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

關(guān)于Spring的循環(huán)依賴問(wèn)題

Android編程精選 ? 來(lái)源:博客園 ? 作者:青石路 ? 2022-06-14 17:21 ? 次閱讀

前情回顧

一探

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

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

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

二探

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

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

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

感興趣的可以去看下

大家跟源碼的時(shí)候,一定要注意版本?。。?/p>

項(xiàng)目模擬

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

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

SpringBoot版本是2.0.3.RELEASE

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

本地啟動(dòng)從未出現(xiàn)循環(huán)依賴問(wèn)題,線上環(huán)境也只是偶發(fā)的pod啟動(dòng)失?。ㄌ崾?a target="_blank">信息直指循環(huán)依賴)

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

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

eefcb9ae-d443-11ec-bce3-dac502259ad0.png

非常簡(jiǎn)單,完整地址:spring-other-circular-reference

我們來(lái)看下類圖

ef226186-d443-11ec-bce3-dac502259ad0.png

MyListenerMyService、MyManager很常規(guī),特殊的是MyConfigMySender

ef484bda-d443-11ec-bce3-dac502259ad0.png

ef60c5e8-d443-11ec-bce3-dac502259ad0.png

問(wèn)題復(fù)現(xiàn)

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

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

ef9f542a-d443-11ec-bce3-dac502259ad0.gif

啟動(dòng)失敗,錯(cuò)誤信息如下:

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?讓樓主感到了陌生

問(wèn)題分析

我們從以下幾個(gè)方面來(lái)分析

BeanDefinition 掃描

目前XML方式的Bean定義越來(lái)越少,除了一些遺留的老項(xiàng)目,基本看不到XML方式的Bean定義了

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

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

f06ca5ce-d443-11ec-bce3-dac502259ad0.png

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

f0849e04-d443-11ec-bce3-dac502259ad0.png

@ComponentScan的處理早于@Bean

BeanDefinition掃描過(guò)程中,會(huì)按掃描順序會(huì)往DefaultListableBeanFactorybeanDefinitionMap中添加BeanDefinition,往beanDefinitionNames添加BeanName

我們來(lái)跟下源碼,看是不是如上所說(shuō)

f0c444c8-d443-11ec-bce3-dac502259ad0.gif

先被掃描的BeanDefinitionBeanName會(huì)被先添加到beanDefinitionNames

BeanDefinition 覆蓋

MyConfig中通過(guò)@Bean定義了MySender,而MySender類上又用了@Component進(jìn)行修飾

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

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

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

也就說(shuō)MySender類上的@Component其實(shí)沒(méi)用,加不加效果是一樣的,這里說(shuō)的沒(méi)用效果僅僅指的是MySenderBeanDefinition

Bean 實(shí)例化順序

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

是先于MyConfigMyListener、MyServiceImpl、MyManager實(shí)例化的

我們來(lái)看下Bean的實(shí)例化順序

f164c63c-d443-11ec-bce3-dac502259ad0.gif

理論上來(lái)講,先被掃描的Bean會(huì)先被實(shí)例化;Bean實(shí)例化的過(guò)程中會(huì)填充屬性,可能會(huì)導(dǎo)致后被掃描的Bean提前被實(shí)例化

如果Bean之間沒(méi)有依賴,那么會(huì)嚴(yán)格按照Bean的掃描順序?qū)嵗?/p>

再看問(wèn)題

我們?cè)倩氐角懊娴膯?wèn)題

f20bec64-d443-11ec-bce3-dac502259ad0.png

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

相較于MyConfigMyListenerMyManager、MyServiceImpl,MySender是最先被掃描到的,所以它最先被實(shí)例化

因?yàn)?span style="margin-top:5px;margin-bottom:5px;padding:3px;background-color:rgb(245,245,245);border-width:1px;border-style:solid;border-color:rgb(204,204,204);color:rgb(0,0,0);font-family:'Courier New';font-size:12px;">MyConfig中通過(guò)@Bean修飾了MySenderBeanDefinition

f28332ba-d443-11ec-bce3-dac502259ad0.png

會(huì)覆蓋掉MySender自身的無(wú)參BeanDefinition

所以會(huì)通過(guò)MySender的有參構(gòu)造方法來(lái)創(chuàng)建MySender實(shí)例

因?yàn)橛袇?gòu)造方法依賴myListener,所以去Spring容器中找MyListener實(shí)例,沒(méi)有找到則創(chuàng)建,然后填充MyListener實(shí)例的屬性

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

f2d5de3e-d443-11ec-bce3-dac502259ad0.png

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

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

最初狀態(tài)

我們還原MySender位置

f2f71892-d443-11ec-bce3-dac502259ad0.png

此時(shí)最先實(shí)例化的是MyConfig,實(shí)例化過(guò)程如下

f320f982-d443-11ec-bce3-dac502259ad0.png

對(duì)象是都可以正常實(shí)例化、初始化的

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

線上問(wèn)題

一通分析下來(lái),還是沒(méi)能找到線上Is there an unresolvable circular reference?的原因

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

問(wèn)題修復(fù)

雖然我們沒(méi)能找到線上問(wèn)題的確切原因,但還是有辦法去根治這個(gè)問(wèn)題的

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

刪掉MyConfig,MySender改成

f34d389e-d443-11ec-bce3-dac502259ad0.png

MySender改成

f3695b96-d443-11ec-bce3-dac502259ad0.png

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

總結(jié)

1、BeanDefinition掃描順序

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

按文件夾名升序順序進(jìn)行掃描,會(huì)遞歸掃描每個(gè)文件夾

文件掃描也是按文件名升序順序進(jìn)行

從線上問(wèn)題來(lái)看,對(duì)這個(gè)掃描順序,樓主是持懷疑態(tài)度的:是Spring會(huì)偶發(fā)的隨機(jī)掃描,還是pod會(huì)導(dǎo)致偶發(fā)的隨機(jī)掃描

2、BeanDefinition覆蓋

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

BeanDefinition覆蓋并不會(huì)影響BeanDefinition的掃描順序

也就是不會(huì)改變BeanNamebeanDefinitionNames中的位置,即不會(huì)影響Bean的示例化順序

3、Bean實(shí)例化順序

理論上來(lái)講,先被掃描到的就先被實(shí)例化,但實(shí)例化過(guò)程中的屬性填充會(huì)打亂這個(gè)順序,會(huì)將被依賴的對(duì)象提前實(shí)例化

4、Spring版本

?一定要結(jié)合版本來(lái)看問(wèn)題

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

原文標(biāo)題:記一次線上偶現(xiàn)的Spring循環(huán)依賴問(wèn)題

文章出處:【微信公眾號(hào):Android編程精選】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

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

    關(guān)注

    8

    文章

    639

    瀏覽量

    29185
  • 循環(huán)
    +關(guān)注

    關(guān)注

    0

    文章

    92

    瀏覽量

    15971
  • spring
    +關(guān)注

    關(guān)注

    0

    文章

    340

    瀏覽量

    14338

原文標(biāo)題:記一次線上偶現(xiàn)的Spring循環(huán)依賴問(wèn)題

文章出處:【微信號(hào):AndroidPush,微信公眾號(hào):Android編程精選】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    java spring教程

    Spring核心概念介紹控制反轉(zhuǎn)(IOC)依賴注入(DI)集合對(duì)象注入等Bean的管理BeanFactoryApplicationContextSpring 在web中的使用
    發(fā)表于 09-11 11:09

    什么是java spring

    。在SSH項(xiàng)目中管理事務(wù)以及對(duì)象的注入Spring是非侵入式的:基于Spring開(kāi)發(fā)的系統(tǒng)中的對(duì)象一般不依賴Spring的類。組成 Spring
    發(fā)表于 09-11 11:16

    Spring工作原理

    依賴關(guān)系核心:bean工廠;在Spring中,bean工廠創(chuàng)建的各個(gè)實(shí)例稱作bean二.AOP(Aspect-Oriented Programming): 面向方面編程1.代理的兩種方式:靜態(tài)代理
    發(fā)表于 07-10 07:41

    Spring筆記分享

    ; 可以管理所有的組件(類)Spring的優(yōu)良特性1) 非侵入式:基于Spring開(kāi)發(fā)的應(yīng)用中的對(duì)象可以不依賴Spring的API2) 依賴
    發(fā)表于 11-04 07:51

    spring教程ppt

    主要內(nèi)容Spring 概述Spring 整體結(jié)構(gòu)Spring實(shí)例Spring核心概念介紹控制反轉(zhuǎn)(IOC)依賴注入(DI)
    發(fā)表于 09-11 11:00 ?138次下載
    <b class='flag-5'>spring</b>教程ppt

    Spring認(rèn)證」什么是Spring GraphQL?

    spring-boot-starter-webflux HTTP、WebSocket 彈簧 WebFlux 依賴{ ? ?實(shí)現(xiàn) 'org.springframework.experimental
    的頭像 發(fā)表于 08-10 14:08 ?814次閱讀
    「<b class='flag-5'>Spring</b>認(rèn)證」什么是<b class='flag-5'>Spring</b> GraphQL?

    Spring開(kāi)發(fā)過(guò)程中依賴注入的幾個(gè)知識(shí)點(diǎn)

    轉(zhuǎn)自丨h(huán)ttps://juejin.cn/post/6844904056230690824 本章的內(nèi)容主要是想探討我們?cè)谶M(jìn)行 Spring 開(kāi)發(fā)過(guò)程當(dāng)中,關(guān)于依賴注入的幾個(gè)知識(shí)點(diǎn)。感興趣的讀者可以

    Spring Validation的使用

    之前也寫(xiě)過(guò)一篇關(guān)于Spring Validation使用的文章,不過(guò)自我感覺(jué)還是浮于表面,本次打算徹底搞懂Spring Validation。本文會(huì)詳細(xì)介紹Spring Validat
    的頭像 發(fā)表于 09-08 10:31 ?884次閱讀

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

    參考圖中 spring 解決循環(huán)依賴 的過(guò)程可知,spring 利用三級(jí)緩中的 objectFactory 生成并返回一個(gè) early 對(duì)象,提前暴露這個(gè) early 地址,供其他對(duì)象
    的頭像 發(fā)表于 12-22 10:34 ?531次閱讀

    怎樣使用Kiuwan保護(hù)Spring Boot應(yīng)用程序呢?

    Spring Boot 提供了快速輕松地構(gòu)建基于Spring 的應(yīng)用程序所需的工具、功能和依賴項(xiàng)。
    的頭像 發(fā)表于 03-16 09:10 ?781次閱讀

    SpringBoot循環(huán)依賴的癥狀和解決方案

    循環(huán)依賴是指在Spring Boot 應(yīng)用程序中,兩個(gè)或多個(gè)類之間存在彼此依賴的情況,形成一個(gè)循環(huán)依賴
    的頭像 發(fā)表于 05-06 15:30 ?819次閱讀

    Spring Boot的啟動(dòng)原理

    可能很多初學(xué)者會(huì)比較困惑,Spring Boot 是如何做到將應(yīng)用代碼和所有的依賴打包成一個(gè)獨(dú)立的 Jar 包,因?yàn)閭鹘y(tǒng)的 Java 項(xiàng)目打包成 Jar 包之后,需要通過(guò) -classpath 屬性
    的頭像 發(fā)表于 10-13 11:44 ?643次閱讀
    <b class='flag-5'>Spring</b> Boot的啟動(dòng)原理

    Spring Boot 的設(shè)計(jì)目標(biāo)

    ,這樣我們就可以盡快的上手。 使用 Spring Boot 來(lái)不僅可以創(chuàng)建基于 war 方式部署的傳統(tǒng)Java應(yīng)用程序,也可以通過(guò)創(chuàng)建獨(dú)立的不依賴任何容器(如 tomcat 等)
    的頭像 發(fā)表于 10-13 14:56 ?580次閱讀
    <b class='flag-5'>Spring</b> Boot 的設(shè)計(jì)目標(biāo)

    Spring依賴注入的方式

    Spring 是一個(gè)開(kāi)源的輕量級(jí)框架,可以用于構(gòu)建企業(yè)級(jí)應(yīng)用程序。其最重要的特性之一是依賴注入(Dependency Injection,DI),這是一種設(shè)計(jì)模式,它可以幫助我們解耦代碼、提高
    的頭像 發(fā)表于 11-22 15:12 ?487次閱讀

    Spring依賴注入的四種方式

    Spring框架中,依賴注入是一種核心的概念和機(jī)制。通過(guò)依賴注入,我們可以讓對(duì)象之間的依賴關(guān)系更加松散,并且能夠方便地進(jìn)行單元測(cè)試和模塊化開(kāi)發(fā)。在
    的頭像 發(fā)表于 12-03 15:11 ?1959次閱讀
    RM新时代网站-首页