源码剖析 Spring 循环依赖

源码剖析 Spring 循环依赖

循环依赖在编程中是一个常见问题(当然,这并不是最佳实践)。并且,Spring 如何解决循环依赖这个问题在面试中也经常见。下面,D瓜哥就从源码的层面深入剖析一下这个问题。

示例程序

先展示一下示例程序:

package com.diguage.truman.context;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.stereotype.Component;

/**
 * @author D瓜哥, https://www.diguage.com/
 * @since 2020-05-24 13:02
 */
public class CircularDependenceSingletonTest {
  public static final Log log = LogFactory.getLog(CircularDependenceSingletonTest.class);

  @Test
  public void test() {
    AnnotationConfigApplicationContext applicationContext
        = new AnnotationConfigApplicationContext();
    applicationContext.register(Config.class);
    applicationContext.refresh();

    log.info(applicationContext.getBean(A.class));
    log.info(applicationContext.getBean(B.class));
    log.info(applicationContext.getBean(C.class));

    log.info("-A--------");
    A a = applicationContext.getBean(A.class);
    log.info(a);
    log.info(a.b);
    log.info("-B--------");
    B b = applicationContext.getBean(B.class);
    log.info(b);
    log.info(b.c);
    log.info("-C--------");
    C c = applicationContext.getBean(C.class);
    log.info(c);
    log.info(c.a);
  }

  @Configuration
  @Import(AbcImportSelector.class)
  public static class Config {
  }

  public static class AbcImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
      return new String[]{
          A.class.getName(),
          B.class.getName(),
          C.class.getName()};
    }
  }


  @Component
  public static class A {
    @Autowired
    B b;
  }

  @Component
  public static class B {
    @Autowired
    C c;
  }

  @Component
  public static class C {
    @Autowired
    A a;
  }
}

上述示例代码中的循环依赖情况如下:

循环依赖
Figure 1. 循环依赖

源码剖析

三级缓存

D瓜哥在 深入剖析 Spring 核心数据结构:BeanFactory 中,概要性地对 BeanFactory 的属性做了一一说明。 而其中的“三级缓存”属性,则是解决循环依赖问题的关键所在:

  1. Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256):Bean 名称到单例 Bean 的映射,用于存放完全初始化好的 Bean。可以理解成,这就是所谓的容器。这是一级缓存。

  2. Map<String, Object> earlySingletonObjects = new HashMap<>(16):Bean 到“未成熟”单例 Bean 的映射。该 Bean 对象只是被创建出来,但是还没有注入依赖。在容器解决循环依赖时,用于存储中间状态。这是二级缓存。

  3. Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16):Bean 名称到 Bean 的 ObjectFactory 对象的映射,存放 Bean 工厂对象。在容器解决循环依赖时,用于存储中间状态。这是三级缓存。

Bean 的获取过程就类似计算机缓存的作用过程:先从一级获取,失败再从二级、三级里面获取。在 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean) 方法中,可以明确看到整个过程:

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(beanName, allowEarlyReference)
/**
 * Return the (raw) singleton object registered under the given name.
 * <p>Checks already instantiated singletons and also allows for an early
 * reference to a currently created singleton (resolving a circular reference).
 * @param beanName the name of the bean to look for
 * @param allowEarlyReference whether early references should be created or not
 * @return the registered singleton object, or {@code null} if none found
 */
@Nullable
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;
}

Bean 创建过程

D瓜哥在 Spring Bean 生命周期概述 中专门讨论过 Bean 的生命周期函数。Bean 的实例创建和依赖注入是分开来处理的。具体到 Spring 的内部函数调用,有可以描述成如下:

Bean创建
Figure 2. Bean创建

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean 方法中,能明确看到三个方法的调用过程。在这个方法上打上断点,开始调试。

实例创建

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
  instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
  // 这个方法里面完成了对象创建,仅仅是对象
  // 执行完整个方法,可以看看控制台的变化
  /**
   * 创建 bean 实例,并将实例包裹在 BeanWrapper 实现类对象中返回。
   * createBeanInstance中包含三种创建 bean 实例的方式:
   *   1. 通过工厂方法创建 bean 实例
   *   2. 通过构造方法自动注入(autowire by constructor)的方式创建 bean 实例
   *   3. 通过无参构造方法方法创建 bean 实例
   *
   * 若 bean 的配置信息中配置了 lookup-method 和 replace-method,则会使用 CGLIB
   * 增强 bean 实例。关于lookup-method和replace-method后面再说。
   */
  instanceWrapper = createBeanInstance(beanName, mbd, args);
}
Object bean = instanceWrapper.getWrappedInstance();

