最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot中循環(huán)依賴的常見陷阱與解決方案

 更新時(shí)間:2025年05月21日 10:42:10   作者:悟能不能悟  
循環(huán)依賴指兩個(gè)或多個(gè)Bean相互依賴對(duì)方,形成一個(gè)閉環(huán),本文將深入探討循環(huán)依賴的根源,分析Spring的解決策略,并提供多種實(shí)戰(zhàn)解決方案,希望對(duì)大家有所幫助

引言

在Spring Boot開發(fā)中,你是否遇到過這樣的錯(cuò)誤信息?

The dependencies of some of the beans in the application context form a cycle

這表示你的應(yīng)用出現(xiàn)了循環(huán)依賴。盡管Spring框架通過巧妙的機(jī)制解決了部分循環(huán)依賴問題,但在實(shí)際開發(fā)中(尤其是使用構(gòu)造器注入時(shí)),開發(fā)者仍需警惕此類問題。本文將深入探討循環(huán)依賴的根源,分析Spring的解決策略,并提供多種實(shí)戰(zhàn)解決方案。

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

循環(huán)依賴指兩個(gè)或多個(gè)Bean相互依賴對(duì)方,形成一個(gè)閉環(huán)。例如:

  • ?Bean A? 的創(chuàng)建需要注入 ?Bean B?
  • ?Bean B? 的創(chuàng)建又需要注入 ?Bean A?

此時(shí),Spring容器在初始化Bean時(shí)會(huì)陷入“死循環(huán)”。以下是一個(gè)典型示例:

@Service
public class ServiceA {
    private final ServiceB serviceB;
    
    public ServiceA(ServiceB serviceB) { // 構(gòu)造器注入ServiceB
        this.serviceB = serviceB;
    }
}
 
@Service
public class ServiceB {
    private final ServiceA serviceA;
    
    public ServiceB(ServiceA serviceA) { // 構(gòu)造器注入ServiceA
        this.serviceA = serviceA;
    }
}

啟動(dòng)應(yīng)用時(shí),Spring會(huì)拋出異常:

BeanCurrentlyInCreationException: Error creating bean with name 'serviceA': Requested bean is currently in creation

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

Spring通過三級(jí)緩存機(jī)制解決單例Bean的循環(huán)依賴問題:

  • ?一級(jí)緩存?(singletonObjects):存放完全初始化好的Bean。
  • ?二級(jí)緩存?(earlySingletonObjects):存放提前曝光的半成品Bean(僅實(shí)例化,未填充屬性)。
  • ?三級(jí)緩存?(singletonFactories):存放Bean的工廠對(duì)象,用于生成半成品Bean。

?解決流程?(以A和B相互依賴為例):

  • 創(chuàng)建A時(shí),先實(shí)例化A(未填充屬性),并將A的工廠放入三級(jí)緩存。
  • 填充A的屬性時(shí)發(fā)現(xiàn)需要B,開始創(chuàng)建B。
  • 創(chuàng)建B時(shí),實(shí)例化B后,發(fā)現(xiàn)需要A,此時(shí)從三級(jí)緩存中通過工廠獲取A的半成品對(duì)象。
  • B完成初始化,放入一級(jí)緩存。
  • A繼續(xù)填充B的實(shí)例,完成初始化,放入一級(jí)緩存。

?關(guān)鍵限制?:該機(jī)制僅支持單例Bean且通過屬性注入的場(chǎng)景。?構(gòu)造器注入會(huì)直接失?。?/p>

三、為何構(gòu)造器注入會(huì)導(dǎo)致循環(huán)依賴失敗

構(gòu)造器注入要求Bean在實(shí)例化階段立即獲得依賴對(duì)象,而三級(jí)緩存機(jī)制需要在屬性注入階段解決依賴。因此,當(dāng)兩個(gè)Bean都使用構(gòu)造器注入時(shí),Spring無(wú)法提前曝光半成品Bean,導(dǎo)致循環(huán)依賴無(wú)法解決。

四、解決方案:打破循環(huán)依賴的四種方法

1. ?改用Setter/Field注入(謹(jǐn)慎使用)??

將構(gòu)造器注入改為Setter或字段注入,允許Spring延遲注入依賴:

