Spring 循環(huán)依賴問題fix

Spring 循環(huán)依賴問題fix

拆分的時候覆山,把error都處理完后歉提,準(zhǔn)備把工程起起來疏唾,發(fā)現(xiàn)spring的循環(huán)依賴問題。具體問題如下

Bean with name 'userManager' has been injected into other beans [daoAuthenticationProvider] in its raw version as part of a circular reference, but has eventually been wrapped (for example as part of auto-proxy creation). 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. 懷疑配置文件的問題

但是在原工程中并沒有這個問題摊滔,所以一開始懷疑是配置文件的配置不一樣阴绢,百度了一下這個error

beanFactory.setAllowRawInjectionDespiteWrapping(true); 

看網(wǎng)上說這個配置了,對于循環(huán)依賴的這個error就會解決掉艰躺。但是在兩個工程里搜索了一下都沒有發(fā)現(xiàn)這個配置過呻袭。
于是只能debug進(jìn)去看看

2. debug 查看 分析

2.1 spring 引用的bean和注入的bean不一致導(dǎo)致的這個error

由于在原工程里是可以循環(huán)引用的,所以對工程和新工程都在初始化這兩個循環(huán)引用的位置進(jìn)行了debug
然后發(fā)現(xiàn)最后兩邊走的邏輯不一樣的在以下的代碼里:

AbstractAutowireCapableBeanFactory.doCreateBean()final String beanName, final RootBeanDefinition mbd, final Object[] args:

...
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
    if (exposedObject == bean) {
        // 原工程走到了這里
        exposedObject = earlySingletonReference;
    }
    else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
        // 新的有error的bean走到里這里
        String[] dependentBeans = getDependentBeans(beanName);
        Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
        for (String dependentBean : dependentBeans) {
            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.");
        }
    }
}
...

從這里已經(jīng)可以看到腺兴,是新工程中的 exposedObject 和 bean不一樣導(dǎo)致的

而這兩者的關(guān)系如下面的代碼

// Initialize the bean instance.
    Object exposedObject = bean;
    try {
        populateBean(beanName, mbd, instanceWrapper);
        if (exposedObject != null) {
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
    }
    catch (Throwable ex) {
        if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
            throw (BeanCreationException) ex;
        }
        else {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
        }
    }

也就是說exposedObject 在initializeBean 函數(shù)之后變掉了

2.2 AnnotationAwareAspectJAutoProxyCreator把返回值修改了

然后發(fā)現(xiàn)在applyBeanPostProcessorsAfterInitialization 函數(shù)中左电,AnnotationAwareAspectJAutoProxyCreator修改了返回的result

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

邏輯如上,也就是說earlyProxyReferences 這個里不存在這個cacheKey

2.3 懷疑是自定義的annotaion修改導(dǎo)致

因?yàn)楹瘮?shù)中页响,AnnotationAwareAspectJAutoProxyCreator是處理annotaion的相關(guān)微酬。需要預(yù)處理Proxy刹孔。
往遠(yuǎn)工程里加了這個annatation翎迁, 但是debug發(fā)現(xiàn)原工程里的這樣的annotaion也沒有問題

2.4 配置文件里起了兩個AnnotationAwareAspectJAutoProxyCreator胆建,才導(dǎo)致了這個問題

看了一下earlyProxyReferences 會在哪一步進(jìn)行put進(jìn)入绽族。
發(fā)現(xiàn)在Factory.getObject()的時候會調(diào)用卵酪。然后斷點(diǎn)到put的地方烹骨,也確實(shí)put進(jìn)入了
但是再debug到postProcessAfterInitialization的時候莫绣,發(fā)現(xiàn)contains就是不對
然后看了下看了一下earlyProxyReferences的值烟瞧,發(fā)現(xiàn)居然有兩個AnnotationAwareAspectJAutoProxyCreator
然后干掉之后確實(shí)是可以的

3. 兩個AnnotationAwareAspectJAutoProxyCreator 導(dǎo)致這個問題的原因

