zoukankan      html  css  js  c++  java
  • spring 循环依赖的一次 理解

    前言:

      在看spring 循环依赖的问题中,知道原理,网上一堆的资料有讲原理。 但今天在看代码过程中,又产生了疑问。
    疑问点如下:
    // 疑问点: 先进行 dependon 判断
    String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } registerDependentBean(dep, beanName); try { getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } // Create bean instance. if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }
      想着这里先会 判断是否有依赖, bean 提前曝光的代码在 检查依赖的后面, 那循环依赖,不就进入死循环了吗? 是否有统统疑问的朋友~~~ --也不知道当时这么想的,只能说 还有提高的空间。
    心想不对,写了简单的demo,开始debug了。
     
      结论:这里的 mbd.getDependsOn() 只有在 配置了 depend-on 标签的时候,才会解析,有值。!!! 这也是导致 理解循环依赖 有问题的关键。

    这里先简单记录下 现在理解的 循环依赖的大致流程:

    1、depende-on 标签的情况

    <bean id="aService" class="com.zzf.spring.dependent.AService" depends-on="bService"/>
    <bean id="bService" class="com.zzf.spring.dependent.BService" depends-on="aService"/>
    

    注: depends-on适用于表面上看起来两个bean之间没有使用属性之类的强连接的bean,但是两个bean又确实存在前后依赖关系的情况,使用了depends-on的时候,依赖他人的bean是先于被依赖bean销毁的。 一般不会这么使用。

    也就是这样配置的情况,才会抛出 BeanCreationException 异常。

    if (isDependent(beanName, dep)) {// 判断返回 true, 抛出循环依赖的exception
       throw new BeanCreationException(mbd.getResourceDescription(), beanName,
             "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
    }
    

      

    2、正确的 xml 配置的循环依赖 demo

    <bean id="aService" class="com.zzf.spring.dependent.AService">
        <property name="bService" ref="bService"/>
    </bean>
    <bean id="bService" class="com.zzf.spring.dependent.BService">
        <property name="aService" ref="aService"/>
    </bean>
    

    3、注解的方式解决循环依赖

    @Service
    public class Aservice {
        @Autowired
        private BService service;
    }
    
    @Service
    public class BService {
        @Autowired
        private Aservice aservice;
    }
    

      

    都说这段是 解决 循环依赖的 关键所在:

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
       Object singletonObject = this.singletonObjects.get(beanName);
       if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
          synchronized (this.singletonObjects) {
             singletonObject = this.earlySingletonObjects.get(beanName);
             if (singletonObject == null && allowEarlyReference) {
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                   singletonObject = singletonFactory.getObject();
                   this.earlySingletonObjects.put(beanName, singletonObject);
                   this.singletonFactories.remove(beanName);
                }
             }
          }
       }
       return singletonObject;
    }
    

      这里主要涉及到3个缓存, singletonObjects,earlySingletonObjects, singletonFactories。

    • singletonObjects: 单例对象的 cache
    • singletonFactories: 单例对象工厂的 cache
    • earlySingletonObjects: 提前曝光的单例对象的 cache。(这是关键)

    这里只考虑 A--B --A的情况:

    Object sharedInstance = getSingleton(beanName); 
    

      第一次 getBean(A)的时候, 返回是 null, 会走如下流程:

    // Create bean instance.
    if (mbd.isSingleton()) {
       sharedInstance = getSingleton(beanName, () -> {
          try {
             return createBean(beanName, mbd, args);
          }
          catch (BeansException ex) {
             // Explicitly remove instance from singleton cache: It might have been put there
             // eagerly by the creation process, to allow for circular reference resolution.
             // Also remove any beans that received a temporary reference to the bean.
             destroySingleton(beanName);
             throw ex;
          }
       });
       bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
    

      在 createBean -> doCreate中有如下: addSingletonFactory() 提前 曝光当前类 工厂,到 singletonFactory中

    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
          isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
       if (logger.isTraceEnabled()) {
          logger.trace("Eagerly caching bean '" + beanName +
                "' to allow for resolving potential circular references");
       }
       addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }
    

      然后执行 protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) 进行 属性的 赋值。 在进行 B的属性赋值中, 发现B还没有初始化, 则会去 调用 BeadFactory.getBean(B) 进行 B的初始化。

      调用B的过程中,和A类似, 也会 提前 在singletonFactory 中曝光, 然后在 populateBean 中,注入 A属性值时, 因为A未初始化,再次 去请求 getBean(A), 这次 在 getSingleton()中,因为 A提前曝光,所以在getSingleton 中 返回 A(可能未完全初始化),最终调用 getObjectForBeanInstance 方法,返回 完全实例话的 bean A, 然后注入到B 中,并完成B的 初始化, bean都会 放进singletonObjects 缓存中。

      TODO: 在 populateBean 中怎么检测 到 properties,这块还需 仔细的去debug,还没完全理清楚。

    参考资料:http://cmsblogs.com/?p=2887,看了多次,总结的很好。

  • 相关阅读:
    python 网络客户端编程端口,模块
    Python反转
    ASP.NET的路由系统
    yield 关键字
    C# Lock关键字
    C#中as和is关键字
    13.4 上下文对象
    请求生命周期
    ASP.NET常用的指令
    ASP.NET Page 指令
  • 原文地址:https://www.cnblogs.com/idea-persistence/p/11261098.html
Copyright © 2011-2022 走看看