Spring源码之循环依赖

1.什么是循环依赖

        在Java中,我们可以使用如下方式,使得两个Service互相持有,代码如下:

@Component
public class AService {
    private BService bService;
    
    @Autowired
    public void setBService(BService bService) {
        this.bService = bService;
    }
}

@Component
public class BService {
    private AService aService;
    
    @Autowired
    public void setAService(AService aService) {
        this.aService = aService;
    }
}

public class Logic {
    public static void main(String[] args) {
        AService aService = new AService();
        BService bService = new BService();
        
        aService.setBService(bService);
        bService.setAService(aService);
    }
}

但Logic#main()这种情况并不会由于循环依赖而导致死循环等问题,循环依赖的问题主要在依赖注入(DI)框架如Spring中出现,为什么这么说呢?

        在《Spring源码之Bean的生命周期》中,我们知道Bean的创建有BeanDefinition加载、Bean实例化、属性填充(依赖注入)、初始化的过程,在BeanDefinition加载、Bean实例化过程中并不会有问题,关键在于属性填充(依赖注入)这步,这步完成了注入点的值注入。值注入前需要先找到要注入的Bean,并将要注入的Bean完成创建流程,在这个过程中,如果Bean之间存在循环依赖且没有其他处理,就会导致死循环的问题,具体如下图所示:

2.如何解决循环依赖

        通过上面的循环依赖生成图中,我们发现要解决这个问题,对于依赖的对象是单例对象的情况下,引入一个中间缓存即可,对于依赖的对象是多例对象的情况下,无法解决,接下来进行两种情况的分析。

2.1 单例对象

2.1.1 依赖单例普通对象

        对于依赖的对象是单例对象的情况下,引入的中间缓存先将其称之为earlySingletonObjects,即将创建的AService实例(半成品)保存到中间缓存earlySingletonObjects中,当创建BService实例进行依赖注入的过程中,将AService实例(半成品)传给BService即可,如下图所示:

通过中间缓存earlySingletonObjects,打破了循环依赖中创建依赖对象时还需重新创建原对象的问题,使得循环依赖得到解决。

2.1.2 依赖单例代理对象

        通过上述方式,解决了原始对象循环依赖注入的问题,但Bean如果需要被代理,在创建过程的初始化后步骤中会创建对应代理对象返回给外部使用,因此上述方式过程给BService赋值的AService对象,与最终创建的AService并不是一个,会导致逻辑执行不一致。要解决这个问题有两种方法解决:

方法①:提前完成AService代理对象的创建并放到中间缓存中;

方法②:提供一个Bean工厂,用于创建AService代理对象。

        方法①的提前完成AService代理对象的创建并放到中间缓存中的方式,会使得原本Bean的实例化、属性填充(依赖注入)、初始化(初始化前、初始化、初始化后)的流程变得复杂且不清晰不易理解,即当发生循环依赖时,会变成实例化、初始化后(创建代理对象)、属性填充(依赖注入)、、初始化(初始化前、初始化、初始化后)的流程,因此此方法并不合适。

        方法②的提供一个Bean工厂,用于创建AService代理对象,即在AService对象实例化后,提供一个Bean工厂,存放到中间缓存中,先将其称之为singletonFactories,接着执行到属性填充(依赖注入)步骤的创建BService过程中,通过从中间缓存singletonFactories中取得Bean工厂并执行创建对象的方法,从而得到AService对象的代理对象,完成BService的属性填充(依赖注入)步骤,进而完成BService对象的创建,使得循环依赖得到解决。整体步骤如下图所示:

        通过earlySingletonObjectssingletonFactories两个中间缓存,分别解决了基础对象循环依赖、代理对象循环依赖的问题,但实际使用过程中,这两种情况是并存的,因此需要一套流程,将两种情况兼容。

