Spring IoC - 循環依賴
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先進行創建,那麼會進入下面方法
-
doGetBeanorg.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 思否
高頻面試題:Spring 如何解決循環依賴? - 知乎 (zhihu.com)
Spring-bean的循環依賴以及解決方式_惜暮-CSDN部落格_spring 循環依賴

