Spring Boot 2.6.0+ 循環依賴問題及解決方案

Spring Boot 2.6.0+ 循環依賴問題及解決方案

目錄

  • 背景
  • 解決方案
    • 1. 配置文件開啟循環依賴(侵入性最低,臨時方案)
    • 2. @Lazy 延遲注入(侵入性低,推薦優先嘗試)
    • 3. 手動從容器獲取(ApplicationContextAware,侵入性中等)
    • 4. 接口隔離 / 中間層解耦(侵入性高,推薦長期方案)
    • 5. 事件驅動(ApplicationEvent,侵入性中等,適合通知場景)
  • 總結
  • 版本差異補充
    在這里插入圖片描述

背景

從 Spring Boot 2.6.0 開始,Spring 團隊默認禁止了循環依賴(circular references),這是一個重要的設計變更。主要原因包括:

  1. 設計原則:循環依賴通常暗示著不良的代碼設計,違反了單一職責原則
  2. 初始化問題:循環依賴可能導致難以預測的bean初始化順序和狀態
  3. 調試困難:循環依賴使得問題排查變得復雜
  4. 性能考慮:三級緩存機制增加了額外的內存開銷

配置變更

spring:main:allow-circular-references: false  # 2.6+ 默認值

常見錯誤信息

The dependencies of some of the beans in the application context form a cycle:
┌─────┐
|  dictionaryServiceImpl defined in file [...DictionaryServiceImpl.class]
↑     ↓
|  dictionaryDataServiceImpl defined in file [...DictionaryDataServiceImpl.class]
└─────┘

解決方案

1. 配置文件開啟循環依賴(侵入性最低,臨時方案)

適用場景:快速解決遺留項目的啟動問題,臨時過渡方案

# application.yml
spring:main:allow-circular-references: true

優點

  • 零代碼修改
  • 立即生效
  • 保持原有業務邏輯不變

缺點

  • 治標不治本
  • 可能隱藏潛在的設計問題
  • 不符合Spring新版本的設計理念

2. @Lazy 延遲注入(侵入性低,推薦優先嘗試)

適用場景:簡單的雙向依賴,不需要在初始化時立即使用依賴對象

實現方式

@Service
public class DictionaryDataServiceImpl implements DictionaryDataService {@Lazy  // 延遲加載,打破循環依賴@Resourceprivate DictionaryService dictionaryService;public void someMethod() {// 只有在真正調用時才會初始化 dictionaryServiceDictionary dict = dictionaryService.getById(1);}
}@Service
public class DictionaryServiceImpl implements DictionaryService {@Resourceprivate DictionaryDataService dictionaryDataService; // 保持正常注入// 業務邏輯...
}

工作原理

  • @Lazy 注入的是一個代理對象,而非真實的bean
  • 只有在第一次調用方法時,才會觸發真實bean的創建
  • 從而避開了啟動時的循環依賴檢查

優點

  • 代碼侵入性小
  • 保持了依賴注入的便利性
  • 符合Spring的設計理念

缺點

  • 首次調用時性能略有損失
  • 需要明確哪個依賴使用@Lazy

3. 手動從容器獲取(ApplicationContextAware,侵入性中等)

適用場景:需要更靈活的依賴獲取方式,或者依賴關系比較復雜

實現方式一:ApplicationContextAware接口

@Service
public class DictionaryDataServiceImpl implements DictionaryDataService, ApplicationContextAware {private ApplicationContext applicationContext;private DictionaryService dictionaryService;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) {this.applicationContext = applicationContext;}private DictionaryService getDictionaryService() {if (dictionaryService == null) {dictionaryService = applicationContext.getBean(DictionaryService.class);}return dictionaryService;}public void someMethod() {Dictionary dict = getDictionaryService().getById(1);}
}

實現方式二:SpringContextHolder工具類

// 工具類
@Component
public class SpringContextHolder implements ApplicationContextAware {private static ApplicationContext context;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) {context = applicationContext;}public static <T> T getBean(Class<T> beanClass) {return context.getBean(beanClass);}public static <T> T getBean(String beanName, Class<T> beanClass) {return context.getBean(beanName, beanClass);}
}// 業務類使用
@Service
public class DictionaryDataServiceImpl implements DictionaryDataService {public void someMethod() {DictionaryService dictionaryService = SpringContextHolder.getBean(DictionaryService.class);Dictionary dict = dictionaryService.getById(1);}
}

優點

  • 完全避免了循環依賴
  • 可以動態獲取bean
  • 適合復雜的依賴場景

缺點

  • 失去了依賴注入的便利性
  • 代碼可讀性稍差
  • 增加了與Spring框架的耦合

