Spring無法解決循環(huán)依賴的五種場(chǎng)景分析
一、構(gòu)造器注入引發(fā)的循環(huán)依賴
1. 問題復(fù)現(xiàn)
@Component
public class ServiceA {
private final ServiceB serviceB;
@Autowired
public ServiceA(ServiceB serviceB) { // 構(gòu)造器注入
this.serviceB = serviceB;
}
}
@Component
public class ServiceB {
private final ServiceA serviceA;
@Autowired
public ServiceB(ServiceA serviceA) { // 構(gòu)造器注入
this.serviceA = serviceA;
}
}報(bào)錯(cuò)信息:Requested bean is currently in creation: Is there an unresolvable circular reference?
2. 原理分析
- 三級(jí)緩存失效:構(gòu)造器注入要求在實(shí)例化階段完成依賴注入,而此時(shí) Bean 尚未放入三級(jí)緩存。
- 生命周期沖突:

3. 解決方案
- 方案 1:將其中一個(gè) Bean 改為 Setter / 字段注入
- 方案 2:使用 `@Lazy` 延遲加載
@Autowired
public ServiceA(@Lazy ServiceB serviceB) {
this.serviceB = serviceB;
}二、原型(Prototype)作用域的循環(huán)依賴
1. 問題復(fù)現(xiàn)
@Scope("prototype")
@Component
public class PrototypeA {
@Autowired private PrototypeB b;
}
@Scope("prototype")
@Component
public class PrototypeB {
@Autowired private PrototypeA a;
}2. 原理分析
緩存機(jī)制不生效:原型 Bean 不會(huì)存入三級(jí)緩存,每次請(qǐng)求都創(chuàng)建新實(shí)例。
Spring 官方限制:明確說明不處理原型 Bean 的循環(huán)依賴。
3. 解決方案
重構(gòu)設(shè)計(jì):避免原型 Bean 之間的循環(huán)依賴
改用單例:評(píng)估是否真的需要原型作用域
三、@Async 注解導(dǎo)致的代理沖突
1. 問題復(fù)現(xiàn)
@Service
public class AsyncServiceA {
@Autowired private AsyncServiceB serviceB;
@Async
public void asyncMethod() { /* ... */ }
}
@Service
public class AsyncServiceB {
@Autowired private AsyncServiceA serviceA;
}2. 原理分析
代理時(shí)序問題:
@Async通過后置處理器生成代理,可能破壞三級(jí)緩存機(jī)制。典型錯(cuò)誤棧:
BeanCreationException: Error creating bean with name 'asyncServiceA': Bean with name 'asyncServiceA' has been injected into other beans [...] in their raw version as part of a circular reference.
3. 解決方案
- 方案 1:對(duì)異步方法所在類使用接口代理
@Async
public interface AsyncService {
void asyncMethod();
}
@Service
public class AsyncServiceImpl implements AsyncService { /* ... */ }- 方案 2:在注入點(diǎn)添加
@Lazy
@Autowired @Lazy private AsyncServiceA serviceA;
四、Configuration 類之間的循環(huán)依賴
1. 問題復(fù)現(xiàn)
@Configuration
public class ConfigA {
@Autowired private ConfigB configB;
}
@Configuration
public class ConfigB {
@Autowired private ConfigA configA;
}2. 原理分析
配置類加載順序:配置類需要優(yōu)先初始化,無法通過常規(guī)循環(huán)依賴解決。
Spring 限制:
@Configuration類被視為特殊 Bean,其代理機(jī)制與普通 Bean 不同。
3. 解決方案
重構(gòu)配置類:合并相關(guān)配置
使用
@DependsOn:明確指定加載順序
@Configuration
@DependsOn("configB")
public class ConfigA { /* ... */ }五、自定義 BeanPostProcessor 引發(fā)的沖突
1. 問題復(fù)現(xiàn)
@Component
public class CustomProcessor implements BeanPostProcessor {
@Autowired private ServiceX x; // 依賴其他Bean
}2. 原理分析
處理器加載時(shí)序:
BeanPostProcessor需要優(yōu)先初始化,此時(shí)普通 Bean 尚未創(chuàng)建。Spring 啟動(dòng)流程:

