亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

前言

在實(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。如下圖:

Spring 是如何解決循環(huán)依賴的

注意,這里不是函數(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)生可能有很多種情況,例如:

  1. A的構(gòu)造方法中依賴了B的實(shí)例對(duì)象,同時(shí)B的構(gòu)造方法中依賴了A的實(shí)例對(duì)象
  2. A的構(gòu)造方法中依賴了B的實(shí)例對(duì)象,同時(shí)B的某個(gè)field或者setter需要A的實(shí)例對(duì)象,以及反之
  3. 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ì)象的初始化主要分為三步:

Spring 是如何解決循環(huán)依賴的

(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è)緩存

  1. alreadyCreated:
  2. 不管單例還是原型,均會(huì)被標(biāo)記,主要用在循環(huán)依賴無(wú)法解決的時(shí)候擦屁股用的。
  3. singletonObjects:
  4. 單例Bean的緩存池
  5. singletonFactories:
  6. 單例Bean在創(chuàng)建之初過(guò)早的暴露出去的Factory,為什么采用工廠方式,是因?yàn)橛行〣ean是需要被代理的,總不能把代理前的暴露出去那就毫無(wú)意義了。
  7. earlySingletonObjects:
  8. 執(zhí)行了工廠方法生產(chǎn)出來(lái)的Bean,總不能每次判斷是否解決了循環(huán)依賴都要執(zhí)行下工廠方法吧,故而緩存起來(lái)。
  9. singletonsCurrentlyInCreation:
  10. 這個(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ú)法解決,框架只能拋異常告訴程序員。

分享到:
標(biāo)簽:Spring
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定