4. 接口隔離 / 中間層解耦(侵入性高,推薦長期方案)

適用場景:重構現有架構,從根本上解決循環依賴問題

方案一:提取公共服務

// 提取公共邏輯到新的服務
@Service
public class DictionaryCommonService {@Resourceprivate DictionaryMapper dictionaryMapper;@Resource private DictionaryDataMapper dictionaryDataMapper;public Dictionary findDictionaryById(Integer id) {return dictionaryMapper.selectById(id);}public List<DictionaryData> findDatasByDictId(Integer dictId) {return dictionaryDataMapper.selectByDictId(dictId);}
}// 重構后的服務
@Service
public class DictionaryServiceImpl implements DictionaryService {@Resourceprivate DictionaryCommonService dictionaryCommonService;@Overridepublic JsonResult articleTypeList() {// 使用公共服務Dictionary dict = dictionaryCommonService.findDictionaryById(1);List<DictionaryData> dataList = dictionaryCommonService.findDatasByDictId(dict.getDictId());// 處理邏輯...}
}@Service
public class DictionaryDataServiceImpl implements DictionaryDataService {@Resourceprivate DictionaryCommonService dictionaryCommonService;// 業務邏輯使用公共服務
}

方案二:接口隔離原則

// 定義最小化接口
public interface DictionaryQueryService {Dictionary getById(Integer id);
}public interface DictionaryDataQueryService {List<DictionaryData> getByDictId(Integer dictId);
}// 實現類只依賴需要的接口
@Service
public class DictionaryServiceImpl implements DictionaryService, DictionaryQueryService {@Resourceprivate DictionaryDataQueryService dictionaryDataQueryService;// 實現邏輯...
}@Service  
public class DictionaryDataServiceImpl implements DictionaryDataService, DictionaryDataQueryService {@Resourceprivate DictionaryQueryService dictionaryQueryService;// 實現邏輯...
}

優點

  • 從根本上解決了設計問題
  • 提高了代碼的可維護性
  • 符合SOLID原則
  • 降低了模塊間的耦合度

缺點

  • 需要大量的代碼重構
  • 可能涉及業務邏輯的調整
  • 短期內工作量較大

5. 事件驅動(ApplicationEvent,侵入性中等,適合通知場景)

適用場景:一個服務需要通知另一個服務執行某些操作

實現方式

// 定義事件
public class DictionaryDataChangeEvent extends ApplicationEvent {private final String dictCode;public DictionaryDataChangeEvent(Object source, String dictCode) {super(source);this.dictCode = dictCode;}public String getDictCode() {return dictCode;}
}// 事件發布者
@Service
public class DictionaryDataServiceImpl implements DictionaryDataService {@Resourceprivate ApplicationEventPublisher eventPublisher;@Overridepublic boolean save(DictionaryData entity) {boolean success = super.save(entity);if (success) {// 發布事件而不是直接調用其他服務eventPublisher.publishEvent(new DictionaryDataChangeEvent(this, entity.getDictCode()));}return success;}
}// 事件監聽者
@Service
public class DictionaryServiceImpl implements DictionaryService {@EventListenerpublic void handleDictionaryDataChange(DictionaryDataChangeEvent event) {// 處理字典數據變更后的邏輯String dictCode = event.getDictCode();// 清除緩存、更新狀態等}
}

優點

  • 完全解耦了兩個服務
  • 支持異步處理
  • 便于擴展(多個監聽者)
  • 符合事件驅動架構

缺點

  • 增加了系統復雜度
  • 調試相對困難
  • 需要理解事件驅動模式

總結

方案侵入性適用場景推薦指數備注
配置開啟循環依賴最低快速修復、臨時方案??治標不治本
@Lazy注解簡單雙向依賴????優先推薦
手動獲取bean中等復雜依賴關系???失去注入便利性
接口隔離/中間層架構重構?????長期最佳方案
事件驅動中等通知場景????解耦效果好

建議處理流程

  1. 短期:使用 @Lazy 快速解決啟動問題
  2. 中期:分析業務邏輯,評估是否需要重構
  3. 長期:通過接口隔離或中間層徹底解決循環依賴

版本差異補充

Spring Boot 2.5.x 及之前

  • 默認 allow-circular-references: true
  • 三級緩存自動處理循環依賴
  • 開發者通常不會意識到循環依賴問題

Spring Boot 2.6.0+

  • 默認 allow-circular-references: false
  • 啟動時檢查并報錯
  • 強制開發者關注和解決循環依賴

Spring Boot 3.0+