3. 解決方案
避免在 BeanPostProcessor 中注入其他 Bean
使用延遲注入
private ObjectProvider<ServiceX> xProvider;
public Object postProcessBeforeInitialization(Object bean, String beanName) {
ServiceX x = xProvider.getIfAvailable();
// ...
}六、終極解決方案工具箱
| 問題類型 | 應(yīng)急方案 | 根治方案 |
|---|---|---|
| 構(gòu)造器循環(huán)依賴 | @Lazy 注解 | 改為 Setter 注入 |
| 原型Bean循環(huán)依賴 | 重構(gòu)作用域 | 引入中間類抽象依賴 |
| AOP代理沖突 | 接口代理模式 | 調(diào)整切面作用順序 |
| 配置類循環(huán)依賴 | @DependsOn 指定順序 | 合并配置類 |
| BeanPostProcessor依賴 | ObjectProvider 延遲獲取 | 分離處理器與業(yè)務(wù)邏輯 |
結(jié)語:跳出循環(huán)依賴的思維陷阱
Spring 的循環(huán)依賴處理機(jī)制體現(xiàn)了框架設(shè)計(jì)的高度智慧,但作為開發(fā)者,最優(yōu)雅的解決方案往往不是技術(shù)手段,而是架構(gòu)設(shè)計(jì)。通過以下原則可從根本上避免循環(huán)依賴:
單一職責(zé)原則:拆分臃腫的 Bean
依賴倒置原則:面向接口編程
層次化設(shè)計(jì):Controller -> Service -> Repository 的嚴(yán)格分層
以上就是Spring無法解決循環(huán)依賴的五種場(chǎng)景分析的詳細(xì)內(nèi)容,更多關(guān)于Spring無法解決循環(huán)依賴的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java多線程編程之使用Synchronized關(guān)鍵字同步類方法
JAVA中要想解決“臟數(shù)據(jù)”的問題,最簡(jiǎn)單的方法就是使用synchronized關(guān)鍵字來使run方法同步,看下面的代碼,只要在void和public之間加上synchronized關(guān)鍵字2014-01-01
詳解Java如何實(shí)現(xiàn)基于Redis的分布式鎖
在不同進(jìn)程需要互斥地訪問共享資源時(shí),分布式鎖是一種非常有用的技術(shù)手段。這篇文章運(yùn)用圖文和實(shí)例代碼介紹了Java如何實(shí)現(xiàn)基于Redis的分布式鎖,文章介紹的很詳細(xì),對(duì)Java和Redis剛興趣的朋友們可以參考借鑒,下面來一起看看。2016-08-08
Java陷阱之a(chǎn)ssert關(guān)鍵字詳解
這篇文章詳細(xì)介紹了Java陷阱之a(chǎn)ssert關(guān)鍵字,有需要的朋友可以參考一下2013-09-09
Spring中Bean的加載與SpringBoot的初始化流程詳解
這篇文章主要介紹了Spring中Bean的加載與SpringBoot的初始化流程詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11
springboot post接口接受json時(shí),轉(zhuǎn)換為對(duì)象時(shí),屬性都為null的解決
這篇文章主要介紹了springboot post接口接受json時(shí),轉(zhuǎn)換為對(duì)象時(shí),屬性都為null的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
Java如何基于EasyExcel實(shí)現(xiàn)導(dǎo)入數(shù)據(jù)校驗(yàn)并生成錯(cuò)誤信息Excel
這篇文章主要介紹了Java如何基于EasyExcel實(shí)現(xiàn)導(dǎo)入數(shù)據(jù)校驗(yàn)并生成錯(cuò)誤信息Excel,為了優(yōu)化項(xiàng)目中的文件導(dǎo)入功能,考慮構(gòu)建一個(gè)基于EasyExcel的通用Excel導(dǎo)入框架,主要解決導(dǎo)入數(shù)據(jù)的校驗(yàn)問題,避免業(yè)務(wù)代碼中堆積大量校驗(yàn)邏輯,需要的朋友可以參考下2024-09-09
Mybatis一對(duì)多與多對(duì)一查詢處理詳解
這篇文章主要給大家介紹了關(guān)于Mybatis一對(duì)多與多對(duì)一查詢處理的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03

