美文网首页Java技术文章
Spring 循环依赖问题fix

Spring 循环依赖问题fix

作者: nyle | 来源:发表于2017-02-24 01:53 被阅读1507次

    Spring 循环依赖问题fix

    拆分的时候,把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<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修改了返回的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,就处理了

    相关文章

      网友评论

        本文标题:Spring 循环依赖问题fix

        本文链接:https://www.haomeiwen.com/subject/osviwttx.html