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

SpringBoot中依賴(lài)循環(huán)問(wèn)題的產(chǎn)生與解決方案

 更新時(shí)間:2025年12月01日 10:11:26   作者:保加利亞的風(fēng)  
在使用SpringBoot開(kāi)發(fā)項(xiàng)目時(shí),我們經(jīng)常會(huì)遇到一個(gè)令人頭疼的問(wèn)題——依賴(lài)循環(huán),當(dāng)兩個(gè)或多個(gè)Bean相互依賴(lài),形成閉環(huán)時(shí),Spring容器就無(wú)法完成它們的初始化,本文將通過(guò)一個(gè)具體案例,深入剖析依賴(lài)循環(huán)是如何產(chǎn)生的,并提供多種實(shí)用的解決方案,需要的朋友可以參考下

引言

在使用 Spring Boot 開(kāi)發(fā)項(xiàng)目時(shí),我們經(jīng)常會(huì)遇到一個(gè)令人頭疼的問(wèn)題——依賴(lài)循環(huán)(Circular Dependency)。當(dāng)兩個(gè)或多個(gè) Bean 相互依賴(lài),形成閉環(huán)時(shí),Spring 容器就無(wú)法完成它們的初始化,從而拋出 BeanCurrentlyInCreationException 異常。

本文將通過(guò)一個(gè)具體案例,深入剖析依賴(lài)循環(huán)是如何產(chǎn)生的,并提供多種實(shí)用的解決方案,幫助你徹底掌握這一常見(jiàn)問(wèn)題的應(yīng)對(duì)之道。

一、什么是依賴(lài)循環(huán)?

依賴(lài)循環(huán)是指兩個(gè)或多個(gè) Spring Bean 在構(gòu)造函數(shù)或字段注入時(shí)互相依賴(lài),形成一個(gè)閉環(huán)。例如:

  • A 依賴(lài) B
  • B 依賴(lài) A
    這種情況下,Spring 容器在創(chuàng)建 A 時(shí)需要先創(chuàng)建 B,而創(chuàng)建 B 又需要先創(chuàng)建 A,陷入死循環(huán)。

二、依賴(lài)循環(huán)的典型場(chǎng)景(案例)

場(chǎng)景描述

假設(shè)我們正在開(kāi)發(fā)一個(gè)用戶(hù)服務(wù)系統(tǒng),有兩個(gè)核心組件:

  • UserService:負(fù)責(zé)用戶(hù)相關(guān)業(yè)務(wù)邏輯
  • OrderService:負(fù)責(zé)訂單處理,其中需要驗(yàn)證用戶(hù)信息
    兩者相互調(diào)用對(duì)方的方法,導(dǎo)致循環(huán)依賴(lài)。

代碼示例

@Service
public class UserService {

    private final OrderService orderService;

    public UserService(OrderService orderService) {
        this.orderService = orderService;
    }

    public void handleUser() {
        System.out.println("Handling user...");
        orderService.checkOrders();
    }
}
@Service
public class OrderService {

    private final UserService userService;

    public OrderService(UserService userService) {
        this.userService = userService;
    }

    public void checkOrders() {
        System.out.println("Checking orders...");
        userService.handleUser(); // 實(shí)際中可能不是直接調(diào)用,但存在依賴(lài)關(guān)系
    }
}

啟動(dòng)報(bào)錯(cuò)

當(dāng)你啟動(dòng) Spring Boot 應(yīng)用時(shí),會(huì)看到類(lèi)似如下錯(cuò)誤:

Error creating bean with name 'userService': Requested bean is currently in creation: Is there an unresolvable circular reference?

這是因?yàn)?Spring 默認(rèn)使用構(gòu)造器注入(Constructor Injection),而構(gòu)造器注入無(wú)法處理循環(huán)依賴(lài)(僅支持 setter 或 field 注入的三級(jí)緩存機(jī)制)。

三、為什么會(huì)出現(xiàn)依賴(lài)循環(huán)?

根本原因在于設(shè)計(jì)層面的耦合度過(guò)高。兩個(gè)本應(yīng)職責(zé)分離的服務(wù),卻互相持有對(duì)方的引用,違反了“單一職責(zé)原則”和“依賴(lài)倒置原則”。

雖然 Spring 框架在某些情況下(如 setter 注入)可以通過(guò)三級(jí)緩存機(jī)制解決循環(huán)依賴(lài),但構(gòu)造器注入 + 循環(huán)依賴(lài) = 必然失敗。

注意:Spring 僅支持單例 Bean 的 setter/field 注入下的循環(huán)依賴(lài),不支持原型(Prototype)Bean 或構(gòu)造器注入的循環(huán)依賴(lài)。

