一、構造器注入引發的循環依賴
1. 問題復現
@Component
public class ServiceA {private final ServiceB serviceB;@Autowiredpublic ServiceA(ServiceB serviceB) { // 構造器注入this.serviceB = serviceB;}
}@Component
public class ServiceB {private final ServiceA serviceA;@Autowiredpublic ServiceB(ServiceA serviceA) { // 構造器注入this.serviceA = serviceA;}
}
?報錯信息:Requested bean is currently in creation: Is there an unresolvable circular reference?
2. 原理分析
-
三級緩存失效:構造器注入要求在實例化階段完成依賴注入,而此時 Bean 尚未放入三級緩存。
-
生命周期沖突:
3. 解決方案
- 方案 1:將其中一個 Bean 改為 Setter / 字段注入
- 方案 2:使用 `@Lazy` 延遲加載
@Autowiredpublic ServiceA(@Lazy ServiceB serviceB) { this.serviceB = serviceB;}
二、原型(Prototype)作用域的循環依賴
1. 問題復現
@Scope("prototype")
@Component
public class PrototypeA {@Autowired private PrototypeB b;
}@Scope("prototype")
@Component
public class PrototypeB {@Autowired private PrototypeA a;
}
2. 原理分析
-
緩存機制不生效:原型 Bean 不會存入三級緩存,每次請求都創建新實例。
-
Spring 官方限制:明確說明不處理原型 Bean 的循環依賴。
3. 解決方案
-
重構設計:避免原型 Bean 之間的循環依賴
-
改用單例:評估是否真的需要原型作用域
三、@Async 注解導致的代理沖突
1. 問題復現
@Service
public class AsyncServiceA {@Autowired private AsyncServiceB serviceB;@Asyncpublic void asyncMethod() { /* ... */ }
}@Service
public class AsyncServiceB {@Autowired private AsyncServiceA serviceA;
}
2. 原理分析
-
代理時序問題:
@Async
?通過后置處理器生成代理,可能破壞三級緩存機制。 -
典型錯誤棧:
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:對異步方法所在類使用接口代理
@Async public interface AsyncService {void asyncMethod(); }@Service public class AsyncServiceImpl implements AsyncService { /* ... */ }
- 方案 2:在注入點添加?
@Lazy
@Autowired @Lazy private AsyncServiceA serviceA;
四、Configuration 類之間的循環依賴
1. 問題復現
@Configuration
public class ConfigA {@Autowired private ConfigB configB;
}@Configuration
public class ConfigB {@Autowired private ConfigA configA;
}
2. 原理分析
-
配置類加載順序:配置類需要優先初始化,無法通過常規循環依賴解決。
-
Spring 限制:
@Configuration
?類被視為特殊 Bean,其代理機制與普通 Bean 不同。
3. 解決方案
-
重構配置類:合并相關配置
-
使用?
@DependsOn
:明確指定加載順序@Configuration @DependsOn("configB") public class ConfigA { /* ... */ }
五、自定義 BeanPostProcessor 引發的沖突
1. 問題復現
@Component
public class CustomProcessor implements BeanPostProcessor {@Autowired private ServiceX x; // 依賴其他Bean
}
2. 原理分析
-
處理器加載時序:
BeanPostProcessor
?需要優先初始化,此時普通 Bean 尚未創建。 -
Spring 啟動流程:
?3. 解決方案
-
避免在 BeanPostProcessor 中注入其他 Bean
-
使用延遲注入
private ObjectProvider<ServiceX> xProvider;public Object postProcessBeforeInitialization(Object bean, String beanName) {ServiceX x = xProvider.getIfAvailable();// ...}
六、終極解決方案工具箱?
問題類型 | 應急方案 | 根治方案 |
---|---|---|
構造器循環依賴 | @Lazy ?注解 | 改為 Setter 注入 |
原型Bean循環依賴 | 重構作用域 | 引入中間類抽象依賴 |
AOP代理沖突 | 接口代理模式 | 調整切面作用順序 |
配置類循環依賴 | @DependsOn ?指定順序 | 合并配置類 |
BeanPostProcessor依賴 | ObjectProvider ?延遲獲取 | 分離處理器與業務邏輯 |
結語:跳出循環依賴的思維陷阱
Spring 的循環依賴處理機制體現了框架設計的高度智慧,但作為開發者,最優雅的解決方案往往不是技術手段,而是架構設計。通過以下原則可從根本上避免循環依賴:
-
單一職責原則:拆分臃腫的 Bean
-
依賴倒置原則:面向接口編程
-
層次化設計:Controller -> Service -> Repository 的嚴格分層