  • 繼續保持對循環依賴的嚴格控制
  • 進一步鼓勵良好的設計模式
  • 可能在未來版本中完全移除循環依賴支持

這個變更體現了Spring團隊對代碼質量架構設計的重視,雖然短期內會帶來一些遷移成本,但長期來看有利于項目的可維護性和穩定性。


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/918422.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/918422.shtml
英文地址,請注明出處:http://en.pswp.cn/news/918422.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

本地代碼上傳Github步驟

1.注冊Github賬號 2.下載git客戶端 下載、安裝步驟可以參考網站&#xff1a;(6 封私信 / 10 條消息) 手把手教你用git上傳項目到GitHub&#xff08;圖文并茂&#xff0c;這一篇就夠了&#xff09;&#xff0c;相信你一定能成功&#xff01;&#xff01; - 知乎 3.在Github上…

5G NR 非地面網絡 (NTN) 5G、太空和統一網絡

非地面網絡 5G 和太空&#xff1a;對 NTN 測試與測量的影響NTN 基站測試與測量NTN 用戶設備的測試設備R&SSMW200A 矢量信號發生器R&SSMBV100B 矢量信號發生器總結5G 和太空&#xff1a;對 NTN 測試與測量的影響 5G 非地面網絡 (NTN) 是無線通信向全球性星基和機載通信…

少兒編程比賽(如藍橋杯、創意編程大賽等)的題目類型、知識點及難度總結

以下是針對主流少兒編程比賽&#xff08;如藍橋杯、創意編程大賽等&#xff09;的題目類型、知識點及難度總結&#xff0c;結合了Scratch和C等語言的真題分析&#xff0c;幫助備賽或教學參考&#xff1a; 一、基礎操作與交互題&#xff08;適合6~10歲&#xff09; 考察圖形化編…

SIFThinker: Spatially-Aware Image Focus for Visual Reasoning

SIFThinker: Spatially-Aware Image Focus for Visual Reasoning Authors: Zhangquan Chen, Ruihui Zhao, Chuwei Luo, Mingze Sun, Xinlei Yu, Yangyang Kang, Ruqi Huang 相關工作總結 視覺思維鏈推理 最近的研究表明&#xff0c;通過上下文學習逐步推理可以顯著提升大型…

學習嵌入式第二十五天

IO 1.概念 IO指input/outputLinux中一切皆文件IO的操作對象是文件 2.文件一段數據的集合文件通常存放在外存中&#xff0c;掉電后數據不丟失分類b(block&#xff0c;塊設備文件) 按塊掃描信息的文件。通常存儲類型的設備為塊設備文件。文件IOc(character&#xff0c;字符設備文…

本地部署接入 whisper + ollama qwen3:14b 總結字幕

1. 實現功能 M4-1 接入 whisper ollama qwen3:14b 總結字幕 自動下載視頻元數據如果有字幕&#xff0c;只下載字幕使用 ollama 的 qwen3:14b 對字幕內容進行總結 2.運行效果 &#x1f50d; 正在提取視頻元數據… &#x1f4dd; 正在下載所有可用字幕… [youtube] Extracting U…

【13-向量化-高效計算】

研究者能夠擴展神經網絡并構建非常大型網絡的原因之一&#xff0c;就是神經網絡可以被向量化&#xff0c;vectorized&#xff1b;可以非常高效地用矩陣地乘法實現。 事實上&#xff0c;并行計算硬件&#xff0c;例如GPU&#xff0c;一些CPU的功能&#xff0c;非常擅長進行非常大…

論文中PDF的公式如何提取-公式提取

Mathcheap - An AI-powered, free alternative to Mathpix Snip. 從PDF中截圖公式&#xff0c;之后 ctrl V 轉換成功 &#xff0c;提取成功 復制到word中&#xff0c;是這樣的 這顯然不是我們需要的。 可以使用Axmath 復制進去Axmath 就能正常顯示公式。 之后再插入word…

用 Flink SQL 和 Paimon 打造實時數倉:深度解析與實踐指南

1. 實時數倉的魅力&#xff1a;從離線到分鐘級的飛躍實時數倉&#xff0c;聽起來是不是有點高大上&#xff1f;其實它沒那么神秘&#xff0c;但確實能讓你的數據處理能力像坐上火箭一樣飆升&#xff01;傳統的離線數倉&#xff0c;像 Hadoop 生態的 Hive&#xff0c;動輒小時級…

【已解決】報錯:WARNING: pip is configured with locations that require TLS/SSL

一、問題背景二、問題分析1. SSL模塊缺失的本質2. Anaconda環境特點三、問題表現四、解決方案詳解1. 完整配置環境變量2. 添加環境變量的步驟3. 測試驗證五、實戰示例六、附加建議七、總結八、參考鏈接一、問題背景 在Windows 10系統中使用Python的包管理工具pip時&#xff0c…

Java項目基本流程(三)

一、頁面初始化階段&#xff08;加載即執行&#xff09;加載欄目列表&#xff08;同步請求&#xff09;發送同步 AJAX 請求到SearchChannel接口&#xff0c;獲取所有欄目數據。清空下拉框&#xff08;.channelid&#xff09;后&#xff0c;先添加 “全部” 選項&#xff0c;再循…

鷓鴣云光伏仿真:項目前期決策的“數據明燈”

曾有一處光伏項目&#xff0c;在精心籌備數月后終于建成&#xff0c;卻在運行初期即因未充分評估山體遮擋影響&#xff0c;導致實際發電量較預期大幅降低近一成。前期決策中的微小疏漏&#xff0c;往往成為項目經濟性與可行性的致命傷。而鷓鴣云光伏仿真軟件正是一盞照亮前路的…

開發指南129-基礎類-BaseController

所有接口都需要繼承BaseControllerBaseController里有很多有用的方法&#xff0c;現舉例最重要的幾個&#xff1a;1、getURI返回接口地址&#xff0c;就是PostMapping或GetMapping中定義的接口地址。常用于返回值中&#xff0c;例如接口的異常處理&#xff1a;try {// 處理邏輯…

C++高頻知識點(十八)

文章目錄86. C多線程中&#xff0c;鎖的實現方式有哪些&#xff1f;1. 互斥鎖&#xff08;Mutex&#xff09;2. 遞歸互斥鎖&#xff08;Recursive Mutex&#xff09;3. 讀寫鎖&#xff08;Shared Mutex&#xff09;4. 自旋鎖&#xff08;Spinlock&#xff09;5. 條件變量&#…

【C語言強化訓練16天】--從基礎到進階的蛻變之旅:Day1

&#x1f525;個人主頁&#xff1a;草莓熊Lotso &#x1f3ac;作者簡介&#xff1a;C研發方向學習者 &#x1f4d6;個人專欄&#xff1a; 《C語言》 《數據結構與算法》《C語言刷題集》《Leetcode刷題指南》 ??人生格言&#xff1a;生活是默默的堅持&#xff0c;毅力是永久的…

【軟考中級網絡工程師】知識點之 TCP 協議深度剖析

目錄一、TCP 協議簡介二、TCP 協議的特點2.1 面向連接2.2 可靠性高2.3 擁塞控制2.4 全雙工通信2.5 高效性2.6 支持多種應用協議2.7 可靠的錯誤恢復三、TCP 協議的工作機制3.1 三次握手建立連接3.2 數據傳輸3.3 四次揮手關閉連接四、TCP 協議的數據包格式五、TCP 協議在實際應用…

操作系統1.5:操作系統引導

目錄 總覽 什么是操作系統引導&#xff1f; 磁盤里邊有哪些相關數據? 操作系統引導(開機過程&#xff09; 總覽 什么是操作系統引導&#xff1f; 操作系統引導(boot)——開機的時候&#xff0c;怎么讓操作系統運行起來? 磁盤里邊有哪些相關數據? 一個剛買來的磁盤(硬…

[鷓鴣云]光伏AI設計平臺解鎖電站開發新范式

1.[鷓鴣云]平臺概述[鷓鴣云]是由鷓鴣云&#xff08;徐州&#xff09;信息技術有限公司傾力打造的&#xff0c;可以媲美?PVsyst的光伏AI設計平臺。它為光伏項目不同階段的開發提供了快速設計、衛星地圖設計、無人機3D設計、Unity3D設計、專業繪圖設計與場區設計多種設計方式&am…

docker compose和docker-compose命令的區別

Docker Compose 有兩種命令形式&#xff1a;docker compose&#xff08;空格連接&#xff09;docker-compose&#xff08;短橫線連接&#xff09;其核心區別如下&#xff1a;一、技術特性docker-compose&#xff08;短橫線&#xff09;獨立可執行文件&#xff1a;作為獨立程序安…

基于Strands Agent開發輔助閱讀Agent

序 本篇由來&#xff0c;在COC上我當面感謝了組委會和姜寧老師&#xff0c;隨即被姜寧老師催稿&#xff0c;本來當天晚上寫了一個流水賬&#xff0c;感覺甚為不妥。于是決定慢慢寫&#xff0c;緩緩道來。要同時兼顧Show me the code&#xff0c;Show me the vide。希望能形成一…