2.1.3 同时依赖普通/代理对象

        从代理对象循环依赖流程中我们发现Bean工厂是用于创建代理对象的,得到的结果也是一个Bean,而普通对象循环依赖流程中中间缓存earlySingletonObjects存放的也是对象,因此将singletonFactories作为earlySingletonObjects的底层缓存即可得到兼容两种情况的流程,具体如下图所示:

2.2 多例对象

        如果AService和BService均是多例对象,意味着Bean的属性填充(依赖注入)步骤发现依赖对象未创建时,必须调用对应创建方法,完成对象创建后进行注入才行,而新创建的对象不能用于下次依赖注入,也就无法存入缓存中进行复用,因此无法解决循环依赖中依赖多例对象的情况。

2.3 其他无法解决的循环依赖情况

        从《Spring源码之依赖注入》中我们知道依赖注入的方式有

  • 构造器注入
  • 属性注入
  • 字段注入
  • 方法参数注入

其中Bean的实例化需要执行构造方法才能完成,且实例化在属性填充(依赖注入)之前,因此构造器注入上的循环依赖无法解决,其余集中情况均可在Bean实例化后的属性填充(依赖注入)环节完成,因此可以解决。

3.源码分析

        通过上述过程,我们分析了循环依赖的解决方法,接下来将深入Spring源码,看看Spring是如何解决的,源码基于Spring5.3.37版本进行分析。

3.1 三级缓存

        在《Spring源码之Bean的生命周期》中我们知道了AbstractBeanFactory#doGetBean()完成了Bean的获取(创建),因此从这个方法作为入口进行分析,其中关于缓存相关源码如下:

protected <T> T doGetBean(
      String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
      throws BeansException {
   // 省略部分代码...

   // 先从缓存中尝试获取单例Bean,获取的Bean有可能是完整Bean,也可能是Bean(半成品)
   Object sharedInstance = getSingleton(beanName);
   // 省略部分Bean创建相关的代码...
}

我们可以看到在创建Bean之前,调用AbstractBeanFactory#getSingleton(String beanName)方法从缓存中尝试获取单例Bean,该方法内部调用了AbstractBeanFactory#getSingleton(String beanName, boolean allowEarlyReference)方法,该方法的源码如下:

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
   // 先从singletonObjects缓存中尝试获取完整的Bean对象
   Object singletonObject = this.singletonObjects.get(beanName);
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
       // 如果当前Bean处于正在创建中,则尝试从earlySingletonObjects缓存中获取Bean对象
      singletonObject = this.earlySingletonObjects.get(beanName);
      // 若获取的依旧是空,且支持创建早期引用,则尝试从singletonFactories缓存中获取Bean对象
      if (singletonObject == null && allowEarlyReference) {
         synchronized (this.singletonObjects) {
               // 再次尝试从singletonObjects缓存中尝试获取完整的Bean对象,因为可能存在其他线程创建完了该对象
            singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
               // 再次尝试从earlySingletonObjects缓存中获取Bean对象
               singletonObject = this.earlySingletonObjects.get(beanName);
               if (singletonObject == null) {
                   // 获取Bean工厂
                  ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                  if (singletonFactory != null) {
                      // 调用Bean工厂的getObject()方法获取Bean对象
                     singletonObject = singletonFactory.getObject();
                     // 将获取的Bean对象放到earlySingletonObjects缓存中
                     this.earlySingletonObjects.put(beanName, singletonObject);
                     // 将当前Bean对应的Bean工厂从singletonFactories缓存中移除
                     this.singletonFactories.remove(beanName);
                  }
               }
            }
         }
      }
   }
   return singletonObject;
}

从此处,我们发现了三个缓存,分别是:singletonObjectsearlySingletonObjectssingletonFactories,三者之间是递进关系,我们将其称之为三级缓存。其中earlySingletonObjects、singletonFactories与第二节分析解决循环依赖引入缓存一样,分别存放的是Bean对象(半成品)、Bean工厂,此处我们发现earlySingletonObjects的Bean对象(半成品)来自于从singletonFactories的Bean工厂创建的Bean对象,因此若singletonFactories不存在对应Bean的Bean工厂,则earlySingletonObjects也将没有Bean对象(半成品);singletonObjects则是存放的最终完整版Bean对象,供后续使用。

