Spring解决循环依赖

什么是循环依赖

循环依赖的关键点在循环,为了直观一点,我们看个图

image-20200405224151194

从图中我们看到,要想实例化类A,必须先要实例化类B,但是在实例化B之前也必须先实例化C,可想要实例化C,就要先实例化A,喏,死循环了,三个类相互引用又相互等待。

循环依赖如何产生的

循环依赖的产生可能有多种情况:

  1. A的构造器中依赖了B的实例,B的构造器中依赖了A的实例
  2. A的构造器中依赖了B的实例,B的属性或者setter依赖了A的实例
  3. A的属性或setter依赖了B的实例,B的属性或者setter依赖了A的实例

Spring IOC解决办法

Spring并不是能解决所有的循环依赖情况,比如上方的情况1是无法解决的,并且如果Bean的scope是protype的,也无法解决。

  1. scope=prototype

    此种情况无法解决循环依赖,因为如果获取bean实例的时候,如果类正在创建中,则会抛出异常,详情查看org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean方法

    // Fail if we're already creating this bean instance:
    // We're assumably within a circular reference.
    if (isPrototypeCurrentlyInCreation(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
    }
    

    从注释我们就可以看出:如果创建bean实例失败则多半因为存在循环依赖。

    /**
     * Return whether the specified prototype bean is currently in creation
     * (within the current thread).
     * @param beanName the name of the bean
     */
    protected boolean isPrototypeCurrentlyInCreation(String beanName) {
      Object curVal = this.prototypesCurrentlyInCreation.get();
      return (curVal != null &&
              (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
    }
    

    prototype类型的Bean在创建之前会进行标记和创建之后进行解标记

    if (mbd.isPrototype()) {
      // It's a prototype -> create a new instance.
      Object prototypeInstance = null;
      try {
        beforePrototypeCreation(beanName);
        prototypeInstance = createBean(beanName, mbd, args);
      }
      finally {
        afterPrototypeCreation(beanName);
      }
      bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
    }
    
    protected void beforePrototypeCreation(String beanName) {
      Object curVal = this.prototypesCurrentlyInCreation.get();
      if (curVal == null) {
        this.prototypesCurrentlyInCreation.set(beanName);
      }
      else ...
    }
    
    protected void afterPrototypeCreation(String beanName) {
      Object curVal = this.prototypesCurrentlyInCreation.get();
      if (curVal instanceof String) {
        this.prototypesCurrentlyInCreation.remove();
      }
      else ...
    }
    
  2. scope=singleton

    Spring通过三级缓存的模式解决非构造器注入引起的循环依赖,详情查看org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean方法

    Object sharedInstance = getSingleton(beanName);
    

    我们看下getSingleton方法:

    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
      // 第一级缓存获取
      Object singletonObject = this.singletonObjects.get(beanName);
      // 第一级未找到缓存且bean处于创建中(例如A定义的构造函数依赖了B对象,得先去创建B对象,或者在populatebean过程中依赖了B对象,得先去创建B对象,此时A处于创建中)
      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;
    }
    

    在singleton方法中出现了三个属性:singletonObjects,earlySingletonObjects,singletonFactories,这也就是上面说的三级缓存,这个方法的逻辑是先获取一级缓存,若一级缓存中不存在,则获取第二级缓存,二级缓存若也不存在则获取三级缓存,若三级缓存都不存在,则在getBean方法中当成prototype处理。那么这三级缓存中主要存的是什么东西呢?

    • 一级缓存:已经初始化完成的bean对象Cache
    • 二级缓存:提前曝光的bean对象Cache
    • 三级缓存:ObjectFactory工厂bean缓存, 存储实例化后的bean factory

    Spring创建一个Bean的实例大致经过三个步骤:

    1. createBeanInstance:调用Bean的构造器进行实例化,调用构造器并未填充属性
    2. populateBean:填充属性,也就是setter所有的property
    3. initializeBean:初始化Bean,会调用指定的init方法,或者afterPropertiesSet方法,这个时候类属性已经注入完成了

    三级缓存何时设置的

    三级缓存的设置点是什么时候?我们来看下类的创建过程org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {
    
      // 1. Instantiate the bean.
    
      // 2. 创建三级缓存
      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");
        }
        // 获取Bean的ObjectFactory并放入三级缓存
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
      }
    
      // Initialize the bean instance.
      Object exposedObject = bean;
      try {
        // 填充属性
        populateBean(beanName, mbd, instanceWrapper);
        // 初始化Bean
        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);
        }
      }
    
      return exposedObject;
    }
    
    • 可以看到第三级缓存是在createBeanInstance方法之后就被设置,这个时候Bean的对象已经被创建出来了,只不过是还不够完美,只是一个壳,但是在容器中已经可以根据对象引用被认出来了;
    • 第二级缓存是在getSingleton方法之后,若第三级缓存中已经存在,则将对象从三级缓存中转移到二级缓存中;
    • 完全初始化之后将自己放入到一级缓存singletonObjects中
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值