什么是循環依賴?
循環依賴是指兩個或多個Bean之間相互依賴,形成閉環的情況。例如:AService依賴BService,而BService又依賴AService。這種場景下,傳統的創建順序無法滿足依賴注入的要求。
Spring的三級緩存機制
Spring通過三級緩存機制優雅地解決了循環依賴問題:
1. 第一級緩存:singletonObjects
存儲完全初始化完成的單例Bean
這里的Bean是經過完整生命周期處理的(包括AOP代理)
2. 第二級緩存:earlySingletonObjects
存儲提前暴露的Bean引用(早期引用)
這些Bean尚未完成完整初始化,但可以用于解決循環依賴
3. 第三級緩存:singletonFactories
存儲Bean的ObjectFactory(工廠對象)
通過工廠可以獲取Bean的早期引用,必要時創建代理對象
解決循環依賴的詳細流程
場景:AService ←→ BService 相互依賴
java
// AService 依賴 BService @Service public class AService {@Autowiredprivate BService bService; }// BService 依賴 AService @Service public class BService {@Autowiredprivate AService aService; }
解決步驟:
開始創建AService
creatingSet.add('AService')
?- 標記AService正在創建實例化AService普通對象
AService需要注入BService
從單例池獲取BService,不存在則開始創建BService
創建BService
creatingSet.add('BService')
?- 標記BService正在創建實例化BService普通對象
BService需要注入AService
BService獲取AService
從單例池獲取AService,不存在
檢查
creatingSet
發現AService正在創建(循環依賴 detected!)從三級緩存獲取AService的ObjectFactory
執行工廠方法:
getEarlyBeanReference()
?→ 必要時創建AService代理對象將結果存入二級緩存
earlySingletonObjects
,并從三級緩存移除工廠
BService繼續初始化
注入AService的早期引用(可能是代理對象)
完成其他屬性填充
執行AOP(如果需要)
將完整BService存入單例池
AService繼續初始化
注入已創建的BService
完成其他屬性填充
執行AOP(但發現已提前AOP,直接使用早期引用)
將完整AService存入單例池
關鍵源碼分析
1. 添加單例工廠
java
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {// 添加到三級緩存this.singletonFactories.put(beanName, singletonFactory);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}} }
在doCreateBean()
方法中,Spring會提前暴露Bean的工廠:
java
// 為了解決循環依賴提前緩存單例創建工廠 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) {addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
2. 獲取單例Bean的邏輯
java
@Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) {// 1. 從一級緩存查找Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {// 2. 從二級緩存查找singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {synchronized (this.singletonObjects) {// 3. 從三級緩存查找并創建早期引用ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();// 移動到二級緩存this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}return singletonObject; }
3. 提前AOP處理
java
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {// 如果需要AOP,則創建代理對象Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);if (specificInterceptors != DO_NOT_PROXY) {Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));return proxy;}return bean; }
為什么需要三級緩存?
一級緩存:存儲完整的單例Bean,保證單例性質
二級緩存:存儲早期引用,避免多次執行AOP產生多個代理對象
三級緩存:存儲工廠對象,延遲決策是否需要進行AOP
如果只有二級緩存,每次獲取早期引用都需要執行AOP,可能導致創建多個不同的代理對象,違反單例原則。