Spring 循環依賴問題fix

拆分的時候,把error都處理完後,準備把工程起起來,發現spring的循環依賴問題。具體問題如下

Spring 循環依賴問題fix

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

看網上說這個配置了,對於循環依賴的這個error就會解決掉。但是在兩個工程裡搜索了一下都沒有發現這個配置過。

於是只能debug進去看看

2. debug 查看 分析

2.1 spring 引用的bean和注入的bean不一致導致的這個error

由於在原工程裡是可以循環引用的,所以對工程和新工程都在初始化這兩個循環引用的位置進行了debug

然後發現最後兩邊走的邏輯不一樣的在以下的代碼裡:

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 actualDependentBeans = new LinkedHashSet(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修改了返回的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修改導致

因為函數中,AnnotationAwareAspectJAutoProxyCreator是處理annotaion的相關。需要預處理Proxy。

往遠工程里加了這個annatation, 但是debug發現原工程裡的這樣的annotaion也沒有問題

2.4 配置文件裡起了兩個AnnotationAwareAspectJAutoProxyCreator,才導致了這個問題

看了一下earlyProxyReferences 會在哪一步進行put進入。

發現在Factory.getObject()的時候會調用。然後斷點到put的地方,也確實put進入了

但是再debug到postProcessAfterInitialization的時候,發現contains就是不對

然後看了下看了一下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 不存在第

一個的代理對象的值,返回自己的代理對象,結果導致不一樣

4. 解決方法

幹掉一個 AnnotationAwareAspectJAutoProxyCreator, 這個循環依賴的error,就處理了

關注、轉發、評論頭條號每天分享java知識,私信回覆“555”贈送一些Dubbo、Redis、Netty、zookeeper、Spring cloud、分佈式資料


分享到:


相關文章: