Spring循環依賴問題修復

Spring循環依賴問題修復
拆分的時候,把錯誤都處理完後,準備把工程起起來,發現彈簧的循環依賴問題。具體問題如下

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.懷疑配置文件的問題
但是在原工程中並沒有這個問題,所以一開始懷疑是配置文件的配置不一樣,百度了一下這個錯誤

beanFactory.setAllowRawInjectionDespiteWrapping(true);

看網上說這個配置了,對於循環依賴的這個錯誤就會解決掉。但是在兩個工程裏搜索了一下都沒有發現這個配置過。
於是只能調試進去看看
2.調查查看分析
2.1 spring引用的bean和注入的bean不一致導致的這個錯誤
由於在原工程裏是可以循環引用的,所以對工程和新工程都在初始化這兩個循環引用的位置進行了調試
然後發現最後兩邊走的邏輯不一樣的在以下的代碼裏:

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.");
        }
    }
}

...

從這裏已經可以看到,是新工程中的exposedObject和bean不一樣導致的
而這兩者的關係如下面的代碼

// 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函數之後變掉了
2.2 AnnotationAwareAspectJAutoProxyCreator把返回值修改了
然後發現在applyBeanPostProcessorsAfterInitialization函數中,AnnotationAwareAspectJAutoProxyCreator修改了返回的結果

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修改導致
因爲函數中,AnnotationAwareAspectJAutoProxyCreator是處理annotaion的相關。需要預處理代理。
往遠工程里加了這個annatation,但是調試發現原工程裏的這樣的annotaion也沒有問題
2.4配置文件裏起了兩個AnnotationAwareAspectJAutoProxyCreator,才導致了這個問題
看了一下earlyProxyReferences會在哪一步進行put進入。
發現在Factory.getObject()的時候會調用。然後斷點到put的地方,也確實put進入了
但是再調試到postProcessAfterInitialization的時候,發現包就是不對
然後看了下看了一下earlyProxyReferences的值,發現居然有兩個AnnotationAwareAspectJAutoProxyCreator
然後幹掉之後確實是可以的
3.兩個AnnotationAwareAspectJAutoProxyCreator導致這個問題的原因
因爲調用actory.getObject()時。調用下面的方法,

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;
    }

就會導致兩個的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發現earlyProxyReferences不存在第一個的代理對象的值,返回自己的代理對象,結果導致不一樣
解決方法
幹掉一個AnnotationAwareAspectJAutoProxyCreator,這個循環依賴的錯誤,就處理了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章