Spring 複習
3.迴圈依賴
3.1 定義
迴圈依賴指多個物件的建立過程中均需要注入對方物件,如下所示
class A{
B b;
public A(){
}
public A(B b){
this.b = b;
}
public void setB(B b){
this.b = b;
}
}
class B{
A a;
public B(){
}
public B(A a){
this.a = a;
}
public void setA(A a){
this.a = a;
}
}
3.2 解決
Spring中將物件建立分為如下兩步
- 例項化:建立初始物件
- 初始化:注入屬性
並且引入三級快取,來提前暴露物件引用,從而解決迴圈依賴的問題
3.3 示例
假設A和B的建立中,field均需要對方的引用,在refresh方法進行到finishBeanFactoryInitialization(beanFactory)時,會開始建立非懶載入的singleton,這裡會先進入preInstantiateSingletons方法,根據beanName呼叫getBean方法,假設此時A先進行建立,那麼會進入下面方法
-
doGetBean
org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean-
getSingleton---1
@Override @Nullable public Object getSingleton(String beanName) { return getSingleton(beanName, true); } @Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }
首先呼叫上面方法先從singletonObjects中場是獲取,發現為null,由於isSingletonCurrentlyInCreation為false(物件未在建立過程中),因此直接返回null
if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }
其中執行DefaultSingletonBeanRegistry#getSingleton(beanName,ObjectFactory)方法簡化版如下,傳入的ObjectFactory實現類是一個lambda表示式,也即用createBean方法重寫ObjectFactory#getObject方法
- getSingleton---2
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "Bean name must not be null"); synchronized (this.singletonObjects) { boolean newSingleton = false; try { singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { } } catch (BeanCreationException ex) { } finally { } if (newSingleton) { addSingleton(beanName, singletonObject); } } return singletonObject; } }
這裡第一行呼叫singletonFactory.getObject方法會觸發createBean,又觸發AbstractAutowireCapableBeanFactory#doCreateBean方法中主題步驟如下
-
例項化bean
-
將bean放入三級快取singletonFactories
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
其中呼叫addSingletonFactory方法如下,此處傳入的lambda表示式給定的即為ObjectFactory物件,在執行其getObject方法時,即執行
getEarlyBeanReference
方法(這裡需要留意!)protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(singletonFactory, "Singleton factory must not be null"); synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { this.singletonFactories.put(beanName, singletonFactory); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } } }
-
A執行populateBean,開始注入屬性b,由於B的物件還未建立,getSingleton---1(b)為null,這時觸發B物件建立
-
B進行例項化
-
B放入三級快取
-
B執行populateBean,開始注入屬性a,呼叫getSingleton---1方法獲取a,發現一級快取singletonObject中沒有對應物件,且正在建立中,則從二級快取earlySingletonObjects中獲取,發現仍然為null且allowEarlyReference預設為true,則去三級快取中去獲取,最終從三級快取中獲取,由於放入三級快取時,lambda表示式為() -> getEarlyBeanReference(beanName, mbd, bean),所以會呼叫getEarlyBeanReference方法如下
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; }
這裡在遍歷後置處理器的過程中,會呼叫到AbstractAutoProxyCreator的postProcessAfterInitialization方法,此方法會判斷A是否被代理,如果被代理會建立代理物件並返回,之後將原有A物件從三級快取中刪除,並將A的代理物件加入到二級快取earlySingletonObjects中,之後將A的代理物件注入給B
-
B執行initializeBean方法,呼叫後置處理器及afterProperties方法,這裡提到後置處理器,同樣會判斷B是否被代理,如果被代理則會建立B的代理物件並返回
-
B建立結束之後,會回到getSingleton---2方法,呼叫addSingleton(beanName, singletonObject);方法,如下
protected void addSingleton(String beanName, Object singletonObject) { synchronized (this.singletonObjects) { this.singletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } }
這裡會將B從三級快取中刪除,並加入到一級快取中
-
將B建立好的物件注入到A中
-
A執行initializeBean方法,進行初始化,初始化完成
-
回到getSingleton--2,執行DefaultSingletonBeanRegistry#addSingleton
protected void addSingleton(String beanName, Object singletonObject) { synchronized (this.singletonObjects) { this.singletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } }
將A從二級快取中刪除,並加入到一級快取中
-
從上面步驟可以看出,
-
三級快取分別用於存放下面三類物件
-
一級快取singletonObjects
完全建立好的物件,如果被代理,則存放代理物件
-
二級快取earlySingletonObjects
未完全建立好的代理物件
-
三級快取singletonFactories
只進行了例項化,未進行屬性注入和初始化的物件
-
-
為何上面機制生效
由於提前暴露了A物件的引用!,因而在B注入好不完整的A物件後,B以為自己建立好了,這時會注入給A,同時A也會將此B物件當作建立好的,並注入給自己,這樣A就真建立完成了,由於B保留著A的引用,這樣B也就真建立完成了
3.4 AoP的考慮
如上在有迴圈依賴的情況下,假設A被代理,那麼需要將A的代理物件注入給B,這時通過getSingleton方法從三級快取獲取物件的過程中,由於ObjectFactory的getObject方法被重寫為AbstractAutowireCapableBeanFactory#getEarlyBeanReference方法,這時會觸發後置處理器的執行,會呼叫AbstractAutoProxyCreator的postProcessAfterInitialization方法,並返回代理物件,之後將代理物件返回用於注入,並放入二級快取,如果A和除了B的其他物件也構成迴圈依賴,之後直接從二級快取中獲取A的代理物件即可
在沒有迴圈依賴的情況下,不會使用到二級快取,如果A被代理,那麼A會在完全建立後,在呼叫後置處理器序列時,會呼叫AbstractAutoProxyCreator的postProcessAfterInitialization方法,並返回代理物件
從上可以看出,
- Spring的機制是儘量讓代理物件靠後建立,也即在沒有迴圈依賴時在物件完全建立後再建立代理物件
- 在延遲建立代理物件的機制下,必須有二級快取,這樣在從三級快取中獲取時,會呼叫ObjectFactory方法,其又呼叫getEarlyBeanReference方法完成代理物件建立,之後二級快取用於儲存代理物件,而一級快取用於存放完全建立完成的物件
- Spring中,如果呼叫某個代理物件a的方法,其中又呼叫了代理物件b的方法,而不是物件b的方法
# 參考
Spring迴圈依賴三級快取是否可以減少為二級快取? - SegmentFault 思否