@Service
public class ServiceA {
    private ServiceB serviceB;
    
    @Autowired // Setter注入
    public void setServiceB(ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}

?優(yōu)點(diǎn)?:快速解決問題。

?缺點(diǎn)?:破壞了不可變性(字段非final),且可能掩蓋設(shè)計(jì)問題。

2. ?使用@Lazy延遲加載?

在依賴對(duì)象上添加@Lazy,告知Spring延遲初始化Bean:

@Service
public class ServiceA {
    private final ServiceB serviceB;
    
    public ServiceA(@Lazy ServiceB serviceB) {
        this.serviceB = serviceB; // 實(shí)際注入的是代理對(duì)象
    }
}

?原理?:Spring生成代理對(duì)象,只有在首次調(diào)用時(shí)才會(huì)真正初始化目標(biāo)Bean。

?適用場(chǎng)景?:解決構(gòu)造函數(shù)注入的循環(huán)依賴。

3. ?重新設(shè)計(jì)代碼結(jié)構(gòu)?

通過分層或提取公共邏輯,消除循環(huán)依賴:

?方案一?:引入中間層(如ServiceC),將A和B的共同依賴轉(zhuǎn)移到C。

?方案二?:使用事件驅(qū)動(dòng)(ApplicationEvent),解耦直接依賴。

// 事件驅(qū)動(dòng)示例
@Service
public class ServiceA {
    @Autowired
    private ApplicationEventPublisher eventPublisher;
 
    public void doSomething() {
        eventPublisher.publishEvent(new EventA());
    }
}
 
@Service
public class ServiceB {
    @EventListener
    public void handleEventA(EventA event) {
        // 處理事件
    }
}

4. ?使用ObjectProvider(推薦)??

在構(gòu)造器中注入ObjectProvider,按需獲取依賴:

@Service
public class ServiceA {
    private final ServiceB serviceB;
    
    public ServiceA(ObjectProvider<ServiceB> serviceBProvider) {
        this.serviceB = serviceBProvider.getIfUnique();
    }
}

?優(yōu)點(diǎn)?:保持構(gòu)造器注入的不可變性,顯式控制依賴獲取時(shí)機(jī)。

?注意?:需確保依賴Bean存在且唯一。

五、最佳實(shí)踐與預(yù)防措施

1.?優(yōu)先使用構(gòu)造器注入?:保持Bean的不可變性和明確依賴,但需警惕循環(huán)依賴。

2.?定期檢測(cè)循環(huán)依賴?:

使用IDE插件(如IntelliJ的Circular Dependencies分析)。

通過Maven/Gradle插件(如spring-boot-dependencies-analysis)。

3.?代碼分層規(guī)范?:

嚴(yán)格遵循分層架構(gòu)(Controller → Service → Repository)。

避免同一層內(nèi)的Bean相互依賴。

4.?單元測(cè)試驗(yàn)證?:編寫集成測(cè)試,驗(yàn)證Bean的初始化過程。

@SpringBootTest
public class CircularDependencyTest {
    @Autowired
    private ApplicationContext context;
 
    @Test
    void contextLoads() {
        // 若啟動(dòng)無(wú)異常,則通過測(cè)試
        assertNotNull(context.getBean(ServiceA.class));
    }
}

六、總結(jié)

循環(huán)依賴是Spring開發(fā)中的常見陷阱,其本質(zhì)是代碼設(shè)計(jì)問題。盡管Spring提供了部分解決方案,但重構(gòu)代碼消除循環(huán)依賴才是根本之道。通過合理使用注入方式、代碼分層和工具檢測(cè),開發(fā)者可以有效避免此類問題,構(gòu)建高可維護(hù)性的應(yīng)用。

?記住?:

  • 慎用@Lazy和Setter注入,它們可能掩蓋設(shè)計(jì)缺陷。
  • 構(gòu)造器注入 + 合理分層 = 更健壯的系統(tǒng)!

到此這篇關(guān)于SpringBoot中循環(huán)依賴的常見陷阱與解決方案的文章就介紹到這了,更多相關(guān)SpringBoot循環(huán)依賴內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解Spring依賴注入的三種方式使用及優(yōu)缺點(diǎn)

    詳解Spring依賴注入的三種方式使用及優(yōu)缺點(diǎn)

