springboot循環(huán)依賴問題案例代碼及解決辦法
在 Spring Boot 中,如果兩個或多個 Bean 之間存在循環(huán)依賴(即 Bean A 依賴 Bean B,而 Bean B 又依賴 Bean A),會導致 Spring 的依賴注入機制無法正確處理,從而拋出異常。以下是循環(huán)依賴的場景分析、案例代碼及常見的解決方法。
1. 什么是循環(huán)依賴?
概念
循環(huán)依賴是指兩個或多個 Bean 相互依賴,形成一個閉環(huán)。例如:
- 直接循環(huán)依賴:
A
依賴B
,B
依賴A
。
- 間接循環(huán)依賴:
A
依賴B
,B
依賴C
,而C
又依賴A
。
2. 循環(huán)依賴的場景案例
以下是一個簡單的直接循環(huán)依賴場景。
案例代碼 Bean A
@Component public class BeanA { @Autowired private BeanB beanB; public BeanA() { System.out.println("BeanA Constructor"); } public void doSomething() { System.out.println("BeanA is doing something"); } }
Bean B
@Component public class BeanB { @Autowired private BeanA beanA; public BeanB() { System.out.println("BeanB Constructor"); } public void doSomething() { System.out.println("BeanB is doing something"); } }
啟動類
@SpringBootApplication public class CircularDependencyDemoApplication { public static void main(String[] args) { SpringApplication.run(CircularDependencyDemoApplication.class, args); } }
運行結果
運行時,Spring 會拋出以下異常:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'beanA':
Requested bean is currently in creation: Is there an unresolvable circular reference?
3. 解決循環(huán)依賴的常見方法
方法 1:使用 @Lazy 注解
@Lazy
注解可以延遲加載 Bean,使 Spring 在真正需要使用時才注入依賴,從而打破循環(huán)依賴。
修改代碼
在其中一個依賴上添加 @Lazy
注解:
BeanA:
@Component public class BeanA { @Autowired @Lazy private BeanB beanB; public BeanA() { System.out.println("BeanA Constructor"); } public void doSomething() { System.out.println("BeanA is doing something"); } }
BeanB:
@Component public class BeanB { @Autowired private BeanA beanA; public BeanB() { System.out.println("BeanB Constructor"); } public void doSomething() { System.out.println("BeanB is doing something"); } }
運行結果
程序運行正常,輸出:
BeanA Constructor
BeanB Constructor
方法 2:使用構造器注入解決循環(huán)依賴
Spring 無法通過構造器注入解決循環(huán)依賴,因為在構造器注入的過程中,所有依賴必須在實例化時完全注入。這種情況下,需要重新設計代碼結構以打破循環(huán)依賴。
重構代碼
將循環(huán)依賴重構為單向依賴。例如,可以引入第三方協(xié)作者 Bean 來解耦。
BeanA:
@Component public class BeanA { private final Helper helper; public BeanA(Helper helper) { this.helper = helper; System.out.println("BeanA Constructor"); } public void doSomething() { System.out.println("BeanA is doing something"); } }
BeanB:
@Component public class BeanB { private final Helper helper; public BeanB(Helper helper) { this.helper = helper; System.out.println("BeanB Constructor"); } public void doSomething() { System.out.println("BeanB is doing something"); } }
Helper:
@Component public class Helper { public void assist() { System.out.println("Helper is assisting"); } }
運行結果
Helper Constructor
BeanA Constructor
BeanB Constructor
通過引入 Helper
,BeanA
和 BeanB
不再直接依賴彼此,循環(huán)依賴被消除。
方法 3:使用 @PostConstruct 或 Setter 注入
通過構造器注入會導致循環(huán)依賴問題,但可以使用 Setter 方法注入來延遲依賴注入的時機。
修改代碼
BeanA:
@Component public class BeanA { private BeanB beanB; public BeanA() { System.out.println("BeanA Constructor"); } @Autowired public void setBeanB(BeanB beanB) { this.beanB = beanB; } public void doSomething() { System.out.println("BeanA is doing something"); } }
BeanB:
@Component public class BeanB { private BeanA beanA; public BeanB() { System.out.println("BeanB Constructor"); } @Autowired public void setBeanA(BeanA beanA) { this.beanA = beanA; } public void doSomething() { System.out.println("BeanB is doing something"); } }
運行結果
BeanA Constructor
BeanB Constructor
Setter 注入允許 Spring 在實例化 Bean 后再設置依賴,從而避免循環(huán)依賴問題。
方法 4:使用 ObjectFactory 或 Provider 進行延遲注入
Spring 提供了 ObjectFactory
和 Provider
接口,用于延遲獲取 Bean,從而避免循環(huán)依賴。
修改代碼 BeanA:
@Component public class BeanA { private final ObjectFactory<BeanB> beanBFactory; public BeanA(ObjectFactory<BeanB> beanBFactory) { this.beanBFactory = beanBFactory; System.out.println("BeanA Constructor"); } public void doSomething() { BeanB beanB = beanBFactory.getObject(); System.out.println("BeanA is doing something with " + beanB); } }
BeanB:
@Component public class BeanB { private final BeanA beanA; @Autowired public BeanB(BeanA beanA) { this.beanA = beanA; System.out.println("BeanB Constructor"); } public void doSomething() { System.out.println("BeanB is doing something"); } }
運行結果
BeanA Constructor
BeanB Constructor
通過 ObjectFactory
,BeanA 可以在需要時動態(tài)獲取 BeanB,避免了循環(huán)依賴。
方法 5:使用 @DependsOn 明確加載順序
如果循環(huán)依賴是因為 Bean 的加載順序問題,可以使用 @DependsOn
指定加載順序。
修改代碼
BeanA:
@Component @DependsOn("beanB") // 指定 BeanB 應該先加載 public class BeanA { @Autowired private BeanB beanB; public BeanA() { System.out.println("BeanA Constructor"); } public void doSomething() { System.out.println("BeanA is doing something"); } }
BeanB:
@Component public class BeanB { @Autowired private BeanA beanA; public BeanB() { System.out.println("BeanB Constructor"); } public void doSomething() { System.out.println("BeanB is doing something"); } }
運行結果
BeanB Constructor
BeanA Constructor
4. 總結
循環(huán)依賴是常見問題,但可以通過多種方式解決:
- 使用
@Lazy
延遲加載。 - 重構代碼,避免直接循環(huán)依賴。
- 使用 Setter 注入或
@PostConstruct
。 - 使用
ObjectFactory
或Provider
進行延遲注入。 - 使用
@DependsOn
明確加載順序。
推薦方案:
如果可以重構代碼,消除循環(huán)依賴本身是最佳實踐。在必要時,結合 @Lazy
或 ObjectFactory
來解決循環(huán)依賴問題。
到此這篇關于springboot循環(huán)依賴問題及其解決辦法的文章就介紹到這了,更多相關springboot循環(huán)依賴內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
spring?@Scheduled注解各參數使用以及定時任務詳解
文章詳細介紹了Spring框架中@Scheduled注解的各個參數,包括cron表達式、時區(qū)、fixedDelay、fixedRate、initialDelay等,并提供了多個示例來說明這些參數的使用方法2024-11-11解決@Async(“taskExecutor“)異步線程報錯問題
這篇文章主要介紹了解決@Async(“taskExecutor“)異步線程報錯問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08springmvc 中dao層和service層的區(qū)別說明
這篇文章主要介紹了springmvc 中dao層和service層的區(qū)別說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08java執(zhí)行shell并獲取shell輸出日志方式
這篇文章主要介紹了java執(zhí)行shell并獲取shell輸出日志方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-04-04