四、解決方案詳解

方案一:重構(gòu)代碼,消除循環(huán)依賴(lài)(推薦)

這是最根本、最優(yōu)雅的解決方式。

思路:

  • 提取公共邏輯到第三個(gè)服務(wù)
  • 使用事件驅(qū)動(dòng)(ApplicationEvent)
  • 通過(guò)接口解耦

示例:引入 UserValidator 服務(wù)

@Service
public class UserValidator {
    public boolean isValid(Long userId) {
        // 驗(yàn)證邏輯
        return true;
    }
}

@Service
public class UserService {
    private final UserValidator userValidator;

    public UserService(UserValidator userValidator) {
        this.userValidator = userValidator;
    }

    public void handleUser() {
        System.out.println("Handling user...");
    }
}

@Service
public class OrderService {
    private final UserValidator userValidator;

    public OrderService(UserValidator userValidator) {
        this.userValidator = userValidator;
    }

    public void checkOrders() {
        if (userValidator.isValid(1L)) {
            System.out.println("Orders are valid.");
        }
    }
}

優(yōu)點(diǎn):職責(zé)清晰,無(wú)循環(huán),易于測(cè)試和維護(hù)。

方案二:使用 @Lazy 延遲加載(快速修復(fù))

在其中一個(gè)注入點(diǎn)上添加 @Lazy 注解,讓 Spring 在實(shí)際使用時(shí)才初始化該 Bean。

@Service
public class UserService {

    private final OrderService orderService;

    public UserService(@Lazy OrderService orderService) {
        this.orderService = orderService;
    }

    // ...
}

Spring 會(huì)在創(chuàng)建 UserService 時(shí)注入一個(gè)代理對(duì)象,真正調(diào)用方法時(shí)才初始化 OrderService。

注意:這只是“繞過(guò)”問(wèn)題,并未真正解決設(shè)計(jì)缺陷,僅適用于臨時(shí)修復(fù)。

方案三:改用 Setter 或 Field 注入(不推薦)

將構(gòu)造器注入改為 @Autowired 字段注入:

@Service
public class UserService {

    @Autowired
    private OrderService orderService;

    // ...
}

Spring 利用三級(jí)緩存(singletonObjects、earlySingletonObjects、singletonFactories)可以在這種情況下完成注入。

缺點(diǎn):

  • 破壞了不可變性
  • 不利于單元測(cè)試
  • 違背 Spring 官方推薦的“優(yōu)先使用構(gòu)造器注入”原則

官方文檔明確建議:盡可能使用構(gòu)造器注入,因?yàn)樗鼙WC依賴(lài)不為 null,且對(duì)象狀態(tài)完整。

方案四:使用 ApplicationContext 手動(dòng)獲取 Bean(極端情況)

@Component
public class ServiceLocator implements ApplicationContextAware {

    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        ServiceLocator.context = applicationContext;
    }

    public static <T> T getBean(Class<T> clazz) {
        return context.getBean(clazz);
    }
}

// 在 UserService 中
public void someMethod() {
    OrderService orderService = ServiceLocator.getBean(OrderService.class);
    orderService.doSomething();
}

此方法破壞了 IoC 原則,應(yīng)盡量避免。

五、總結(jié)

方案是否推薦說(shuō)明
重構(gòu)代碼(提取公共邏輯)? 強(qiáng)烈推薦從根源解決問(wèn)題,提升架構(gòu)質(zhì)量
使用 @Lazy?? 謹(jǐn)慎使用快速修復(fù),但掩蓋設(shè)計(jì)問(wèn)題
改用字段注入? 不推薦違背最佳實(shí)踐,降低代碼質(zhì)量
手動(dòng)獲取 Bean? 不推薦破壞依賴(lài)注入原則

最佳實(shí)踐建議:

  • 優(yōu)先使用構(gòu)造器注入
  • 避免服務(wù)之間直接相互依賴(lài)
  • 通過(guò)領(lǐng)域事件、消息隊(duì)列或中介服務(wù)解耦
  • 定期審查 Bean 依賴(lài)圖(可使用 spring-boot-starter-actuator 的 /beans 端點(diǎn))

六、結(jié)語(yǔ)

依賴(lài)循環(huán)是 Spring 開(kāi)發(fā)中的常見(jiàn)陷阱,但它也是一面鏡子,反映出我們代碼設(shè)計(jì)中的耦合問(wèn)題。與其尋找“繞過(guò)”的技巧,不如借此機(jī)會(huì)優(yōu)化架構(gòu),寫(xiě)出更清晰、更健壯的代碼。

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