因?yàn)檎{(diào)用actory.getObject() 時. 調(diào)用下面的方法诗鸭,

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
        Object exposedObject = bean;
        if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
                    SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
                    exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
                    if (exposedObject == null) {
                        return exposedObject;
                    }
                }
            }
        }
        return exposedObject;
    }

就會導(dǎo)致兩個的AnnotationAwareAspectJAutoProxyCreator的earlyProxyReferences中含有不一樣的代理對象

而在最后匹配時的邏輯

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            result = beanProcessor.postProcessAfterInitialization(result, beanName);
            if (result == null) {
                return result;
            }
        }
        return result;
    }

第二個AnnotationAwareAspectJAutoProxyCreator 發(fā)現(xiàn)earlyProxyReferences 不存在第一個的代理對象的值,返回自己的代理對象参滴,結(jié)果導(dǎo)致不一樣

4. 解決方法

干掉一個 AnnotationAwareAspectJAutoProxyCreator, 這個循環(huán)依賴的error强岸,就處理了

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市砾赔,隨后出現(xiàn)的幾起案子蝌箍,更是在濱河造成了極大的恐慌青灼,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,378評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妓盲,死亡現(xiàn)場離奇詭異杂拨,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)悯衬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評論 3 399
  • 文/潘曉璐 我一進(jìn)店門弹沽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人筋粗,你說我怎么就攤上這事策橘。” “怎么了娜亿?”我有些...
    開封第一講書人閱讀 168,983評論 0 362
  • 文/不壞的土叔 我叫張陵丽已,是天一觀的道長。 經(jīng)常有香客問我买决,道長沛婴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,938評論 1 299
  • 正文 為了忘掉前任督赤,我火速辦了婚禮瘸味,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘够挂。我一直安慰自己旁仿,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,955評論 6 398
  • 文/花漫 我一把揭開白布孽糖。 她就那樣靜靜地躺著枯冈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪办悟。 梳的紋絲不亂的頭發(fā)上尘奏,一...
    開封第一講書人閱讀 52,549評論 1 312
  • 那天,我揣著相機(jī)與錄音病蛉,去河邊找鬼炫加。 笑死,一個胖子當(dāng)著我的面吹牛铺然,可吹牛的內(nèi)容都是我干的俗孝。 我是一名探鬼主播,決...
    沈念sama閱讀 41,063評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼魄健,長吁一口氣:“原來是場噩夢啊……” “哼赋铝!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起沽瘦,我...
    開封第一講書人閱讀 39,991評論 0 277
  • 序言:老撾萬榮一對情侶失蹤革骨,失蹤者是張志新(化名)和其女友劉穎农尖,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體良哲,經(jīng)...
    沈念sama閱讀 46,522評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡盛卡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,604評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了筑凫。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窟扑。...
    茶點(diǎn)故事閱讀 40,742評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖漏健,靈堂內(nèi)的尸體忽然破棺而出嚎货,到底是詐尸還是另有隱情,我是刑警寧澤蔫浆,帶...
    沈念sama閱讀 36,413評論 5 351
  • 正文 年R本政府宣布殖属,位于F島的核電站,受9級特大地震影響瓦盛,放射性物質(zhì)發(fā)生泄漏洗显。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,094評論 3 335
  • 文/蒙蒙 一原环、第九天 我趴在偏房一處隱蔽的房頂上張望挠唆。 院中可真熱鬧,春花似錦嘱吗、人聲如沸玄组。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽俄讹。三九已至,卻和暖如春绕德,著一層夾襖步出監(jiān)牢的瞬間患膛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評論 1 274
  • 我被黑心中介騙來泰國打工耻蛇, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留踪蹬,地道東北人。 一個月前我還...
    沈念sama閱讀 49,159評論 3 378
  • 正文 我出身青樓臣咖,卻偏偏與公主長得像跃捣,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子亡哄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,747評論 2 361

推薦閱讀更多精彩內(nèi)容