• 【spring源码分析】IOC容器初始化(十二)


    前言:在doCreateBean方法中还遗留一个问题没有分析:循环依赖。循环依赖在Spring中是非常重要的一个知识点,因此单独进行分析。


    什么是循环依赖

    循环依赖就是循环引用,两个或两个以上的bean互相引用对方,最终形成一个闭环。如A依赖B,B依赖C,C依赖A。如下图所示:

    循环依赖其实就是一个死循环的过程,在初始化A的时候发现引用了B,则就会去初始化B,然后发现B又引用C,则又去初始化C,在初始化C的时候,再次发现C引用了A,则又去初始化A,这样就处于死循环,除非有终结条件。

    Spring中循环依赖的场景有两种:

    • 构造器循环依赖。
    • setter循环依赖。

    构造器循环依赖

    表示通过构造器注入构成的循环依赖,此依赖是无法解决的,只能抛出BeanCurrentlyInCreationException异常。

    setter循环依赖

    表示通过setter注入方式构成的循环依赖,对于setter注入造成的循环依赖,Spring只解决单例模式下的循环依赖,对于其他作用域的循环依赖,则抛出BeanCurrentlyInCreationException异常。为什么Spring只解决单例模式下的循环依赖呢,这里首先了解下Spring的几个作用域。

    Spring的scope作用域

    • singleton:默认的scope,每个scope是singleton的bean都会被定义一个单例对象,该对象的生命周期与Spring IOC容器一致的(但在第一次注入时才会创建)。
    • prototype:如果为bean的作用域为prototype,则每次注入时都会创建一个新的对象。
    • request:bean被定义为在每个HTTP请求 中创建一个单例对象,也就是说在单个请求中都会复用这个单例对象。
    • session:bean被定义为在一个session的生命周期内创建一个单例对象。
    • application:bean被定义为在ServletContext的生命周期中复用一个单例对象。
    • websocket:bean被定义为在websocket的生命周期中复用一个单例对象。

    从上面对Spring中scope作用域的介绍,也可大致了解为什么Spring中只解决单例模式下的循环依赖了,因为其他作用域对象的生命周期并不与Spring IOC容器一致,并且最主要的一点是Spring并不会对除了单例模式的bean做缓存,因此Spring只能解决单例模式下的循环依赖。

    具体循环依赖解决流程

    首先在看bean的加载的入口,在doGetBean方法中有如下代码:

    首先会根据beanName从单例bean缓存中获取,如果不为空,则直接返回,前面已经分析了该方法,这里再次提出来。

     1 // DefaultListableBeanFactory
     2 protected Object getSingleton(String beanName, boolean allowEarlyReference) {
     3         // 从单例缓存中加载Bean
     4         Object singletonObject = this.singletonObjects.get(beanName);
     5         // 缓存中bean为空,且当前bean正在创建
     6         if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
     7             // 做同步
     8             synchronized (this.singletonObjects) {
     9                 // 从earlySingletonObjects集合中获取
    10                 singletonObject = this.earlySingletonObjects.get(beanName);
    11                 // earlySingletonObjects集合中没有,其允许提前创建
    12                 if (singletonObject == null && allowEarlyReference) {
    13                     // 从singletonFactories中获取对应的ObjectFactory
    14                     ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
    15                     if (singletonFactory != null) {
    16                         // 获取bean
    17                         singletonObject = singletonFactory.getObject();
    18                         // 将bean添加到earlySingletonObjects集合中
    19                         this.earlySingletonObjects.put(beanName, singletonObject);
    20                         // 从singletonFactories中移除对应的
    21                         this.singletonFactories.remove(beanName);
    22                     }
    23                 }
    24             }
    25         }
    26         return singletonObject;
    27     }

    分析:

    这里注意isSingletonCurrentlyInCreation函数,判断当前bean是否正在被创建,这里就是判断beanName是否在singletonsCurrentlyInCreation集合中,那么正在被创建的bean是合适添加进去的呢?还是在doGetBean方法中

     1 // DefaultSingletonBeanRegistry
     2 public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
     3         Assert.notNull(beanName, "Bean name must not be null");
     4         // 做同步
     5         synchronized (this.singletonObjects) {
     6             // 从缓存中检查一遍
     7             // 因为singlton模式其实已经复用了创建的bean,所以该步骤必须检查
     8             Object singletonObject = this.singletonObjects.get(beanName);
     9             // 为空,开始进行加载
    10             if (singletonObject == null) {
    11                 if (this.singletonsCurrentlyInDestruction) {
    12                     throw new BeanCreationNotAllowedException(beanName,
    13                                                               "Singleton bean creation not allowed while singletons of this factory are in destruction " +
    14                                                                       "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
    15                 }
    16                 if (logger.isDebugEnabled()) {
    17                     logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
    18                 }
    19                 // 加载前置处理 其实就是打一个标记
    20                 beforeSingletonCreation(beanName);
    21                 // 首先将新的newSingleton设置为false
    22                 boolean newSingleton = false;
    23                 boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
    24                 if (recordSuppressedExceptions) {
    25                     this.suppressedExceptions = new LinkedHashSet<>();
    26                 }
    27                 try {
    28                     // 初始化bean
    29                     // 该过程其实是调用createBean()方法 这里是一个回调方法
    30                     singletonObject = singletonFactory.getObject();
    31                     newSingleton = true;
    32                 } catch (IllegalStateException ex) {
    33                     // Has the singleton object implicitly appeared in the meantime ->
    34                     // if yes, proceed with it since the exception indicates that state.
    35                     singletonObject = this.singletonObjects.get(beanName);
    36                     if (singletonObject == null) {
    37                         throw ex;
    38                     }
    39                 } catch (BeanCreationException ex) {
    40                     if (recordSuppressedExceptions) {
    41                         for (Exception suppressedException : this.suppressedExceptions) {
    42                             ex.addRelatedCause(suppressedException);
    43                         }
    44                     }
    45                     throw ex;
    46                 } finally {
    47                     if (recordSuppressedExceptions) {
    48                         this.suppressedExceptions = null;
    49                     }
    50                     // 一堆异常处理后,进行后置处理 移除标志
    51                     afterSingletonCreation(beanName);
    52                 }
    53                 // 新的bean 加入缓存中
    54                 if (newSingleton) {
    55                     addSingleton(beanName, singletonObject);
    56                 }
    57             }
    58             return singletonObject;
    59         }

    分析:

    注意第20行代码beforeSingletonCreation函数。

    1 // DefaultSingletonBeanRegistry
    2 protected void beforeSingletonCreation(String beanName) {
    3         // 这里会添加到正在创建bean的集合中
    4         // 注意第一个条件,如果存在,则为false,直接短路
    5         // 只有当第一个条件不存在[false]时,才会去进行添加操作
    6         if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
    7             throw new BeanCurrentlyInCreationException(beanName);
    8         }
    9     }

    分析:

    这里就会将正在创建的beanName添加到singletonsCurrentlyInCreation,如果出现构造函数的循环依赖bean注入,则会在此处抛出BeanCurrentlyCreationException异常,具体可以跟着代码调试一把,就可以更清楚的知晓流程。

    接下来继续回到单例模式的循环依赖中:

    在加载bean时,首先从单例缓存中获取bean对象。

    • 首先从单例缓存中获取bean对象,如果缓存中存在bean对象则直接返回(单例模式的bean在创建过程中会进行缓存[singletonObjects])。
    • 如果缓存中bean对象为空,且当前bean正在创建,则从earlySingletonObjects中获取。
    • 如果earlySingletonObjects集合中不存在,且允许提前创建bean,则从singletonFactories中获取单例工厂,若singleFactory不为空,则通过getObject方法获取bean,并将bean对象加入到earlySingletonObjects集合中,然后从singleFactory集合中移除对应的单例工厂对象。

    注意这里涉及到三个集合:

    • singletonObjects (一级)单例对象 Cache
    • earlySingletonObjects (二级)提前曝光的单例对象 Cache
    • singletonFactories (三级)单例对象工厂 Cache
     1 /**
     2      * Cache of singleton objects: bean name to bean instance.
     3      * 存放的是单例 bean 的映射。
     4      * <p>
     5      * 对应关系为 bean name --> bean instance
     6      */
     7     private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
     8 
     9     /**
    10      * Cache of singleton factories: bean name to ObjectFactory.<br/>
    11      * 存放的是 ObjectFactory,可以理解为创建单例 bean 的 factory 。
    12      * <p>
    13      * 对应关系是 bean name --> ObjectFactory
    14      */
    15     private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
    16 
    17     /**
    18      * Cache of early singleton objects: bean name to bean instance.<br/>
    19      * 存放的是早期的 bean,对应关系也是 bean name --> bean instance。
    20      * <p>
    21      * 它与 {@link #singletonFactories} 区别在于 earlySingletonObjects 中存放的 bean 不一定是完整。
    22      * <p>
    23      * 从 {@link #getSingleton(String)} 方法中,我们可以了解,bean 在创建过程中就已经加入到 earlySingletonObjects 中了。
    24      * 所以当在 bean 的创建过程中,就可以通过 getBean() 方法获取。
    25      * <p>
    26      * 这个 Map 也是【循环依赖】的关键所在。
    27      */
    28     private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

    这三个缓存集合就是解决Spring中循环依赖的所在,具体流程:

    • 首先从一级缓存singletonObjects中获取,如果为null,且当前bean正在被创建,则从二级缓存earlySingletonObjects中获取,如果还是为null,且允许singletonFactories通过getObject获取,则从三级缓存singletonFactories中获取,如果得到,则将其加入二级缓存earlySingletonObjects中,并从三级缓存singletonFactories中移除对应的工厂对象(因为单例模式的bean只会被创建一次),这样三级缓存就升级到二级缓存了,所以二级缓存存在的意义就是缓存三级缓存中ObjectFactory#getObject的执行结果,提前曝光单例Bean对象。

    如果从单例缓存中得到bean对象,则会调用getObjectForBeanInstance方法进一步处理,因为从缓存中得到的bean是最原始的bean,并不一定是最终所需要的bean对象。

    上面分析了从缓存中获取bean对象,但是缓存中的值是从什么地方添加进来的呢?如果你调试过源码会发现这样一段代码:

     1 // AbstractAutowireCapableBeanFactory
     2     boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
     3                 isSingletonCurrentlyInCreation(beanName));// 当前单例bean是否正在被创建
     4         if (earlySingletonExposure) {
     5             if (logger.isDebugEnabled()) {
     6                 logger.debug("Eagerly caching bean '" + beanName +
     7                                      "' to allow for resolving potential circular references");
     8             }
     9             // 提前将创建的bean实例加入到singletonFactories中
    10             // 为了后期避免循环依赖
    11             addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    12         }

    分析:

    当一个bean满足三个条件,则会被加入到缓存中。三个条件如下:

    • 单例。
    • 运行提前暴露bean。
    • 当前bean正在被创建中。

    DefaultSingletonBeanRegistry#addSingletonFactroy

     1     protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
     2         Assert.notNull(singletonFactory, "Singleton factory must not be null");
     3         synchronized (this.singletonObjects) {
     4             if (!this.singletonObjects.containsKey(beanName)) {
     5                 this.singletonFactories.put(beanName, singletonFactory);
     6                 this.earlySingletonObjects.remove(beanName);
     7                 this.registeredSingletons.add(beanName);
     8             }
     9         }
    10     }

    分析:

    • 如果单例缓存中不存在beanName,则将singletonFactory进行缓存[singletonFactories集合],注意这里还有一个earlySingletonObjects.remove操作,该操作是删除二级缓存(提前曝光的单例对象bean),因为二级缓存是从三级缓存转换而来的,因此在对三级缓存[singletonFactories]进行缓存时,需要remove二级缓存。
    • 这段代码发生在createBeanInstance方法之后,也就是说这个bean其实已经被创建出来了,但它还是不是很完美(没有进行属性填充和初始化),但是对于其他依赖它的对象而言已经足够了(可根据对象引用定位到堆中对象),能够被认出来。所以Spring解决setter构造依赖的关键,就是对bean进行提前曝光。

    上面介绍的了三级缓存[singletonFactories]与二级缓存[earlySingletonObjects]的出处,接下来看一级缓存的出处:

    在DefaultSingletonBeanRegistry#getSingleton函数中:

     1 public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
     2         Assert.notNull(beanName, "Bean name must not be null");
     3         // 做同步
     4         synchronized (this.singletonObjects) {
     5             // 从缓存中检查一遍
     6             // 因为singlton模式其实已经复用了创建的bean,所以该步骤必须检查
     7             Object singletonObject = this.singletonObjects.get(beanName);
     8             // 为空,开始进行加载
     9             if (singletonObject == null) {
    10                 if (this.singletonsCurrentlyInDestruction) {
    11                     throw new BeanCreationNotAllowedException(beanName,
    12                                                               "Singleton bean creation not allowed while singletons of this factory are in destruction " +
    13                                                                       "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
    14                 }
    15                 if (logger.isDebugEnabled()) {
    16                     logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
    17                 }
    18                 // 加载前置处理 其实就是打一个标记
    19                 beforeSingletonCreation(beanName);
    20                 // 首先将新的newSingleton设置为false
    21                 boolean newSingleton = false;
    22                 boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
    23                 if (recordSuppressedExceptions) {
    24                     this.suppressedExceptions = new LinkedHashSet<>();
    25                 }
    26                 try {
    27                     // 初始化bean
    28                     // 该过程其实是调用createBean()方法 这里是一个回调方法
    29                     singletonObject = singletonFactory.getObject();
    30                     newSingleton = true;
    31                 } catch (IllegalStateException ex) {
    32                     // Has the singleton object implicitly appeared in the meantime ->
    33                     // if yes, proceed with it since the exception indicates that state.
    34                     singletonObject = this.singletonObjects.get(beanName);
    35                     if (singletonObject == null) {
    36                         throw ex;
    37                     }
    38                 } catch (BeanCreationException ex) {
    39                     if (recordSuppressedExceptions) {
    40                         for (Exception suppressedException : this.suppressedExceptions) {
    41                             ex.addRelatedCause(suppressedException);
    42                         }
    43                     }
    44                     throw ex;
    45                 } finally {
    46                     if (recordSuppressedExceptions) {
    47                         this.suppressedExceptions = null;
    48                     }
    49                     // 一堆异常处理后,进行后置处理 移除标志
    50                     afterSingletonCreation(beanName);
    51                 }
    52                 // 新的bean 加入缓存中
    53                 if (newSingleton) {
    54                     addSingleton(beanName, singletonObject);
    55                 }
    56             }
    57             return singletonObject;
    58         }
    59     }

    分析:

    这里关注第54行处:addSingleton函数。

    1 // DefaultSingletonBeanRegistry
    2     protected void addSingleton(String beanName, Object singletonObject) {
    3         synchronized (this.singletonObjects) {
    4             this.singletonObjects.put(beanName, singletonObject);
    5             this.singletonFactories.remove(beanName);
    6             this.earlySingletonObjects.remove(beanName);
    7             this.registeredSingletons.add(beanName);
    8         }
    9     }

    分析:

    这里就是将bean加入一级缓存中[singletonObjects],同时remove二级缓存和三级缓存中值,因为bean已经被创建成功了,二级缓存与三级缓存也就不需要了。

    总结

    从上面分析过程可以知道为什么Spring只解决单例模式下的循环依赖了吧?

    • Spring只对单例模式的bean进行了提前暴光[singletonFactories],这也是由于其他scope域bean的特性所致。

    单例模式下循环依赖解决流程:

    • 首先A完成初始化第一步并将自己提前曝光出来(通过 ObjectFactory 将自己提前曝光),A在初始化的时候,发现自己依赖对象 B,此时就会去尝试 get(B),这个时候发现 B 还没有被创建出来。
    • 然后B就走创建流程,在B初始化的时候,同样发现自己依赖 C,C也没有被创建出来。
    • 这个时候C又开始初始化进程,但是在初始化的过程中发现自己依赖 A,于是尝试 get(A),这个时候由于A已经添加至缓存中(一般都是添加至三级缓存singletonFactories),通过 ObjectFactory 提前曝光,所以可以通过ObjectFactory#getObject()方法来拿到 A 对象,C 拿到 A 对象后顺利完成初始化,然后将自己添加到一级缓存中
    • 回到B,B 也可以拿到 C 对象,完成初始化,A可以顺利拿到B完成初始化。到这里整个链路就已经完成了初始化过程了。

    如果你看过《Spring源码深度解析》这本书,还能发现如下流程图:

    注:看图说话,笔者最为喜欢!其实去亲身调试一下循环依赖的代码,可能会有更加深刻的认识。


    by Shawn Chen,2019.04.29,下午。

  • 相关阅读:
    阿里云物联网平台: 使用阿里云物联网平台提供的自定义Topic通信控制(Air202,TCP透传指令)
    ESA2GJK1DH1K基础篇: 源码使用注意事项和程序优化
    ESP8266 SDK开发: 常见问题及程序BUG修复
    ESP8266 SDK开发: 微信小程序篇-微信小程序通过UDP实现和ESP8266局域网通信控制
    ESP8266 SDK开发: 物联网篇-ESP8266连接阿里云物联网平台,使用阿里云提供的物模型Topic通信控制
    ESP8266 SDK开发: 物联网篇-ESP8266连接阿里云物联网平台,使用阿里云提供的自定义Topic通信控制
    ESA2GJK1DH1K基础篇: APP使用SmartConfig绑定Wi-Fi 设备并通过MQTT控制设备(V1.0)(AT+TCP非透传指令)
    【spring源码分析】@Value注解原理
    【vue】npm、node版本查看及npm常用命令
    【vue】常用操作
  • 原文地址:https://www.cnblogs.com/developer_chan/p/10783620.html
Copyright © 2020-2023  润新知