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,這個循環依賴的錯誤,就處理了