关于 createBeanInstance 方法,已经在上面的注释中增加了响应说明,这里就不再贴代码了。

依赖注入

接着上面的代码,往下看,看如何完成注入的:

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
  mbd.resolvedTargetType = beanType;
}

// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
  if (!mbd.postProcessed) {
    try {
      applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
    }
    catch (Throwable ex) {
      throw new BeanCreationException(mbd.getResourceDescription(), beanName,
          "Post-processing of merged bean definition failed", ex);
    }
    mbd.postProcessed = true;
  }
}

// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
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));
}

// Initialize the bean instance.
Object exposedObject = bean;
try {
  // 设置属性,非常重要 FIXME
  populateBean(beanName, mbd, instanceWrapper);

addSingletonFactory

先来看看 addSingletonFactory 方法:

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory
/**
 * Add the given singleton factory for building the specified singleton
 * if necessary.
 * <p>To be called for eager registration of singletons, e.g. to be able to
 * resolve circular references.
 * @param beanName the name of the bean
 * @param singletonFactory the factory for the singleton object
 */
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
  Assert.notNull(singletonFactory, "Singleton factory must not be null");
  synchronized (this.singletonObjects) {
    if (!this.singletonObjects.containsKey(beanName)) {
      this.singletonFactories.put(beanName, singletonFactory);
      this.earlySingletonObjects.remove(beanName);
      this.registeredSingletons.add(beanName);
    }
  }
}

从这里可以明显看出,代码符合我们上面注释中的描述: singletonFactory 变量被放入到 singletonFactories 变量中了。

populateBean

再来看看 populateBean

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
/**
 * Populate the bean instance in the given BeanWrapper with the property values
 * from the bean definition.
 * @param beanName the name of the bean
 * @param mbd the bean definition for the bean
 * @param bw the BeanWrapper with bean instance
 */
@SuppressWarnings("deprecation")  // for postProcessPropertyValues
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
  // 省略 N 行代码……

  boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
  boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);

  PropertyDescriptor[] filteredPds = null;
  if (hasInstAwareBpps) {
    if (pvs == null) {
      pvs = mbd.getPropertyValues();
    }
    for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
      // 从这里开始注入依赖,
      // 如果 bp 是 CommonAnnotationBeanPostProcessor 或 AutowiredAnnotationBeanPostProcessor 就执行注入
      PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
      if (pvsToUse == null) {
        if (filteredPds == null) {
          filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
        }
        pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
        if (pvsToUse == null) {
          return;
        }
      }
      pvs = pvsToUse;
    }
  }
  if (needsDepCheck) {
    if (filteredPds == null) {
      filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
    }
    checkDependencies(beanName, mbd, filteredPds, pvs);
  }

  if (pvs != null) {
    applyPropertyValues(beanName, mbd, bw, pvs);
  }
}

Spring Bean 生命周期概述 中对 Bean 的生命周期做了概要的介绍。这里就体现出来 CommonAnnotationBeanPostProcessorAutowiredAnnotationBeanPostProcessor 的作用。上面我们用的是 @Autowired 注解。所以,这里使用 AutowiredAnnotationBeanPostProcessor 来处理。

依赖注入的调用链

查找依赖的调用链很繁琐,中间有牵涉到 Bean 创建的过程,这里只列出调用过程中的主要方法列表,需要请根据自己需要来单步调试。

完成依赖注入的调用链如下:

  1. org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean

  2. org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties

  3. org.springframework.beans.factory.annotation.InjectionMetadata#inject

  4. org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject

  5. org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency

  6. org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency

  7. org.springframework.beans.factory.config.DependencyDescriptor#resolveCandidate

  8. org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)

  9. org.springframework.beans.factory.annotation.InjectionMetadata#inject — 最后,还是在这里完成注入。

