JobPlus知识库 IT 软件开发 文章
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.

怀疑配置文件的问题

但是在原工程中并没有这个问题,所以一开始怀疑是配置文件的配置不一样,百度了一下这个错误

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会在哪一步进行把进入。
发现在Factory.getObject()的时候会调用。然后断点到放的地方,也确实把进入了
但是再调试到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不存在第一个的代理对象的值,返回自己的代理对象,结果导致不一样

4.解决方法

干掉一个AnnotationAwareAspectJAutoProxyCreator,这个循环依赖的错误,就处理了。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

¥ 打赏支持
113人赞 举报
分享到
用户评价(0)

暂无评价,你也可以发布评价哦:)

扫码APP

扫描使用APP

扫码使用

扫描使用小程序