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

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

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

引言

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

本文將通過一個具體案例,深入剖析依賴循環(huán)是如何產(chǎn)生的,并提供多種實用的解決方案,幫助你徹底掌握這一常見問題的應對之道。

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

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

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

二、依賴循環(huán)的典型場景(案例)

場景描述

假設我們正在開發(fā)一個用戶服務系統(tǒng),有兩個核心組件:

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

代碼示例

@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(); // 實際中可能不是直接調(diào)用,但存在依賴關(guān)系
    }
}

啟動報錯

當你啟動 Spring Boot 應用時,會看到類似如下錯誤:

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

這是因為 Spring 默認使用構(gòu)造器注入(Constructor Injection),而構(gòu)造器注入無法處理循環(huán)依賴(僅支持 setter 或 field 注入的三級緩存機制)。

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

根本原因在于設計層面的耦合度過高。兩個本應職責分離的服務,卻互相持有對方的引用,違反了“單一職責原則”和“依賴倒置原則”。

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

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

四、解決方案詳解

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

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

思路:

  • 提取公共邏輯到第三個服務
  • 使用事件驅(qū)動(ApplicationEvent)
  • 通過接口解耦

示例:引入 UserValidator 服務

@Service
public class UserValidator {
    public boolean isValid(Long userId) {
        // 驗證邏輯
        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)點:職責清晰,無循環(huán),易于測試和維護。

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

在其中一個注入點上添加 @Lazy 注解,讓 Spring 在實際使用時才初始化該 Bean。

@Service
public class UserService {

    private final OrderService orderService;

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

    // ...
}

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

注意:這只是“繞過”問題,并未真正解決設計缺陷,僅適用于臨時修復。

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

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

@Service
public class UserService {

    @Autowired
    private OrderService orderService;

    // ...
}

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

缺點:

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

官方文檔明確建議:盡可能使用構(gòu)造器注入,因為它能保證依賴不為 null,且對象狀態(tài)完整。

方案四:使用 ApplicationContext 手動獲取 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 原則,應盡量避免。

五、總結(jié)

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

最佳實踐建議:

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

六、結(jié)語

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

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

相關(guān)文章

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

    SpringBoot中@Import注解的使用方式

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

    SpringBoot中Tomcat和SpringMVC整合源碼分析

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

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

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

    java Lombok之@Accessors用法及說明

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

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

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

    sky-take-out項目中Redis的使用示例詳解

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

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

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

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

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

    詳談Java幾種線程池類型介紹及使用方法

    下面小編就為大家?guī)硪黄斦凧ava幾種線程池類型介紹及使用方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-03-03
  • Java實現(xiàn)對華北、華南、華東和華中四個區(qū)域的劃分

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

    在Java中,通過定義枚舉類、編寫主程序和進行測試,本文詳細介紹了如何劃分華北、華南、華東和華中四個區(qū)域,首先定義枚舉類標識區(qū)域,然后通過主程序接收用戶輸入并返回相應區(qū)域,最后通過測試用例確保正確性,文章還介紹了甘特圖和餅狀圖的使用
    2024-09-09

最新評論

抚松县| 合肥市| 建宁县| 二手房| 会东县| 克拉玛依市| 慈溪市| 乐至县| 泰来县| 宜良县| 永安市| 文山县| 太仓市| 凤凰县| 子长县| 崇州市| 富平县| 南木林县| 徐闻县| 临泽县| 永登县| 扬中市| 沅江市| 海林市| 孙吴县| 昔阳县| 荥经县| 清流县| 夹江县| 体育| 乐都县| 英山县| 东莞市| 呼玛县| 云龙县| 泰兴市| 沁阳市| 外汇| 南川市| 乾安县| 太康县|