文章內(nèi)容來源:拉勾教育Java高薪訓(xùn)練營(yíng)8期
知識(shí)的獲取,來源于輸出!這是我在拉勾教育Java高薪訓(xùn)練營(yíng)8期學(xué)習(xí)后的一個(gè)總結(jié)與分享,選取spring的循環(huán)依賴處理這一知識(shí)點(diǎn)做總結(jié),因?yàn)槲矣X得它涵蓋spring創(chuàng)建bean的流程,既梳理了循環(huán)依賴,也能對(duì)bean的創(chuàng)建流程進(jìn)行一個(gè)回顧。
1.談?wù)勗诶唇逃?xùn)練營(yíng)的學(xué)習(xí)感受
1.1 報(bào)名學(xué)習(xí)初衷
技術(shù)遇到瓶頸,目前的工作技術(shù)對(duì)技術(shù)提升難度大,給自己一個(gè)提升于與學(xué)習(xí)的機(jī)會(huì)
1.2 學(xué)習(xí)方式
直播+錄播,有班主任負(fù)責(zé)學(xué)習(xí)事務(wù),導(dǎo)師負(fù)責(zé)指導(dǎo)學(xué)習(xí)
1.3 學(xué)習(xí)感受
老師講課能夠由淺入深,有深度也有廣度,跟著老師學(xué)下來。對(duì)相對(duì)復(fù)雜的知識(shí)也能掌握(如框架源碼),最終能夠手寫實(shí)現(xiàn)核心邏輯
1.4 老師
班主任認(rèn)真負(fù)責(zé),經(jīng)常我們監(jiān)督學(xué)習(xí)和服務(wù)同學(xué)們,導(dǎo)師也是有問必答,幫助同學(xué)學(xué)習(xí)、解惑
2.spring循環(huán)依賴處理機(jī)制
2.1 什么是循環(huán)依賴
循環(huán)依賴就是指對(duì)象之間的相互引用,通俗來講,就是兩個(gè)或者兩個(gè)以上的對(duì)象互相持有對(duì)方的引用,用代碼表示就是:
public class LagouBean {
private ItBean itBean;
...
}
public class ItBean {
private LagouBean lagouBean;
...
}
在開發(fā)中使用spring時(shí),對(duì)象一般都是交給spring來創(chuàng)建和管理,就像下面這樣:
<bean id="lagouBean" class="com.lagou.edu.LagouBean">
<property name="ItBean" ref="itBean"/>
</bean>
<bean id="itBean" class="com.lagou.edu.ItBean">
<property name="LagouBean" ref="lagouBean"/>
</bean>
如上兩個(gè)類,在創(chuàng)建LagouBean的時(shí)候發(fā)現(xiàn)它依賴了ItBean,于是要先去創(chuàng)建ItBean,這時(shí)候發(fā)現(xiàn)ItBean又依賴了LagouBean,此時(shí)又要去創(chuàng)建LagouBean,好像陷入了一種死循環(huán)...但在使用spring的時(shí)候,發(fā)現(xiàn)它并不會(huì)進(jìn)入死循環(huán)導(dǎo)致程序異常,反而能夠正常創(chuàng)建對(duì)象供程序調(diào)用,那么它是如何處理的?
2.2 spring如何處理循環(huán)依賴
經(jīng)過翻閱spring創(chuàng)建bean部分的源碼得知,spring處理循環(huán)依賴的核心思想是基于java的引用傳遞,獲得對(duì)象的引用時(shí),屬性是可以延后設(shè)置的。利用這一特性,spring在創(chuàng)建對(duì)象時(shí),先不設(shè)置其屬性,實(shí)例化后提前暴露到容器中,提供給其他對(duì)象引用。
所以創(chuàng)建LagouBean時(shí),實(shí)例化后立即暴露到容器中,然后填充屬性,此時(shí)發(fā)現(xiàn)它依賴了ItBean,則先去創(chuàng)建ItBean,ItBean實(shí)例化后也會(huì)提前暴露,后填充屬性,此時(shí)發(fā)現(xiàn)ItBean依賴了LagouBean,因?yàn)長(zhǎng)agouBean提前已經(jīng)暴露出來了,就可以從容器中取到完成ItBean的創(chuàng)建,ItBean創(chuàng)建完成,填充到LagouBean完成創(chuàng)建。
spring為了完成這一過程,采用了三級(jí)緩存機(jī)制,對(duì)象的提前暴露就是將創(chuàng)建的對(duì)象事先放入緩存之中。在org.springframework.beans.factory.support.DefaultSingletonBeanRegistry類中,定義了這三個(gè)緩存:
Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
Map<String, Object> earlySingletonObjects = new HashMap<>(16);
- singletonObjects:一級(jí)緩存,存放創(chuàng)建完成的單例對(duì)象,是完完整整的對(duì)象,經(jīng)歷了實(shí)例化、屬性填充、后置處理等一系列步驟后創(chuàng)建的對(duì)象
- singletonFactories:三級(jí)緩存,存放的是將實(shí)例化后的bean包裝成的ObjectFactory
- earlySingletonObjects:二級(jí)緩存,存放的是提前暴露的bean,未填充屬性,但是已經(jīng)可以被其他對(duì)象引用了,這里bean的獲取主要是調(diào)用三級(jí)緩存中存放的ObjectFactory的getObject()方法創(chuàng)建
綜上,spring處理循環(huán)依賴的思想主要基于java引用傳遞,采用三級(jí)緩存機(jī)制,將對(duì)象僅實(shí)例化后提前暴露存放至緩存中,使得依賴它的對(duì)象可以獲取其引用完成創(chuàng)建,然后進(jìn)行屬性填充、初始化等操作,最后完成對(duì)象的創(chuàng)建。附上一張學(xué)習(xí)時(shí)的圖方便理解:

