Spring的循環(huán)依賴,到底是什么樣的
前一段時間,阿粉的讀者給阿粉留言,說在面試的時候,有個面試官就問她,Spring 的各種知識,Spring 的生命周期, Spring 的循環(huán)依賴是如何解決的。
就這么幾個問題,雖然回答的不是很好,但是也是很幸運的接到了 offer ,畢竟面試一般很少會因為一兩個面試題回答的不好,就直接 pass 的,還是看綜合表現(xiàn)的,既然問到阿粉這個 Spring 是如何處理循環(huán)依賴的了,那么阿粉就得來解釋一下,Spring 是如何處理循環(huán)依賴的。
循環(huán)依賴
什么是循環(huán)依賴,說到循環(huán)依賴,這個實際上是沒有那么復雜的,就比如很簡單的說,A 引用了 B ,而這個時候 B 也引用了 A ,那么這種情況實際上就是出現(xiàn)了循環(huán)依賴的問題了,實際上也可以把循環(huán)依賴稱之為循環(huán)引用,兩個或者兩個以上的bean互相持有對方,最終形成閉環(huán)。
這就是循環(huán)依賴,也就是循環(huán)引用,

注意,這里不是函數(shù)的循環(huán)調(diào)用,是對象的相互依賴關(guān)系。循環(huán)調(diào)用其實就是一個死循環(huán),除非有終結(jié)條件。否則的話,他就是一個死循環(huán).
Spring 中的循環(huán)依賴
那么 Spring 的循環(huán)依賴都有什么呢?
- 構(gòu)造器的循環(huán)依賴
- field屬性的循環(huán)依賴
那么針對這兩種循環(huán)依賴,Spring 它是如何解決的呢?這就很特殊了,構(gòu)造器的循環(huán)依賴問題實際上算是個無解的操作,只能拋出 BeanCurrentlyInCreationException 異常,也就是說,這個構(gòu)造器導致的循環(huán)依賴,Spring 是沒有辦法來處理的,也只是給拋出了異常,但是對于 字段屬性 的循環(huán)依賴,還是有解決辦法的。
Spring怎么解決循環(huán)依賴
這個時候,我們就得看看 Spring 的對象初始化的過程了,
Spring的單例對象的初始化主要分為三步:
- createBeanInstance 實例化
- populateBean 填充屬性
- initializeBean 初始化
createBeanInstance 實例化實際上就是調(diào)用對象的構(gòu)造方法實例化對象,populateBean 實際上就是對 bean 的依賴屬性進行一個賦值填充,而 initializeBean 則是調(diào)用 Spring xml 中的 init 方法。
這個時候,我們看到這個初始化的過程,一般就應(yīng)該能猜到會發(fā)生 循環(huán)依賴? 的位置是哪一步了,而單從 bean 的初始化來看,循環(huán)依賴發(fā)生的位置就是在 createBeanInstance 實例化? 以及 populateBean 填充屬性 當中,
發(fā)生的循環(huán)依賴也是
- 構(gòu)造器的循環(huán)依賴
- field屬性的循環(huán)依賴
那么 Spring 又是怎么解決這種單例的循環(huán)依賴的問題的呢?
?三級緩存
那么這三級緩存分別是哪三級的緩存呢?又分別代表了什么含義?
- singletonFactories :單例對象工廠的cache,用于存放完全初始化好的 bean,從該緩存中取出的 bean 可以直接使用
- earlySingletonObjects :提前暴光的單例對象的Cache,存放原始的 bean 對象(尚未填充屬性),用于解決循環(huán)依賴
- singletonObjects:單例對象的cache,存放 bean 工廠對象,用于解決循環(huán)依賴
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); //一級緩存
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); // 二級緩存
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16); // 三級緩存
如果要分析這個 三級緩存 如何解決循環(huán)依賴,那么勢必需要知道 Spring 中對象的創(chuàng)建的過程。
對象創(chuàng)建過程,可以大致分為五個步驟,
1.protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly)
AbstractBeanFactory? 中的 doGetBean()方法
2.protected Object getSingleton(String beanName, boolean allowEarlyReference)
DefaultSingletonBeanRegistry? 中的 getSingleton()方法
- 在這個方法中,先從一級緩存 singletonObjects 中去獲取。(如果獲取到就直接return)
- 如果獲取不到,并且對象正在創(chuàng)建中,就再從二級緩存 earlySingletonObjects 中獲取。
- 如果還是獲取不到且允許 singletonFactories? 通過 getObject()? 獲取,就從三級緩存singletonFactory.getObject()(三級緩存)獲取
- 如果獲取到了則:從 singletonFactories? 中移除,并放入 earlySingletonObjects 中
- 這就相當于 ctrl+x ,把三級緩存中的數(shù)據(jù)剪切到了二級緩存。
源碼如下:
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;
}
3.protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
AbstractAutowireCapableBeanFactory? 中的 doCreateBean() 方法
//添加到三級緩存
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
4.protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw)
AbstractAutowireCapableBeanFactory? 中的 populateBean() 方法進行屬性賦值
5.protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
AbstractAutowireCapableBeanFactory? 中的 initializeBean() 初始化對象
源碼部分阿粉就不再往上貼那么多了,大家找源碼肯定很簡單,內(nèi)部也有具體方法的注釋,
Spring 解決循環(huán)依賴的訣竅就在于 singletonFactories 這個三級cache。
這個 cache 的類型是 ObjectFactory?。這里就是解決循環(huán)依賴的關(guān)鍵,發(fā)生在createBeanInstance之后,也就是說單例對象此時已經(jīng)被創(chuàng)建出來(調(diào)用了構(gòu)造器)。
這個對象已經(jīng)被生產(chǎn)出來了,雖然還不完美(還沒有進行初始化的第二步和第三步),但是已經(jīng)能被人認出來了(根據(jù)對象引用能定位到堆中的對象),所以Spring此時將這個對象提前曝光出來讓大家認識,讓大家使用。
如果你能在面試的時候,回答成這個樣子,那么這個問題,你至少已經(jīng)算是回答的比較好了。
但是如果問到這里,面試官有意想要繼續(xù)深挖一下,你既然知道使用三級緩存解決了這個循環(huán)依賴的問題了,那么是不是必須三級緩存才能解決,二級緩存不能解決嗎?
這就另外又給你引申出一個問題來了,二級緩存到底能不能解決呢?
其實,二級緩存也是能夠?qū)崿F(xiàn)的,如果自己想要實現(xiàn),那么就得去改寫 AbstractAutowireCapableBeanFactory? 的 doCreateBean 的方法了,
//添加到三級緩存
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
//從三級緩存中取出立刻放入二級緩存
getSingleton(beanName, true);
}
如果要使用二級緩存解決循環(huán)依賴,意味著Bean在構(gòu)造完后就創(chuàng)建代理對象,這樣違背了Spring設(shè)計原則。
Spring結(jié)合AOP跟Bean的生命周期,是在Bean創(chuàng)建完全之后通過AnnotationAwareAspectJAutoProxyCreator這個后置處理器來完成的,在這個后置處理的postProcessAfterInitialization方法中對初始化后的Bean完成AOP代理。
如果出現(xiàn)了循環(huán)依賴,那沒有辦法,只有給Bean先創(chuàng)建代理,但是沒有出現(xiàn)循環(huán)依賴的情況下,設(shè)計之初就是讓Bean在生命周期的最后一步完成代理而不是在實例化后就立馬完成代理。
所以,你知道為什么不使用二級緩存直接來處理了,而是增加了三級緩存來處理這個循環(huán)依賴了吧!

























