日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

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

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

前言

在實際工作中,經(jīng)常由于設計不佳或者各種因素,導致類之間相互依賴。這些類可能單獨使用時不會出問題,但是在使用Spring進行管理的時候可能就會拋出BeanCurrentlyInCreationException等異常 。當拋出這種異常時表示Spring解決不了該循環(huán)依賴,本文將簡要說明Spring對于循環(huán)依賴的解決方法。

什么是循環(huán)依賴?

循環(huán)依賴其實就是循環(huán)引用,也就是兩個或則兩個以上的bean互相持有對方,最終形成閉環(huán)。比如A依賴于B,B依賴于C,C又依賴于A。如下圖:

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

注意,這里不是函數(shù)的循環(huán)調用,是對象的相互依賴關系。循環(huán)調用其實就是一個死循環(huán),除非有終結條件。

Spring中循環(huán)依賴場景有:

(1)構造器的循環(huán)依賴

(2)field屬性的循環(huán)依賴。

循環(huán)依賴的產生和解決的前提

循環(huán)依賴的產生可能有很多種情況,例如:

  1. A的構造方法中依賴了B的實例對象,同時B的構造方法中依賴了A的實例對象
  2. A的構造方法中依賴了B的實例對象,同時B的某個field或者setter需要A的實例對象,以及反之
  3. A的某個field或者setter依賴了B的實例對象,同時B的某個field或者setter依賴了A的實例對象,以及反之

當然,Spring對于循環(huán)依賴的解決不是無條件的,首先前提條件是針對scope單例并且沒有顯式指明不需要解決循環(huán)依賴的對象,而且要求該對象沒有被代理過。同時Spring解決循環(huán)依賴也不是萬能,以上三種情況只能解決兩種,第一種在構造方法中相互依賴的情況Spring也無力回天。結論先給在這,下面來看看Spring的解決方法,知道了解決方案就能明白為啥第一種情況無法解決了。

Spring怎么解決循環(huán)依賴

Spring的循環(huán)依賴的理論依據(jù)其實是基于JAVA的引用傳遞,當我們獲取到對象的引用時,對象的field或則屬性是可以延后設置的(但是構造器必須是在獲取引用之前)。

Spring的單例對象的初始化主要分為三步:

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

(1)createBeanInstance:實例化,其實也就是調用對象的構造方法實例化對象

(2)populateBean:填充屬性,這一步主要是多bean的依賴屬性進行填充

(3)initializeBean:調用spring xml中的init 方法。

從上面講述的單例bean初始化步驟我們可以知道,循環(huán)依賴主要發(fā)生在第一、第二部。也就是構造器循環(huán)依賴和field循環(huán)依賴。

那么我們要解決循環(huán)引用也應該從初始化過程著手,對于單例來說,在Spring容器整個生命周期內,有且只有一個對象,所以很容易想到這個對象應該存在Cache中,Spring為了解決單例的循環(huán)依賴問題,使用了三級緩存。

主要的幾個緩存

  1. alreadyCreated:
  2. 不管單例還是原型,均會被標記,主要用在循環(huán)依賴無法解決的時候擦屁股用的。
  3. singletonObjects:
  4. 單例Bean的緩存池
  5. singletonFactories:
  6. 單例Bean在創(chuàng)建之初過早的暴露出去的Factory,為什么采用工廠方式,是因為有些Bean是需要被代理的,總不能把代理前的暴露出去那就毫無意義了。
  7. earlySingletonObjects:
  8. 執(zhí)行了工廠方法生產出來的Bean,總不能每次判斷是否解決了循環(huán)依賴都要執(zhí)行下工廠方法吧,故而緩存起來。
  9. singletonsCurrentlyInCreation:
  10. 這個很明白了,如果以上的緩存都是用來解決循環(huán)依賴的話,那么這個緩存就是用來檢測是否存在循環(huán)依賴的。

主要步驟

1、判斷該Bean是否已經(jīng)在創(chuàng)建,是則拋異常

if (isPrototypeCurrentlyInCreation(beanName)) {

throw new BeanCurrentlyInCreationException(beanName);

}

2、標記該Bean已經(jīng)被創(chuàng)建,理由見下面

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暴露出去,而是暴露個Factory呢?因為有些Bean是需要被代理的,看下getEarlyBeanReference的實現(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;

}

那什么時候執(zhí)行這個工廠方法呢?當你依賴到了該Bean而單例緩存里面有沒有該Bean的時候就會調用該工廠方法生產Bean,看下getSingleton的實現(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)在有兩個疑問了,就舉個對Bean進行Wrap的操作吧,Spring是如何規(guī)避重復Wrap的呢?

1)從代碼可以看到,執(zhí)行完工廠方法會緩存到earlySingletonObjects中,因此再次調用該方法不會重復執(zhí)行Wrap的

2)對于有些BeanPostProcessor提供對Bean的Wrap的操作,但是生命周期位于在set操作之后,如果提前暴露出去被其他Bean執(zhí)行了工廠方法給Wrap起來,回過來自己再執(zhí)行BeanPostProcessor的后處理操作的時候不會發(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;

}

這個BeanPostProcessor很典型了,用于創(chuàng)建代理類的。一般這種BeanPostProcessor總要提供一個getEarlyBeanReference的接口供其他Bean使用,而又防止了其他類直接使用到該類最原始的版本。這就是上述兩個方法如此相似的原因。置于略微的差異,你應該看出來,是防止重復執(zhí)行方法。

4、初始化Bean,執(zhí)行一個個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會不會改變Bean的版本,當然,如果改變了,肯定要出錯的,在這里,Spring就沒有做依賴解決了(都給你把代理類解決了你還想啥呢),只是做了檢查,如下。

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)當發(fā)現(xiàn)最初的Bean和ExposedObject不一致的時候(這里其實值得推敲的,只有BeanPostProcessor會導致Bean的版本更新,但是負責處理代理的BeanPostProcessor也會導致版本更新,最后豈不是和普通的BeanPostProcessor一樣走到else里面去拋異常了,詭異了!仔細想想負責處理代理的BeanPostProcessor是不會導致Bean的版本更新的,所以最后要把getSingleton取出來的最新版本的Bean賦給它,好好理解吧,真不知道怎么解釋了)就會走到else里面,看看else里面的邏輯:

2)檢查所以依賴到該Bean的哪些Bean們,如果他們已經(jīng)創(chuàng)建了,那么拋異常!這就是為什么用alreadyCreated的原因,因為原型Bean C如果依賴到了該Bean A的話,原型Bean C還能用么?當然作廢了,而且還無法解決,框架只能拋異常告訴程序員。

分享到:
標簽:Spring
用戶無頭像

網(wǎng)友整理

注冊時間:

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

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

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

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

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

答題星2018-06-03

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

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數(shù)有氧達人2018-06-03

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

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

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

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定