相關(guān)文章

  • SpringBoot中@Import注解的使用方式

    SpringBoot中@Import注解的使用方式

    這篇文章主要介紹了SpringBoot中@Import注解的使用方式,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-05-05
  • SpringBoot中Tomcat和SpringMVC整合源碼分析

    SpringBoot中Tomcat和SpringMVC整合源碼分析

    Tomcat和SpringMVC都是通過(guò)這樣的方式進(jìn)行集成的,SpringBoot出現(xiàn)之前SpringMVC項(xiàng)目是直接部署在Tomcat服務(wù)器中的,這篇文章主要介紹了SpringBoot中Tomcat和SpringMVC整合源碼分析,需要的朋友可以參考下
    2022-07-07
  • SpringBoot+SpringSession+Redis實(shí)現(xiàn)session共享及唯一登錄示例

    SpringBoot+SpringSession+Redis實(shí)現(xiàn)session共享及唯一登錄示例

    這篇文章主要介紹了SpringBoot+SpringSession+Redis實(shí)現(xiàn)session共享及唯一登錄示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • java Lombok之@Accessors用法及說(shuō)明

    java Lombok之@Accessors用法及說(shuō)明

    這篇文章主要介紹了java Lombok之@Accessors用法及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • MyBatis如何使用selectKey返回主鍵的值

    MyBatis如何使用selectKey返回主鍵的值

    這篇文章主要介紹了MyBatis如何使用selectKey返回主鍵的值,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • sky-take-out項(xiàng)目中Redis的使用示例詳解

    sky-take-out項(xiàng)目中Redis的使用示例詳解

    SpringCache是Spring的緩存抽象層,通過(guò)注解簡(jiǎn)化緩存管理,支持Redis等提供者,適用于方法結(jié)果緩存、更新和刪除操作,但無(wú)法實(shí)現(xiàn)Redis的高級(jí)功能(如數(shù)據(jù)結(jié)構(gòu)、事務(wù)、分布式鎖),本文給大家介紹sky-take-out項(xiàng)目中Redis的使用,感興趣的朋友一起看看吧
    2025-07-07
  • 關(guān)于ArrayList初始化容量的問(wèn)題

    關(guān)于ArrayList初始化容量的問(wèn)題

    這篇文章主要介紹了關(guān)于ArrayList初始化容量的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • java截取字符串中的指定字符的兩種方法(以base64圖片為例)

    java截取字符串中的指定字符的兩種方法(以base64圖片為例)

    本文介紹了使用Java截取字符串中指定字符的方法,通過(guò)substring索引和正則實(shí)現(xiàn),文章詳細(xì)介紹了實(shí)現(xiàn)步驟和示例代碼,對(duì)于想要了解如何使用Java截取字符串指定字符的讀者具有一定的參考價(jià)值
    2023-08-08
  • 詳談Java幾種線程池類(lèi)型介紹及使用方法

    詳談Java幾種線程池類(lèi)型介紹及使用方法

    下面小編就為大家?guī)?lái)一篇詳談Java幾種線程池類(lèi)型介紹及使用方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-03-03
  • Java實(shí)現(xiàn)對(duì)華北、華南、華東和華中四個(gè)區(qū)域的劃分

    Java實(shí)現(xiàn)對(duì)華北、華南、華東和華中四個(gè)區(qū)域的劃分

    在Java中,通過(guò)定義枚舉類(lèi)、編寫(xiě)主程序和進(jìn)行測(cè)試,本文詳細(xì)介紹了如何劃分華北、華南、華東和華中四個(gè)區(qū)域,首先定義枚舉類(lèi)標(biāo)識(shí)區(qū)域,然后通過(guò)主程序接收用戶(hù)輸入并返回相應(yīng)區(qū)域,最后通過(guò)測(cè)試用例確保正確性,文章還介紹了甘特圖和餅狀圖的使用
    2024-09-09

最新評(píng)論

桐梓县| 济宁市| 乌什县| 县级市| 西峡县| 和平县| 玉树县| 北宁市| 恩施市| 新乡县| 时尚| 乐东| 汪清县| 铜鼓县| 都昌县| 霸州市| 平定县| 南川市| 延庆县| 华安县| 涟源市| 舒兰市| 南丹县| 永川市| 夏津县| 东阳市| 永寿县| 涡阳县| 南昌市| 略阳县| 新郑市| 灵丘县| 开封市| 防城港市| 汤阴县| 盐城市| 竹山县| 鸡东县| 内乡县| 河曲县| 色达县|