    這篇文章主要介紹了spring依賴注入的三種方式的使用方法,以及優(yōu)缺點(diǎn)的介紹,通過代碼示例介紹的非常詳細(xì),感興趣的小伙伴可以參考一下
    2023-04-04
  • java遍歷Map的幾種方法分析

    java遍歷Map的幾種方法分析

    這篇文章主要介紹了java遍歷Map的幾種方法,結(jié)合實(shí)例形式分析了使用循環(huán)與迭代等方法操作Map遍歷的相關(guān)技巧,需要的朋友可以參考下
    2016-08-08
  • mybatis 解決從列名到屬性名的自動(dòng)映射失敗問題

    mybatis 解決從列名到屬性名的自動(dòng)映射失敗問題

    這篇文章主要介紹了mybatis 解決從列名到屬性名的自動(dòng)映射失敗問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • Java配置DBeaver的詳細(xì)步驟

    Java配置DBeaver的詳細(xì)步驟

    DBeaver是一個(gè)集成的數(shù)據(jù)庫(kù)客戶端工具,需要java語(yǔ)言支持,所以安裝之前需要配置JDK環(huán)境,這篇文章主要介紹了Java配置DBeaver的詳細(xì)步驟,需要的朋友可以參考下
    2021-03-03
  • SpringDataMongoDB多文檔事務(wù)的實(shí)現(xiàn)

    SpringDataMongoDB多文檔事務(wù)的實(shí)現(xiàn)

    mongodb4.0也出來一段時(shí)間了,這個(gè)版本最為大眾期待的特性就是支持了多文檔事務(wù)。這篇文章主要介紹了SpringDataMongoDB多文檔事務(wù)的實(shí)現(xiàn),感興趣的小伙伴們可以參考一下
    2018-11-11
  • Maven中怎么手動(dòng)添加jar包到本地倉(cāng)庫(kù)詳解(repository)

    Maven中怎么手動(dòng)添加jar包到本地倉(cāng)庫(kù)詳解(repository)

    這篇文章主要給大家介紹了關(guān)于Maven中怎么手動(dòng)添加jar包到本地倉(cāng)庫(kù)的相關(guān)資料,文中通過圖文以及實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2023-04-04
  • Java中的List集合初始化及常見方法解析

    Java中的List集合初始化及常見方法解析

    這篇文章主要介紹了Java中的List集合初始化及常見方法解析,List集合的特點(diǎn)是元素有序可重復(fù),只要是帶集合、數(shù)組的都叫有序,因若無(wú)序就不會(huì)存在有下標(biāo),本文來講一下List集合初始化及常見方法,需要的朋友可以參考下
    2023-10-10
  • 分享Java常用開發(fā)編輯器工具

    分享Java常用開發(fā)編輯器工具

    這篇文章主要給大家分享了分享Java常用開發(fā)編輯器工具,文章內(nèi)容介紹非常詳細(xì),具有很大的參考價(jià)值,需要的小伙伴可以參考一下,希望對(duì)你的工作或?qū)W習(xí)有一定的幫助
    2022-03-03
  • Maven實(shí)現(xiàn)項(xiàng)目構(gòu)建工具

    Maven實(shí)現(xiàn)項(xiàng)目構(gòu)建工具

    本文主要介紹了Maven實(shí)現(xiàn)項(xiàng)目構(gòu)建工具,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • spring XML配置文件標(biāo)簽詳解

    spring XML配置文件標(biāo)簽詳解

    這篇文章主要介紹了spring XML配置文件標(biāo)簽詳解,本文給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧
    2024-12-12

最新評(píng)論

自治县| 廊坊市| 松阳县| 五家渠市| 惠安县| 固安县| 中宁县| 衡东县| 灌云县| 渭源县| 湘潭县| 金沙县| 铜川市| 吉安市| 小金县| 苏尼特右旗| 西盟| 武邑县| 神池县| 铁岭市| 南丰县| 朝阳县| 柳河县| 嘉义市| 永平县| 孟连| 伊金霍洛旗| 长春市| 磴口县| 历史| 元阳县| 平罗县| 高要市| 黑水县| 大余县| 长子县| 仁布县| 禹城市| 永丰县| 北宁市| 贺州市|