前言
在實(shí)際工作中,經(jīng)常由于設(shè)計(jì)不佳或者各種因素,導(dǎo)致類之間相互依賴。這些類可能單獨(dú)使用時(shí)不會(huì)出問(wèn)題,但是在使用Spring進(jìn)行管理的時(shí)候可能就會(huì)拋出BeanCurrentlyInCreationException等異常 。當(dāng)拋出這種異常時(shí)表示Spring解決不了該循環(huán)依賴,本文將簡(jiǎn)要說(shuō)明Spring對(duì)于循環(huán)依賴的解決方法。
什么是循環(huán)依賴?
循環(huán)依賴其實(shí)就是循環(huán)引用,也就是兩個(gè)或則兩個(gè)以上的bean互相持有對(duì)方,最終形成閉環(huán)。比如A依賴于B,B依賴于C,C又依賴于A。如下圖:
注意,這里不是函數(shù)的循環(huán)調(diào)用,是對(duì)象的相互依賴關(guān)系。循環(huán)調(diào)用其實(shí)就是一個(gè)死循環(huán),除非有終結(jié)條件。
Spring中循環(huán)依賴場(chǎng)景有:
(1)構(gòu)造器的循環(huán)依賴
(2)field屬性的循環(huán)依賴。
循環(huán)依賴的產(chǎn)生和解決的前提
循環(huán)依賴的產(chǎn)生可能有很多種情況,例如:
- A的構(gòu)造方法中依賴了B的實(shí)例對(duì)象,同時(shí)B的構(gòu)造方法中依賴了A的實(shí)例對(duì)象
- A的構(gòu)造方法中依賴了B的實(shí)例對(duì)象,同時(shí)B的某個(gè)field或者setter需要A的實(shí)例對(duì)象,以及反之
- A的某個(gè)field或者setter依賴了B的實(shí)例對(duì)象,同時(shí)B的某個(gè)field或者setter依賴了A的實(shí)例對(duì)象,以及反之
當(dāng)然,Spring對(duì)于循環(huán)依賴的解決不是無(wú)條件的,首先前提條件是針對(duì)scope單例并且沒(méi)有顯式指明不需要解決循環(huán)依賴的對(duì)象,而且要求該對(duì)象沒(méi)有被代理過(guò)。同時(shí)Spring解決循環(huán)依賴也不是萬(wàn)能,以上三種情況只能解決兩種,第一種在構(gòu)造方法中相互依賴的情況Spring也無(wú)力回天。結(jié)論先給在這,下面來(lái)看看Spring的解決方法,知道了解決方案就能明白為啥第一種情況無(wú)法解決了。
Spring怎么解決循環(huán)依賴
Spring的循環(huán)依賴的理論依據(jù)其實(shí)是基于JAVA的引用傳遞,當(dāng)我們獲取到對(duì)象的引用時(shí),對(duì)象的field或則屬性是可以延后設(shè)置的(但是構(gòu)造器必須是在獲取引用之前)。
Spring的單例對(duì)象的初始化主要分為三步:
(1)createBeanInstance:實(shí)例化,其實(shí)也就是調(diào)用對(duì)象的構(gòu)造方法實(shí)例化對(duì)象
(2)populateBean:填充屬性,這一步主要是多bean的依賴屬性進(jìn)行填充
(3)initializeBean:調(diào)用spring xml中的init 方法。
從上面講述的單例bean初始化步驟我們可以知道,循環(huán)依賴主要發(fā)生在第一、第二部。也就是構(gòu)造器循環(huán)依賴和field循環(huán)依賴。
那么我們要解決循環(huán)引用也應(yīng)該從初始化過(guò)程著手,對(duì)于單例來(lái)說(shuō),在Spring容器整個(gè)生命周期內(nèi),有且只有一個(gè)對(duì)象,所以很容易想到這個(gè)對(duì)象應(yīng)該存在Cache中,Spring為了解決單例的循環(huán)依賴問(wèn)題,使用了三級(jí)緩存。
主要的幾個(gè)緩存
- alreadyCreated:
- 不管單例還是原型,均會(huì)被標(biāo)記,主要用在循環(huán)依賴無(wú)法解決的時(shí)候擦屁股用的。
- singletonObjects:
- 單例Bean的緩存池
- singletonFactories:
- 單例Bean在創(chuàng)建之初過(guò)早的暴露出去的Factory,為什么采用工廠方式,是因?yàn)橛行〣ean是需要被代理的,總不能把代理前的暴露出去那就毫無(wú)意義了。
- earlySingletonObjects:
- 執(zhí)行了工廠方法生產(chǎn)出來(lái)的Bean,總不能每次判斷是否解決了循環(huán)依賴都要執(zhí)行下工廠方法吧,故而緩存起來(lái)。
- singletonsCurrentlyInCreation:
- 這個(gè)很明白了,如果以上的緩存都是用來(lái)解決循環(huán)依賴的話,那么這個(gè)緩存就是用來(lái)檢測(cè)是否存在循環(huán)依賴的。
主要步驟
1、判斷該Bean是否已經(jīng)在創(chuàng)建,是則拋異常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
2、標(biāo)記該Bean已經(jīng)被創(chuàng)建,理由見(jiàn)下面
protected void markBeanAsCreated(String beanName) {
this.alreadyCreated.add(beanName);
}
3、初始化Bean之前提前把Factory暴露出去
addSingletonFactory(beanName, new ObjectFactory() {
public Object getObject() throws BeansException {
return getEarlyBeanReference(beanName, mbd, bean);
}
});
為什么不把Bean暴露出去,而是暴露個(gè)Factory呢?因?yàn)橛行〣ean是需要被代理的,看下getEarlyBeanReference的實(shí)現(xiàn):
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (Iterator it = getBeanPostProcessors().iterator(); it.hasNext(); ) {
BeanPostProcessor bp = (BeanPostProcessor) it.next();
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
return exposedObject;
}
那什么時(shí)候執(zhí)行這個(gè)工廠方法呢?當(dāng)你依賴到了該Bean而單例緩存里面有沒(méi)有該Bean的時(shí)候就會(huì)調(diào)用該工廠方法生產(chǎn)Bean,看下getSingleton的實(shí)現(xiàn):
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory singletonFactory = (ObjectFactory) this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
現(xiàn)在有兩個(gè)疑問(wèn)了,就舉個(gè)對(duì)Bean進(jìn)行Wrap的操作吧,Spring是如何規(guī)避重復(fù)Wrap的呢?
1)從代碼可以看到,執(zhí)行完工廠方法會(huì)緩存到earlySingletonObjects中,因此再次調(diào)用該方法不會(huì)重復(fù)執(zhí)行Wrap的
2)對(duì)于有些BeanPostProcessor提供對(duì)Bean的Wrap的操作,但是生命周期位于在set操作之后,如果提前暴露出去被其他Bean執(zhí)行了工廠方法給Wrap起來(lái),回過(guò)來(lái)自己再執(zhí)行BeanPostProcessor的后處理操作的時(shí)候不會(huì)發(fā)生重復(fù)嗎?
【AbstractAutoProxyCreator】
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.add(cacheKey);
return wrapIfNecessary(bean, beanName, cacheKey);
}
【AbstractAutoProxyCreator】
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
return bean;
}
這個(gè)BeanPostProcessor很典型了,用于創(chuàng)建代理類的。一般這種BeanPostProcessor總要提供一個(gè)getEarlyBeanReference的接口供其他Bean使用,而又防止了其他類直接使用到該類最原始的版本。這就是上述兩個(gè)方法如此相似的原因。置于略微的差異,你應(yīng)該看出來(lái),是防止重復(fù)執(zhí)行方法。
4、初始化Bean,執(zhí)行一個(gè)個(gè)BeanPostProcessor
protected Object initializeBean(String beanName, Object bean, RootBeanDefinition mbd) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(this);
}
Object wrAppedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
你不能保證這些亂七八糟的BeanPostProcessor會(huì)不會(huì)改變Bean的版本,當(dāng)然,如果改變了,肯定要出錯(cuò)的,在這里,Spring就沒(méi)有做依賴解決了(都給你把代理類解決了你還想啥呢),只是做了檢查,如下。
5、循環(huán)依賴解決不了的情況下的依賴檢查
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set actualDependentBeans = new LinkedHashSet(dependentBeans.length);
for (int i = 0; i < dependentBeans.length; i++) {
String dependentBean = dependentBeans[i];
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
1)當(dāng)發(fā)現(xiàn)最初的Bean和ExposedObject不一致的時(shí)候(這里其實(shí)值得推敲的,只有BeanPostProcessor會(huì)導(dǎo)致Bean的版本更新,但是負(fù)責(zé)處理代理的BeanPostProcessor也會(huì)導(dǎo)致版本更新,最后豈不是和普通的BeanPostProcessor一樣走到else里面去拋異常了,詭異了!仔細(xì)想想負(fù)責(zé)處理代理的BeanPostProcessor是不會(huì)導(dǎo)致Bean的版本更新的,所以最后要把getSingleton取出來(lái)的最新版本的Bean賦給它,好好理解吧,真不知道怎么解釋了)就會(huì)走到else里面,看看else里面的邏輯:
2)檢查所以依賴到該Bean的哪些Bean們,如果他們已經(jīng)創(chuàng)建了,那么拋異常!這就是為什么用alreadyCreated的原因,因?yàn)樵虰ean C如果依賴到了該Bean A的話,原型Bean C還能用么?當(dāng)然作廢了,而且還無(wú)法解決,框架只能拋異常告訴程序員。






