目錄
三級緩存核心原理
循環依賴的解決過程
1. Bean A創建過程中提前曝光工廠
2. Bean B創建時發現依賴A,從緩存獲取
3. Bean A繼續完成初始化
三級緩存的作用總結
二級緩存為何不夠解決緩存依賴?
三級緩存如何解決?
為什么不直接在實例化后創建代理?
總結
Spring通過三級緩存機制解決循環依賴問題,這與populateBean()
方法密切相關。下面我用簡化代碼解釋這個機制:
三級緩存核心原理
Spring在DefaultSingletonBeanRegistry
中維護了三個重要的緩存:
// 一級緩存:存儲完全初始化的單例bean
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);// 二級緩存:存儲早期曝光的單例bean(未完全初始化)
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);// 三級緩存:存儲單例工廠對象,用于創建早期曝光的bean
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
循環依賴的解決過程
當發生循環依賴時,Spring通過以下步驟解決:
1. Bean A創建過程中提前曝光工廠
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {// 1. 實例化Bean(調用構造函數)BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);// 2. 提前曝光一個ObjectFactory,用于解決循環依賴boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences);if (earlySingletonExposure) {addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));}// 3. 填充屬性(可能觸發對Bean B的依賴)populateBean(beanName, mbd, instanceWrapper);// 4. 初始化Bean(調用init方法和AOP代理)exposedObject = initializeBean(beanName, exposedObject, mbd);return exposedObject;
}// 將工廠添加到三級緩存
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {synchronized (this.singletonObjects) {if (!this.singletonObjects.containsKey(beanName)) {this.singletonFactories.put(beanName, singletonFactory);this.earlySingletonObjects.remove(beanName);}}
}
2. Bean B創建時發現依賴A,從緩存獲取
當populateBean()
處理B的依賴時:
protected Object resolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) {// 查找依賴的bean(假設這里是A)String dependencyName = descriptor.getDependencyName();// 從三級緩存中獲取beanObject bean = getSingleton(dependencyName, false);return bean;
}// 從三級緩存中獲取bean的核心方法
protected Object getSingleton(String beanName, boolean allowEarlyReference) {// 1. 先從一級緩存獲取Object singletonObject = this.singletonObjects.get(beanName);// 2. 如果一級緩存沒有且bean正在創建中if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {// 3. 從二級緩存獲取singletonObject = this.earlySingletonObjects.get(beanName);// 4. 二級緩存沒有且允許早期引用if (singletonObject == null && allowEarlyReference) {synchronized (this.singletonObjects) {// 5. 從三級緩存獲取工廠ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {// 6. 通過工廠創建早期bean實例singletonObject = singletonFactory.getObject();// 7. 放入二級緩存this.earlySingletonObjects.put(beanName, singletonObject);// 8. 從三級緩存移除this.singletonFactories.remove(beanName);}}}}return singletonObject;
}
3. Bean A繼續完成初始化
當B完成創建后,A繼續完成自己的初始化:
// 完成Bean A的初始化
exposedObject = initializeBean(beanName, exposedObject, mbd);// 將完全初始化的bean放入一級緩存
addSingleton(beanName, exposedObject);protected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {this.singletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);this.earlySingletonObjects.remove(beanName);}
}
三級緩存的作用總結
- 一級緩存(singletonObjects):存儲完全初始化的單例bean,可直接使用
- 二級緩存(earlySingletonObjects):存儲早期曝光的bean(未完成初始化),用于解決循環依賴
- 三級緩存(singletonFactories):存儲工廠對象,允許在需要時創建早期bean,并支持AOP代理
通過這種機制,Spring允許在bean尚未完全初始化時就將其引用暴露給依賴它的其他bean,從而解決循環依賴問題。注意:只有單例作用域的bean支持循環依賴,原型模式不支持。你的問題非常關鍵!實際上,二級緩存理論上可以解決循環依賴,但Spring依然使用三級緩存,主要出于AOP代理的考慮。我來詳細解釋:
二級緩存為何不夠解決緩存依賴?
假設只有兩級緩存:
singletonObjects
(完全初始化的bean)earlySingletonObjects
(早期曝光的bean)
當出現循環依賴時,我們可以在實例化后直接將bean放入earlySingletonObjects
。但這樣會有一個問題:如果bean需要AOP代理,早期曝光的將是原始對象,而不是代理對象。
舉個例子:
// Bean A依賴B,Bean B依賴A
class A {@Autowired private B b;@Transactional public void doSomething() {} // 需要AOP代理
}class B {@Autowired private A a;
}
當A和B循環依賴時:
- A實例化后放入二級緩存(此時是原始對象)
- B創建時從二級緩存獲取A的原始對象
- B完成創建,注入A的原始對象
- A繼續初始化,創建代理對象
最終結果:B持有的是A的原始對象,而不是代理對象,導致AOP失效!
三級緩存如何解決?
Spring通過三級緩存引入了ObjectFactory
,將AOP代理的創建延遲到真正需要早期引用時:
// 三級緩存:存儲工廠對象,用于創建可能需要代理的早期bean
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);// 添加工廠到三級緩存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));// 獲取早期引用的方法(可能創建代理)
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {Object exposedObject = bean;if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;// 如果需要代理,這里會創建代理對象exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);}}}return exposedObject;
}
當B需要A的早期引用時:
- 從三級緩存獲取
ObjectFactory
- 通過工廠調用
getEarlyBeanReference()
,此時才決定是否創建代理 - 將結果(可能是代理對象)放入二級緩存
這樣一來,B持有的就是A的代理對象,保證了AOP的正確性。
為什么不直接在實例化后創建代理?
你可能會問:為什么不直接在實例化A后就創建代理,然后放入二級緩存?這樣不就不需要三級緩存了嗎?
原因有兩點:
- 性能優化:不是所有bean都需要代理,延遲到真正需要時再創建可以避免不必要的代理
- 順序正確性:Spring的后置處理器執行順序是有規范的。
postProcessBeforeInitialization
和postProcessAfterInitialization
應該在bean初始化階段執行,而不是實例化階段。如果提前創建代理,會破壞這個順序。
總結
二級緩存可以解決普通對象的循環依賴,但無法解決代理對象的循環依賴。三級緩存通過引入ObjectFactory
,將代理創建延遲到真正需要早期引用時,既保證了AOP的正確性,又維持了后置處理器的執行順序。這是Spring在循環依賴和AOP之間找到的精妙平衡點。