springboot循環(huán)依賴問題案例代碼及解決辦法
在 Spring Boot 中,如果兩個或多個 Bean 之間存在循環(huán)依賴(即 Bean A 依賴 Bean B,而 Bean B 又依賴 Bean A),會導(dǎo)致 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);
}
}運行結(jié)果
運行時,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");
}
}運行結(jié)果
程序運行正常,輸出:
BeanA Constructor
BeanB Constructor
方法 2:使用構(gòu)造器注入解決循環(huán)依賴
Spring 無法通過構(gòu)造器注入解決循環(huán)依賴,因為在構(gòu)造器注入的過程中,所有依賴必須在實例化時完全注入。這種情況下,需要重新設(shè)計代碼結(jié)構(gòu)以打破循環(huán)依賴。
重構(gòu)代碼
將循環(huán)依賴重構(gòu)為單向依賴。例如,可以引入第三方協(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");
}
}運行結(jié)果
Helper Constructor
BeanA Constructor
BeanB Constructor
通過引入 Helper,BeanA 和 BeanB 不再直接依賴彼此,循環(huán)依賴被消除。
方法 3:使用 @PostConstruct 或 Setter 注入
通過構(gòu)造器注入會導(dǎo)致循環(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");
}
}運行結(jié)果
BeanA Constructor
BeanB Constructor
Setter 注入允許 Spring 在實例化 Bean 后再設(shè)置依賴,從而避免循環(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");
}
}運行結(jié)果
BeanA Constructor
BeanB Constructor
通過 ObjectFactory,BeanA 可以在需要時動態(tài)獲取 BeanB,避免了循環(huán)依賴。
方法 5:使用 @DependsOn 明確加載順序
如果循環(huán)依賴是因為 Bean 的加載順序問題,可以使用 @DependsOn 指定加載順序。
修改代碼
BeanA:
@Component
@DependsOn("beanB") // 指定 BeanB 應(yīng)該先加載
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");
}
}運行結(jié)果
BeanB Constructor
BeanA Constructor
4. 總結(jié)
循環(huán)依賴是常見問題,但可以通過多種方式解決:
- 使用
@Lazy延遲加載。 - 重構(gòu)代碼,避免直接循環(huán)依賴。
- 使用 Setter 注入或
@PostConstruct。 - 使用
ObjectFactory或Provider進行延遲注入。 - 使用
@DependsOn明確加載順序。
推薦方案:
如果可以重構(gòu)代碼,消除循環(huán)依賴本身是最佳實踐。在必要時,結(jié)合 @Lazy 或 ObjectFactory 來解決循環(huán)依賴問題。
到此這篇關(guān)于springboot循環(huán)依賴問題及其解決辦法的文章就介紹到這了,更多相關(guān)springboot循環(huán)依賴內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談Java(SpringBoot)基于zookeeper的分布式鎖實現(xiàn)
這篇文章主要介紹了Java(SpringBoot)基于zookeeper的分布式鎖實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-03-03
MyBatis-Plus進行分頁查詢優(yōu)化的實踐指南
在實際開發(fā)中,分頁查詢是常見的需求,尤其是需要關(guān)聯(lián)其他表獲取額外信息的場景,本文將介紹如何使用 MyBatis-Plus 進行高效分頁查詢,并結(jié)合關(guān)聯(lián)查詢優(yōu)化數(shù)據(jù)填充,需要的可以了解下2025-07-07
SpringBoot使用Sa-Token實現(xiàn)路徑攔截和特定接口放行
這篇文章主要介紹了SpringBoot使用Sa-Token實現(xiàn)路徑攔截和特定接口放行,文中通過代碼示例講解的非常詳細,對大家的學習或工作有一定的幫助,需要的朋友可以參考下2024-06-06
詳解Spring Boot 中實現(xiàn)定時任務(wù)的兩種方式
這篇文章主要介紹了Spring Boot 中實現(xiàn)定時任務(wù)的兩種方式,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-04-04
SpringBoot整合SpringTask實現(xiàn)定時任務(wù)的流程
這篇文章主要介紹了SpringBoot整合SpringTask實現(xiàn)定時任務(wù)的流程,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-06-06