3.2 singletonFactories

        singletonFactories是在AbstractAutowireCapableBeanFactory#doCreateBean()方法中添加的Bean工厂,此方法作为Bean对象创建的入口方法,包含了Bean实例的创建、属性填充(依赖注入)、初始化步骤,这些均在《Spring源码之Bean的生命周期》中分析过,因此此处重点分析singletonFactories的值的来源,其核心源码如下:

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
       throws BeanCreationException {
    // 省略部分代码...
    if (instanceWrapper == null) {
        // 创建Bean实例
       instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    // 省略部分代码...

    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
          isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
       // 省略日志代码...
       // 如果当前Bean是单例Bean且配置了允许循环依赖,且当前Bean正在创建中,
       // Bean正在创建中,意味着Bean创建了一半,重新调用了Bean的创建方法来创建依赖对象
       // 则将包含当前Bean对象(半成品)BeanFactory匿名对象放到singletonFactories缓存中
       addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }

    // Initialize the bean instance.
    Object exposedObject = bean;
    try {
        // 属性填充(依赖注入)
       populateBean(beanName, mbd, instanceWrapper);
       // 初始化
       exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
    // 省略部分代码...

    if (earlySingletonExposure) {
       // 如果当前Bean是单例Bean且配置了允许循环依赖,且当前Bean正在创建中,再次尝试获取Bean对象
       // 此时可以从earlySingletonObjects中获取的Bean对象(半成品),因为在创建依赖对象过程中执行了BeanFactory
       Object earlySingletonReference = getSingleton(beanName, false);
       if (earlySingletonReference != null) {
           // 此处大部分情况下是相等,即使存在创建代理对象的过程,
           // 因为在创建依赖对象时创建了当前Bean的代理对象,
           // AbstractAutoProxyCreator#postProcessAfterInitialization()方法中将会将原始Bean对象直接返回
          if (exposedObject == bean) {
              // 因为发生循环依赖时,代理对象放到了earlySingletonObjects中,
              // 所以需要再次赋值,将代理对象赋值给exposedObject并返回给上层方法
             exposedObject = earlySingletonReference;
          }
          else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
             String[] dependentBeans = getDependentBeans(beanName);
             Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
             for (String dependentBean : dependentBeans) {
                if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                   actualDependentBeans.add(dependentBean);
                }
             }
             if (!actualDependentBeans.isEmpty()) {
                 // 省略抛出异常代码...
             }
          }
       }
    }

    // Register bean as disposable.
    try {
       registerDisposableBeanIfNecessary(beanName, bean, mbd);
    }
    catch (BeanDefinitionValidationException ex) {
       throw new BeanCreationException(
             mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
    }

    return exposedObject;
}

本方法总结下来就是:如果当前Bean是单例Bean且配置了允许循环依赖,且当前Bean正在创建中,则将包含当前Bean对象(半成品)BeanFactory匿名对象放到singletonFactories缓存中。如果存在创建代理对象的过程,则在Bean初始化完成后,返回前,需要将代理对象(完整)从earlySingletonObjects取出并返回。此处还引入了singletonsCurrentlyInCreation缓存,用于存储创建中的Bean名称,便于Bean创建流程中的相关判断。

关于() -> getEarlyBeanReference(beanName, mbd, bean)部分对应的核心源码如下:

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
       for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
          exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
       }
    }
    return exposedObject;
}

其内部实际调用的是AbstractAutoProxyCreator#getEarlyBeanReference()方法,其核心源码如下:

public Object getEarlyBeanReference(Object bean, String beanName) {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    // 将当前对象放到earlyProxyReferences缓存中,
    // 用于后续AbstractAutoProxyCreator#postProcessAfterInitialization()避免再次生成代理对象
    this.earlyProxyReferences.put(cacheKey, bean);
    // 创建代理对象
    return wrapIfNecessary(bean, beanName, cacheKey);
}

        其中关于创建代理对象(AOP)的核心AbstractAutoProxyCreator#postProcessAfterInitialization()将于后续进行深入分析。其内部通过earlyProxyReferences缓存避免了代理对象被提前创建后的再次创建,其核心源码如下:

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
       Object cacheKey = getCacheKey(bean.getClass(), beanName);
       // 如果earlyProxyReferences中不存在当前Bean,才会走代理对象创建过程
       if (this.earlyProxyReferences.remove(cacheKey) != bean) {
          return wrapIfNecessary(bean, beanName, cacheKey);
       }
    }
    return bean;
}

3.3 singletonObjects

通过AbstractAutowireCapableBeanFactory#doCreateBean()完成了Bean的完整创建,得到了完整的Bean对象,此时需要将完整的Bean对象放置到singletonObjects缓存中,对应逻辑在DefaultSingletonBeanRegistry#getSingleton(String beanName, ObjectFactory<?> singletonFactory) 方法中,其核心源码如下:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    synchronized (this.singletonObjects) {
        // 首先尝试从singletonObjects缓存中获取Bean对象
       Object singletonObject = this.singletonObjects.get(beanName);
       if (singletonObject == null) {
          // 省略部分代码...
             // 此处为调用AbstractAutowireCapableBeanFactory#createBean()得到Bean对象
             singletonObject = singletonFactory.getObject();
             newSingleton = true;
          }
          // 省略部分代码...
          if (newSingleton) {
              // 添加到singletonObjects缓存中
             addSingleton(beanName, singletonObject);
          }
       }
       return singletonObject;
    }
}

4.总结

        在Spring中,通过引入singletonsCurrentlyInCreation、singletonObjects、earlySingletonObjects、singletonFactories、earlyProxyReferences缓存,解决普通/代理对象的循环依赖问题。无法解决的循环依赖情况有:①构造方法注入;②依赖与被依赖的Bean均为多例Bean。

singletonsCurrentlyInCreation

在DefaultSingletonBeanRegistry类中定义,类型为Set<String>,存放了正在创建的Bean名称,用于循环依赖过程中判断Bean是否正在创建中。

singletonObjects

在DefaultSingletonBeanRegistry类中定义,类型为Map<String, Object>,作为一级缓存,存放了创建完成(经过实例化、依赖注入、初始化)的完整Bean对象(普通或代理对象),用于后续其他Bean创建获取依赖及运行过程中获取要使用的单例Bean。

earlySingletonObjects

在DefaultSingletonBeanRegistry类中定义,类型为Map<String, Object>,作为二级缓存,存放了半成品的Bean对象(经过实例化),若不存在循环依赖则此缓存为空。当对应Bean对象(半成品)经过依赖注入、初始化流程后,将自动变成完整的Bean对象(Java对象引用的作用)。

singletonFactories

在DefaultSingletonBeanRegistry类中定义,类型为Map<String, ObjectFactory<?>>,作为三级缓存,存放了BeanFactory,在需要动态代理的情况下为原始对象生成代理对象并返回,否则返回原始对象。用于延迟代理对象的创建。此缓存作为兼容普通/代理对象获取逻辑的缓存,避免了对创建代理对象(Spring的Bean生命周期中AOP(初始化后))的流程调整,保证了Bean生命周期的清晰与完整。

earlyProxyReferences

在AbstractAutoProxyCreator类中定义,类型为Map<Object, Object>,存放了提前创建了代理对象的原始普通Bean对象,该缓存在Bean生命周期的初始化后阶段中的AbstractAutoProxyCreator#postProcessAfterInitialization()方法中被移除,用于避免初始化后的AOP流程中重复创建代理对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值