3.spring循環(huán)依賴處理源碼解析
經(jīng)過上一部分的大致分析,這部分將對(duì)源碼進(jìn)行一個(gè)解讀,深入了解一下spring處理循環(huán)依賴的整個(gè)流程,寫了如下一段測(cè)試代碼:
public void testIoC() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
LagouBean lagouBean = applicationContext.getBean(LagouBean.class);
System.out.println(lagouBean);
}
3.1 容器初始化源碼解析
如上面的測(cè)試代碼,使用了ClassPathXmlApplicationContext容器作為測(cè)試對(duì)象,通過代碼調(diào)試跟蹤,最開始調(diào)用了ClassPathXmlApplicationContext的構(gòu)造方法,方法內(nèi)部調(diào)用了org.springframework.context.support.AbstractApplicationContext#refresh()來完成容器的創(chuàng)建
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)throws BeansException {
//初始化父類
super(parent);
//設(shè)置配置信息
setConfigLocations(configLocations);
//完成容器的初始化
if (refresh) {
refresh();
}
}
在refresh()方法的核心就是完成BeanFactory的創(chuàng)建,加載beandefition,然后進(jìn)行bean的創(chuàng)建
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//創(chuàng)建beanfactory,加載beandefition注冊(cè)到beandefiitoRegisty
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//初始化bean,填充屬性,初始化方法調(diào)用..
finishBeanFactoryInitialization(beanFactory);
}
}
在finishBeanFactoryInitialization()方法中,調(diào)用了org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons()方法
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
//實(shí)例化所有非延遲加載的bean
beanFactory.preInstantiateSingletons();
}
preInstantiateSingletons()中調(diào)用了org.springframework.beans.factory.support.AbstractBeanFactory#getBean(String name)方法,Bean的創(chuàng)建流程就是從這里開始的,getBean()自調(diào)用了doGetBean()方法,開始真正創(chuàng)建bean了
3.2 獲取對(duì)象doGetBean方法解析
protected <T> T doGetBean() throws BeansException {
Object bean;
//標(biāo)記:1
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
try {
if (mbd.isSingleton()) {
//標(biāo)記:2
sharedInstance = getSingleton(beanName, () -> {
// 創(chuàng)建單例Bean的主要方法,返回的bean是完整的
return createBean(beanName, mbd, args);
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
}
return (T) bean;
}
doGetBean()方法的主要邏輯為:
- 根據(jù)beanName嘗試從緩存中取對(duì)象LagouBean
- 取到就返回對(duì)應(yīng)的實(shí)例
- 未取到,則進(jìn)行bean的創(chuàng)建
其中重點(diǎn)標(biāo)記處:
- 標(biāo)記1: 調(diào)用
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(String beanName)方法,嘗試從緩存中取對(duì)象 - 標(biāo)記2: 判斷如果要?jiǎng)?chuàng)建的對(duì)象是單例對(duì)象,調(diào)用getSingleton的重載方法
getSingleton(String beanName, ObjectFactory<?> singletonFactory)進(jìn)行對(duì)象的創(chuàng)建,它的內(nèi)部,有兩段重點(diǎn)代碼:
//標(biāo)記:2-1
singletonObject = singletonFactory.getObject();
//標(biāo)記:2-2
addSingleton(beanName, singletonObject);
- 標(biāo)記2-1: 從上面的標(biāo)記2中可以看出singletonFactory.getObject()調(diào)用的是方法
createBean(beanName, mbd, args)返回最終創(chuàng)建的對(duì)象 - 標(biāo)記2-2: 新創(chuàng)建的bean添加到【一級(jí)緩存】中,以后getBean的時(shí)候就可以直接獲取了,源碼如下:
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// 將新創(chuàng)建的bean添加到一級(jí)緩存中
this.singletonObjects.put(beanName, singletonObject);
// 從其他緩存中移除相關(guān)的bean
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
}
}
3.3 創(chuàng)建對(duì)象doCreateBean源碼解析
上面說到創(chuàng)建對(duì)象的方法在createBean(beanName, mbd, args),內(nèi)部調(diào)用了org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean方法,其內(nèi)部邏輯分為5大步,這里將其拆開來剖析:
3.3.1 實(shí)例化對(duì)象
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
BeanWrapper instanceWrapper = null;
// 調(diào)用無參構(gòu)造實(shí)例化Bean
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// 實(shí)例化后的Bean對(duì)象,這里獲取到的是一個(gè)原始對(duì)象,即沒有進(jìn)行屬性填充的對(duì)象
final Object bean = instanceWrapper.getWrappedInstance();
}
這部分的主要邏輯就是調(diào)用無參構(gòu)造實(shí)例化,拿到一個(gè)原始的Bean
3.3.2 將原始對(duì)象放入三級(jí)緩存
protected Object doCreateBean() {
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
}
- earlySingletonExposure:是否”提前暴露“原始對(duì)象的引用,如果需要提前暴露單例bean,則將該bean工廠放入【三級(jí)緩存】中
- 方法
addSingletonFactory則是將原始對(duì)象包裝成ObjectFactory放入三級(jí)緩存,源碼如下:
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
//向三級(jí)緩存里添加ObjectFactory
this.singletonFactories.put(beanName, singletonFactory);
}
}
}
- 匿名內(nèi)部方法
getEarlyBeanReference是SmartInstantiationAwareBeanPostProcessor接口的的一個(gè)默認(rèn)方法,實(shí)現(xiàn)這個(gè)方法的只有org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator這個(gè)類,實(shí)現(xiàn)邏輯為:
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
它內(nèi)部自調(diào)用了方法 wrapIfNecessary:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// Create proxy if we have advice.
if (specificInterceptors != DO_NOT_PROXY) {
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
return proxy;
}
return bean;
}
這個(gè)方法關(guān)鍵點(diǎn)在 createProxy,顧名思義它是為bean創(chuàng)建代理對(duì)象的,所以它的整個(gè)邏輯就是,如果當(dāng)前bean需要?jiǎng)?chuàng)建代理對(duì)象,那么它返回的就是代理對(duì)象,否則返回原始對(duì)象。
那么getEarlyBeanReference在什么時(shí)候調(diào)用的?
還記得在3.2章節(jié)講述doGetBean方法時(shí)提到首先會(huì)從緩存中嘗試獲取bean嗎?如下:
protected <T> T doGetBean() throws BeansException {
Object sharedInstance = getSingleton(beanName);
}
調(diào)用的getSingleton方法就是嘗試從緩存中獲取bean,這里來看一下它的源碼:
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// 從 singletonObjects 獲取實(shí)例,singletonObjects 中的實(shí)例都是準(zhǔn)備好的 bean 實(shí)例,可以直接使用
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// 從二級(jí)緩存中獲取單例bean
singletonObject = this.earlySingletonObjects.get(beanName);
// allowEarlyReference :表示是否允許從singletonFactories中通過getObject拿到對(duì)象
if (singletonObject == null && allowEarlyReference) {
// 從三級(jí)緩存中獲取單例bean,并移動(dòng)到二級(jí)緩存
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 通過單例工廠獲取單例bean
singletonObject = singletonFactory.getObject();
// 從三級(jí)緩存移動(dòng)到了二級(jí)緩存,并移除singletonFactory
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
可以看到,從三級(jí)緩存獲取對(duì)象時(shí),調(diào)用了singletonFactory.getObject(),這里最終調(diào)用的就是匿名內(nèi)部方法getEarlyBeanReference。
所以可以說三級(jí)緩存的功效就是,在bean放入【二級(jí)代理】之前,通過ObjectFactory為當(dāng)前bean創(chuàng)建代理對(duì)象(如果需要的話),保證別的bean引用它時(shí)是一個(gè)代理對(duì)象。
3.3.3 屬性填充
經(jīng)過上一小節(jié)的分析可知:
- 原始對(duì)象已經(jīng)創(chuàng)建成功
- 原始對(duì)象包裝成ObjectFactory放入了三級(jí)緩存
接下來,就是對(duì)bean進(jìn)行屬性填充了
protected Object doCreateBean() {
...
populateBean(beanName, mbd, instanceWrapper);
...
}
屬性填充調(diào)用了populateBean方法,它最終調(diào)用了 applyPropertyValues進(jìn)行屬性填充:
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
for (PropertyValue pv : original) {
if (pv.isConverted()) {
deepCopy.add(pv);
}
else {
//獲取依賴的bean
String propertyName = pv.getName();
Object originalValue = pv.getValue();
//獲取依賴的對(duì)象
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
}
}
方法applyPropertyValues內(nèi)部遍歷處理依賴,獲取依賴的bean,調(diào)用org.springframework.beans.factory.support.BeanDefinitionValueResolver#resolveValueIfNecessary方法去獲取被依賴的對(duì)象:
public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
return resolveReference(argName, ref);
}
}
方法內(nèi)部又自調(diào)用了resolveValueIfNecessary方法:
private Object resolveReference(Object argName, RuntimeBeanReference ref) {
//獲取或創(chuàng)建依賴對(duì)象
bean = this.beanFactory.getBean(refName);
return bean;
}
方法 resolveReference根據(jù)獲取的beanName調(diào)用beanFactory的getBean方法去獲取依賴的bean,此時(shí)如果bean還未被創(chuàng)建,則會(huì)走一遍創(chuàng)建bean的流程,之后拿到依賴的bean,進(jìn)行屬性填充。
以文章開頭的LagouBean與ItBean為例,說明一下整個(gè)流程:
(1)首先嘗試從緩存中獲取LagouBean,發(fā)現(xiàn)沒有,則創(chuàng)建LagouBean,實(shí)例化后,放入三級(jí)緩存
(2)為L(zhǎng)agouBean填充屬性,發(fā)現(xiàn)依賴了ItBean
(3)創(chuàng)建ItBean,實(shí)例化后,放入三級(jí)緩存
(4)為ItBean填充屬性,發(fā)現(xiàn)依賴了LagouBean,于是嘗試從緩存中獲取LagouBean
(5)發(fā)現(xiàn)LagouBean在三級(jí)緩存中,則調(diào)用對(duì)應(yīng)的objectFactory.getObject()拿到對(duì)象LagouBean(如果LagouBean需要?jiǎng)?chuàng)建代理,也是在這一步完成的)
(6)拿到對(duì)象LagouBean后移動(dòng)到二級(jí)緩存,可供其他對(duì)象引用了,ItBean也因此可以完成屬性填充,初始化,完成創(chuàng)建,最后放入一級(jí)緩存,返回給LagouBean
(7) LagouBean拿到ItBean,繼續(xù)走填充流程,直到填充完畢,創(chuàng)建完成,放入一級(jí)緩存,到此LagouBean就可以使用了,解決了循環(huán)依賴問題
通過上述流程發(fā)現(xiàn),如果LagouBean 與ItBean都需要?jiǎng)?chuàng)建代理對(duì)象,LagouBean的代理對(duì)象的創(chuàng)建在第(5)可完成,因?yàn)镮tBean的創(chuàng)建在LagouBean的屬性填充過程中,創(chuàng)建完直接放入了一級(jí)緩存,沒有走二級(jí)緩存,ItBean的代理對(duì)象好像并沒有創(chuàng)建的地方,那么spring如何保證LagouBean最終依賴的是ItBean的代理對(duì)象呢?其實(shí)ItBean的代理對(duì)象發(fā)生在bean初始化這一步,它在屬性填充之后執(zhí)行。
3.3.4 初始化
初始化就是給創(chuàng)建好的bean做后置處理,就像創(chuàng)建了一個(gè)人,populateBean就是給人填充各種五臟六腑,血肉...初始化就是給人穿上外衣,如下圖源碼所示,initializeBean方法就是對(duì)bean進(jìn)行初始化
protected Object doCreateBean() {
//填充屬性(依賴注入)
populateBean(beanName, mbd, instanceWrapper);
//調(diào)用初始化方法,完成bean的初始化操作
exposedObject = initializeBean(beanName, exposedObject, mbd);
return exposedObject;
}
initializeBean方法做了4個(gè)動(dòng)作,如下
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
invokeAwareMethods(beanName, bean);
Object wrappedBean = bean;
//before
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
//觸發(fā)初始化方法的調(diào)用
invokeInitMethods(beanName, wrappedBean, mbd);
//after
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
return wrappedBean;
}
-
invokeAwareMethods方法主要給bean設(shè)置beanName,beanfactory,applicationContext -
applyBeanPostProcessorsBeforeInitialization指預(yù)初始化方法,內(nèi)部比較簡(jiǎn)單 -
invokeInitMethods方法做了兩件事:一個(gè)是調(diào)?InitializingBean
的afterPropertiesSet?法,一個(gè)是調(diào)用自定義的init方法
protected void invokeInitMethods() {
//調(diào)用InitializingBean的afterPropertiesSet()
((InitializingBean) bean).afterPropertiesSet();
//調(diào)用自定義的init方法
invokeCustomInitMethod(beanName, bean, mbd);
}
-
applyBeanPostProcessorsAfterInitialization后初始化方法,它內(nèi)部調(diào)用了接口org.springframework.beans.factory.config. BeanPostProcessor的postProcessAfterInitialization方法,這個(gè)方法的實(shí)現(xiàn)類為org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator,它的源碼如下,有兩個(gè)重要的方法:
public abstract class AbstractAutoProxyCreator{
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
//放進(jìn)緩存earlyProxyReferences里,bean此時(shí)是原始對(duì)象,標(biāo)記該對(duì)象是否被循環(huán)依賴
this.earlyProxyReferences.put(cacheKey, bean);
//創(chuàng)建代理對(duì)象
return wrapIfNecessary(bean, beanName, cacheKey);
}
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// remove方法返回記錄的bean
// 若被循環(huán)引用了,說明執(zhí)行了getEarlyBeanReference方法,remove返回值是==bean的
// 若沒被循環(huán)引用,getEarlyBeanReference沒執(zhí)行 remove方法返回null,所以就進(jìn)入創(chuàng)建代理對(duì)象步驟
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
}
在3.3.2章節(jié)中講過,getEarlyBeanReference是處理循環(huán)依賴的關(guān)鍵方法,它的執(zhí)行要先于postProcessAfterInitialization,功能就是創(chuàng)建好代理對(duì)象,最終放入二級(jí)緩存,提前暴露代理對(duì)象的引用。
postProcessAfterInitialization方法執(zhí)行在處理循環(huán)依賴之后,所以先進(jìn)行判斷有沒有被循環(huán)引用過,沒有循環(huán)引用過,則會(huì)創(chuàng)建它的代理對(duì)象返回。
所以,前文中所說的ItBean沒有被循環(huán)引用,如果它需要?jiǎng)?chuàng)建代理對(duì)象的話,在postProcessAfterInitialization這里就會(huì)去創(chuàng)建它的代理對(duì)象。最終保證依賴ItBean的類也能拿到它的代理對(duì)象引用。
到此,循環(huán)依賴處理也就講述結(jié)束了。最后附上一張我自己手繪的圖方便理解:

4.spring循環(huán)依賴常見問題總結(jié)
1.作用域?yàn)閜rototype的 bean對(duì)象支持循環(huán)依賴處理嗎?
不支持。prototype的 bean存在循環(huán)引用的話會(huì)直接跑出異常org.springframework.beans.factory.BeanCurrentlyInCreationException,spring解決循環(huán)依賴的核心思想就是基于java的引用對(duì)bean緩存,提前曝光給其他對(duì)象引用,對(duì)于prototype類型的每次請(qǐng)求都會(huì)創(chuàng)建一個(gè)bean,循環(huán)引用太多了的話,無法進(jìn)行緩存處理,也就無法提前曝光引用。
2.如果構(gòu)造器注入方式也有循環(huán)依賴情況,支持嗎?
不支持。spring處理循環(huán)依賴的核心思想是基于java的引用傳遞,提前實(shí)例化好bean進(jìn)行曝光。實(shí)例化bean需要調(diào)用構(gòu)造器,存在循環(huán)依賴的情況時(shí),會(huì)無法實(shí)例化提前曝光,所以構(gòu)造器的循環(huán)依賴無法解決。
3.二級(jí)緩存能解決循環(huán)依賴嗎?
- 沒有AOP的情況下,二級(jí)緩存能解決循環(huán)依賴
- 有AOP的情況下,就無法解決了,因?yàn)樽⑷氲狡渌鸼ean里的要是一個(gè)代理對(duì)象,如果只有二級(jí)緩存,那么注入到其他bean里的就有可能是個(gè)原始對(duì)象。這里有人說Bean 在實(shí)例化后就完成 代理對(duì)象的創(chuàng)建不就解決問題了嗎?這樣的話,其實(shí)連二級(jí)緩存都不需要了,一個(gè)緩存就夠了,里面又處理原始對(duì)象,又處理代理對(duì)象,又要完成初始化等,這么想想就不是很優(yōu)雅,也違背了單一原則,使用三級(jí)緩存各司其職,也體現(xiàn)了spring在設(shè)計(jì)上的優(yōu)雅。