拆分的時候,把error都處理完後,準備把工程起起來,發現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. 懷疑配置文件的問題
但是在原工程中並沒有這個問題,所以一開始懷疑是配置文件的配置不一樣,百度了一下這個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
從這裡已經可以看到,是新工程中的 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、分佈式資料
閱讀更多 Java大寶寶 的文章