Spring Bean 循環依賴解決簡單分析
本文Spring版本:
5.0.5.RELEASE
什麼是循環依賴:
循環依賴其實就是循環引用,也就是兩個或則兩個以上的bean互相持有對方,最終形成閉環。比如A 依賴 B,B 又依賴 A;或者A依賴於B,B依賴於C,C又依賴於A。
Spring 循環依賴的處理方式:
①構造器的循環依賴:這種依賴spring是處理不了的,直 接拋出BeanCurrentlylnCreationException異常。
②單例模式下的setter循環依賴:通過“三級緩存”處理循環依賴。
③非單例循環依賴:無法處理。
Spring 如何解決單例Bean的循環依賴問題?
利用三級緩存(DefaultSingletonBeanRegistry中定義)。
singletonObjects |
用於存放完全初始化好的 bean,從該緩存中取出的 bean 可以直接使用 |
earlySingletonObjects | 存放原始的 bean 對象(尚未填充屬性) |
singletonFactories | 存放 bean對應的工廠對象(ObjectFactory) |
解決循環依賴加載bean的大致流程:
判斷緩存中是否有X:
//DefaultSingletonBeanRegistry#getSingleton
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//singletonObjects 第一級緩存,BeanFactory的單例全存在singletonObjects中
//保存的是已經初始化完全的單例
Object singletonObject = this.singletonObjects.get(beanName);
//isSingletonCurrentlyInCreation=true,代表beanName所代表的bean循環依賴了
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//第二級緩存,保存的是未初始化完全的單例(只是實例化)
singletonObject = this.earlySingletonObjects.get(beanName);
//allowEarlyReference在當前場景下,默認爲true
if (singletonObject == null && allowEarlyReference) {
//第三級緩存,不是真的緩存,緩存的是生成二級緩存的工廠方法
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//通過緩存singletonFactories中的對象工廠獲取bean的對象
//如果有AOP,獲取的會是代理對象
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
實例化bean之後,會把獲取當前bean的方式放入到singletonFactories緩存:
注意:Map<String, ObjectFactory<?>> singletonFactories
該級緩存存放的並不是實例化的bean,而是一個ObjectFactory對象,它可以構造出對應的bean
//AbstractAutowireCapableBeanFactory#doCreateBean
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
//添加緩存
addSingletonFactory(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
}
當bean實例化完成之後,會被放到一級緩存singletonObjects中,用於被其他bean獲取:
//DefaultSingletonBeanRegistry#getSingleton#addSingleton
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
爲什麼是三級緩存?
好像使用 earlySingletonObjects 和 singletonObjects 兩級緩存,一個存放早期對象,一個存放初始化完成後的對象,也能實現同樣的功能,singletonFactories 好像顯得有些多此一舉。其實不是的,對於普通對象,確實只要返回剛創建完的早期對象就好了,但對於內部有被 AOP 增強的方法的對象,需要返回的是代理對象。我們可以看一下 ObjectFactory 匿名內部類裏面調用的 getEarlyBeanReference 方法:
//AbstractAutowireCapableBeanFactory
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
// SmartInstantiationAwareBeanPostProcessor 這個後置處理器會在返回早期對象時
// 被調用,如果返回的對象需要加強,那這裏就會生成代理對象
// 如果返回的對象需要AOP增強,那這裏exposedObject 就會替換成代理對象的引用
// 比如上圖中的bean X 如果被AOP增強,那麼bean Y 加載時獲取的bean X 其實是X的代理對象
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
我們可以看到對於一般的對象,返回的就是傳入的早期對象,但是對於內部有被 AOP 增強的方法的對象,會使用後置處理器返回一個代理對象。
如果在解決循環依賴時生成了代理對象,那麼 AbstractAutoProxyCreator 會把原對象放入一個 Map 中,這個 Map 的作用是防止同類對象被重複動態代理:
//AbstractAutoProxyCreator
public Object getEarlyBeanReference(Object bean, String beanName) {
// 提前暴露代理對象的引用,是在postProcessAfterInitialization之前執行
Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
this.earlyProxyReferences.add(cacheKey);
}
return this.wrapIfNecessary(bean, beanName, cacheKey);
}
//AbstractAutoProxyCreator
//有些BeanPostProcessor提供對Bean的Wrap的操作,但是生命週期位於在set操作之後,避免重複代理
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
earlySingletonObjects作用是可以提升多次循環依賴加載的效率(singletonFactory.getObject() 不用多次執行),比如 X 依賴 Y, Y 依賴 X, Z 也依賴 X。Y 注入 X 時 earlySingletonObjects 已經通過 singletonFactory.getObject() 得到了原始X,這樣加載Y時就不用再執行 singletonFactory.getObject() 構造 X 實例了,直接從earlySingletonObjects獲取即可。