zoukankan      html  css  js  c++  java
  • 彻底理解Spring如何解决循环依赖

    Spring bean生命周期

    可以简化为以下5步。

    1、构建BeanDefinition

    2、实例化 Instantiation

    3、属性赋值 Populate

    4、初始化 Initialization(BeanPostprocessor -> Aware,init)

    5、销毁 Destruction 

    Spring 三级缓存作用

    一级缓存

    /** Cache of singleton objects: bean name to bean instance. */
    Map<String, Object> singletonObjects;

    用来保存实例化、初始化都完成的bean对象。

    二级缓存

    /** Cache of early singleton objects: bean name to bean instance. */
    Map<String, Object> earlySingletonObjects ;

    用来保存实例化完成,但是未初始化完成的对象(这个对象不一定是原始对象,也有可能是经过AOP生成的代理对象)。

    三级缓存

    /** Cache of singleton factories: bean name to ObjectFactory. */
    Map<String, ObjectFactory<?>> singletonFactories;

    用来保存一个对象工厂(ObjectFactory),提供一个匿名内部类,用于创建二级缓存中的对象。

    三级缓存中提到的ObjectFactory即 () -> getEarlyBeanReference(beanName,mbd,bean),其中bean就是原始对象。

    其中getEarlyBeanReference 方法是 SmartInstantiationAwareBeanPostProcessor接口中定义的,AbstractAutoProxyCreator(Spring AOP proxy creator)实现了该方法。

    Spring三级缓存实现

    获取beanName:A

    org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean(beanName:A)
      org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName:A)
        org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName:A, allowEarlyReference:true)

    分别按照一级缓存、二级缓存、三级缓存顺序加载。如果存在循环依赖(比如beanName:B依赖beanName:A),而且三级缓存中存在beanName:A的引用,则从三级缓存中拿到beanName:A对应的提早曝光的对象(可能是原始对象,也可能是代理对象)并放入二级缓存。比如又有beanName:C依赖beanName:A,会直接从二级缓存中获取到。

    bean创建和初始化完成

    org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean(beanName:A)
      org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName:A, ObjectFactory:lamda表达,调用AbstractBeanFactory#createBean(beanName:A))
      org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton(beanName:A,singletonObject:A)

    直接添加到一级缓存

    bean创建完成之后

    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(beanName:A, RootBeanDefinition:mbd, Object[]:args)
      org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean(beanName:A, RootBeanDefinition:mbd, Object[]:args)
        org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory(beanName:A, () -> getEarlyBeanReference(beanName:A, mbd, bean:A))
      org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean  //属性填充
      org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean   //初始化

    将ObjectFactory对象放入三级缓存,beanName:A -> ObjectFactory( () -> getEarlyBeanReference(beanName:A, mbd, bean:A) )

    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
          throws BeanCreationException {
       // Instantiate the bean.
       BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
       }
       final Object bean = instanceWrapper.getWrappedInstance();
       boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
             isSingletonCurrentlyInCreation(beanName));
          addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
       }
       // Initialize the bean instance.
       Object exposedObject = bean;
       populateBean(beanName, mbd, instanceWrapper);
       exposedObject = initializeBean(beanName, exposedObject, mbd);
    
       if (earlySingletonExposure) {
        //org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName:A, allowEarlyReference:false)  Object earlySingletonReference
    = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { } } } }

    一个简单的A、B互相依赖循环依赖场景

    @Async注解循环依赖报错

    @Transactional使用的是自动代理创建器AbstractAutoProxyCreator,它实现了getEarlyBeanReference()方法从而很好的对循环依赖提供了支持。

    @Async的代理创建使用的是AsyncAnnotationBeanPostProcessor单独的后置处理器实现的,它只在一处postProcessAfterInitialization()实现了对代理对象的创建,因此若出现它被循环依赖了,就会报BeanCurrentlyInCreationException。

    protected Object doCreateBean( ... ){
        ...
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }
        ...
    
        Object exposedObject = bean;
    
        /**
        *假如A实例有方法注有@Async注解,A实例依赖B实例,B实例依赖A实例。创建A实例时先走到populateBean方法,然后开始填充属性B实例,B实例也会走到populateBean方法,然后从三级缓存中通过A流程中的getEarlyBeanReference()方法,从而拿到A的早期引用*。执行A的getEarlyBeanReference()方法的时候,会执行自动代理创建器,这里最终得到是可能是原始A对象,也可能是代理后的A对象(注意哦,A实例的@Async注解这里还没有被处理呢)。exposedObject此时指向的是原始A实例。
        */
        populateBean(beanName, mbd, instanceWrapper);
    
         /**
         *标注有@Async的A实例的代理对象在此处会被生成, 参照类:AsyncAnnotationBeanPostProcessor。执行完之后,exposedObject指向的是个代理对象而非原始A实例了。
         */
        exposedObject = initializeBean(beanName, exposedObject, mbd);
        
        ...
        // 这里是报错的重点。
        if (earlySingletonExposure) {
            /**
            *因为A被B循环依赖进去了,所以此时A是被放进了二级缓存的,所以此处earlySingletonReference指向的是通过创建A实例流程中的getEarlyBeanReference()返回的A实例(再强调用一下,可能是原始对象,也可能是代理对象)。
            *说到这里,什么情况下earlySingletonReference==null?也就是getSingleton(beanName:A, false)==null,只有当A实例没有牵涉到循环依赖的时候(即不存在A依赖B且B依赖A的场景;单独存在B依赖A是没有问题,A的三级缓存根本不会执行,所以二级缓存就不会有值,A创建并初始化完成之后直接放到了一级缓存)。
            */
            Object earlySingletonReference = getSingleton(beanName, false);
            if (earlySingletonReference != null) {
                //这里exposedObject指向的是被@Aysnc代理过的对象,而bean是原始对象,所以此处不相等,走else逻辑。
                if (exposedObject == bean) {
                    exposedObject = earlySingletonReference;
                }
                /**
                *allowRawInjectionDespiteWrapping 标注是否允许此Bean的原始类型被注入到其它Bean里面,即使自己最终会被包装(代理)。
                *默认是false表示不允许,如果改为true表示允许,就不会报错啦。
                *另外dependentBeanMap是记录着每个Bean它所依赖的Bean的Map。
                */
                else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                    //因为A依赖于B,所以此处拿到了B实例的beanName
                    String[] dependentBeans = getDependentBeans(beanName);
                    Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
    
                    /**
                    *B实例经过removeSingletonIfCreatedForTypeCheckOnly最终返返回false  因为alreadyCreated里面已经有它了表示B已经完全创建完成了。
                    *既然B实例已经创建完成了,通过创建A实例流程中的getEarlyBeanReference()返回的A实例已经注入到了B实例中,此时B实例注入的和exposedObject指向的不是同一个A实例,那肯定就有问题了。
                    */
                    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.");
                    }
                }
            }
        }
        ...
    }

    具体原因分析参考:https://blog.csdn.net/f641385712/article/details/92797058

    总结

    1、Spring 解决循环依赖有两个前提条件:不全是构造器方式的循环依赖,必须是单例。

    2、如果没有出现循环依赖,第三级缓存(singletonFactories)将不会使用到,对象会按照Spring创建bean的生命周期流程,最后将bean直接放到第一级缓存(singletonObjects)中。

    3、一定要三级缓存嘛,二级缓存不能解决循环依赖?不能,主要是为了生成代理对象。

    因为三级缓存中放的是生成具体对象的匿名内部类(ObjectFactory),它可能生成代理对象,也可能是普通的实例对象。使用三级缓存主要是为了保证不管什么时候使用的都是一个对象。假设只有二级缓存的情况,往二级缓存中放的显示一个普通的Bean对象,BeanPostProcessor去生成代理对象之后,覆盖掉二级缓存中的普通Bean对象,无法保证程多线程环境下获取到bean对象一致性。

  • 相关阅读:
    支持向量机 (三): 优化方法与支持向量回归
    支持向量机 (二): 软间隔 svm 与 核函数
    支持向量机 (一): 线性可分类 svm
    拉格朗日乘子法
    特征选择: 卡方检验、F 检验和互信息
    Python 多进程、多线程效率比较
    Umi+Dva搭建Cesium 3D开发环境
    React 项目生产版本迭代页面不刷新问题
    React-Native学习手册----搭建基于ios平台的开发环境
    Cesium 绘制点、线、面和测距
  • 原文地址:https://www.cnblogs.com/hujunzheng/p/14094804.html
Copyright © 2011-2022 走看看