• 彻底理解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对象一致性。

  • 相关阅读:
    剑指Offer-11.二进制中1的个数(C++/Java)
    剑指Offer-10.矩形覆盖(C++/Java)
    剑指Offer-9.变态跳台阶(C++/Java)
    UVA 1608 Non-boring sequence 不无聊的序列(分治,中途相遇)
    UVA1607 Gates 与非门电路 (二分)
    UVA 1451 Average平均值 (数形结合,斜率优化)
    UVA 1471 Defense Lines 防线 (LIS变形)
    UVA 1606 Amphiphilic Carbon Molecules 两亲性分子 (极角排序或叉积,扫描法)
    UVA 11134 FabledRooks 传说中的车 (问题分解)
    UVA 1152 4 Values Whose Sum is Zero 和为0的4个值 (中途相遇)
  • 原文地址:https://www.cnblogs.com/hujunzheng/p/14094804.html
Copyright © 2020-2023  润新知