加入容器

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory<?>) 方法中,可以看到 Spring 在获得 Bean 实例后的处理过程:

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(String, ObjectFactory<?>)
/**
 * Return the (raw) singleton object registered under the given name,
 * creating and registering a new one if none registered yet.
 * @param beanName the name of the bean
 * @param singletonFactory the ObjectFactory to lazily create the singleton
 * with, if necessary
 * @return the registered singleton object
 */
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
  Assert.notNull(beanName, "Bean name must not be null");
  synchronized (this.singletonObjects) {
    Object singletonObject = this.singletonObjects.get(beanName);
    if (singletonObject == null) {
      if (this.singletonsCurrentlyInDestruction) {
        throw new BeanCreationNotAllowedException(beanName,
            "Singleton bean creation not allowed while singletons of this factory are in destruction " +
            "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
      }
      if (logger.isDebugEnabled()) {
        logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
      }
      beforeSingletonCreation(beanName);
      boolean newSingleton = false;
      boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
      if (recordSuppressedExceptions) {
        this.suppressedExceptions = new LinkedHashSet<>();
      }
      try {
        singletonObject = singletonFactory.getObject();
        newSingleton = true;
      }
      catch (IllegalStateException ex) {
        // Has the singleton object implicitly appeared in the meantime ->
        // if yes, proceed with it since the exception indicates that state.
        singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
          throw ex;
        }
      }
      catch (BeanCreationException ex) {
        if (recordSuppressedExceptions) {
          for (Exception suppressedException : this.suppressedExceptions) {
            ex.addRelatedCause(suppressedException);
          }
        }
        throw ex;
      }
      finally {
        if (recordSuppressedExceptions) {
          this.suppressedExceptions = null;
        }
        afterSingletonCreation(beanName);
      }
      if (newSingleton) {
        // 创建完 Bean 后,将其加入到容器中
        addSingleton(beanName, singletonObject);
      }
    }
    return singletonObject;
  }
}

加入容器的操作也很简单:

org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingleton
/**
 * Add the given singleton object to the singleton cache of this factory.
 * <p>To be called for eager registration of singletons.
 * @param beanName the name of the bean
 * @param singletonObject the singleton object
 */
protected void addSingleton(String beanName, Object singletonObject) {
  synchronized (this.singletonObjects) {
    this.singletonObjects.put(beanName, singletonObject);
    this.singletonFactories.remove(beanName);
    this.earlySingletonObjects.remove(beanName);
    this.registeredSingletons.add(beanName);
  }
}

小结

这里,假设 A → BB → A 两层循环依赖来说明问题

  1. 通过 applicationContext.getBean(A.class) 方法,委托给 AbstractBeanFactory#doGetBean 方法来尝试获取 Bean;获取不到则开始创建;

    1. Bean 是调用 instanceWrapper = createBeanInstance(beanName, mbd, args); 方法创建出来了实例,然后又通过 addSingletonFactory(beanName, () → getEarlyBeanReference(beanName, mbd, bean)); 将已经创建的实例封装到一个 ObjectFactory<?> singletonFactory 匿名类中,放入到三级缓存中。

    2. populateBean(beanName, mbd, instanceWrapper); 方法,通过 CommonAnnotationBeanPostProcessorAutowiredAnnotationBeanPostProcessorpostProcessProperties 查找依赖,完成注入。

      1. 查找依赖时,就会通过调用 getBean(beanName) 获取 Bean B。此时,还没有 Bean B,则会从这里的第二步开始执行,创建实例,封装后加入到三级缓存 singletonFactories 中,调用 populateBean(beanName, mbd, instanceWrapper); 方法,通过 CommonAnnotationBeanPostProcessorAutowiredAnnotationBeanPostProcessorpostProcessProperties 查找依赖,完成注入。依赖注入的过程,请看 依赖注入的调用链 小节。

      2. 到这里,就要查找 Bean A 了,一二三级缓存依次来查找(DefaultSingletonBeanRegistry#getSingleton(beanName, allowEarlyReference)),在三级缓存中,找到了对应的 ObjectFactory<?> singletonFactory 实例,然后调用 getObject() 方法,获得 A 的实例,将其加入到二级缓存中,将三级中的相关内容清理掉。从这里也可以看出,通过 AbstractBeanFactory#doGetBean 方法获得的 Bean 不一定是完全初始化好的 Bean,有可能是一个未完成初始化的实例对象。

      3. 获得 A 的实例后,就可以完成 Bean B 的初始化,调用 DefaultSingletonBeanRegistry#addSingleton 方法,将其加入一级缓存 singletonObjects 中,也就是容器中。(由于 Bean B 可以直接完成依赖注入,则它不会从三级缓存跳到二级缓存。最后的三级缓存在调用 addSingleton 方法时,直接被清理掉了。)

    3. 到这里就可以获取 Bean B 了,然后完成 A 的依赖注入。

  2. 最后,通过调用 DefaultSingletonBeanRegistry#addSingleton 方法,将 Bean A 加入到一级缓存 singletonObjects 中,也就是容器中。所有的初始化工作就完成了。

需要注意的是,有两种情况,Spring 是没办法完成循环注入的:

  1. 构造函数注入 — 这种要求在实例之前创建好依赖的实例,但是明显无法完成,所以不能解决循环依赖。

  2. PROTOTYPE 类型的 Bean 相互依赖 — 刚刚看到,上面的三级缓存变量都是为 SINGLETON 类型的 Bean 准备的。PROTOTYPE 类型的 Bean 在检查到循环依赖时,就直接抛异常了。