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强岸,就處理了