目录

  • ㈠ 先看一看循环依赖的几种形态
  • ㈡ Spring对循环依赖的支持
  • ⑴ 三级缓存
  • ⑵ 创建Bean的简单流程
  • ㈢ @DependsOn 注解
  • ㈣ 循环依赖分析
  • ⑴ 单例Bean全构造函数注入
  • ⑵ 单例Bean构造函数+setter方法注入
  • ⑶ 单例setter方法/字段注入
  • ⑷ 存在代理的单例setter方法注入
  • ⑸ 多例Bean之间的循环依赖
  • ⑹ JavaConfig不能配置循环依赖
  • ㈤ 总结


声明:文章内容仅代表个人对源码的理解和观点,可能存在理解错误的地方,欢迎留言探讨、指正。不喜勿喷,谢谢。
个人用的是 v5.2.4.RELEASE 版本的源码,因为几年前阅读时已经写了一些注释,所以没有拉取最新的源码,与最新的源码会存在一定的差异。

在本文中的Bean,如果没有特殊说明,都是指非懒加载的单例Bean。
个人习惯了将原型模式的Bean叫做多例Bean,大家在看文章的时候,将就将就。

循环依赖就是一个或多个Bean之间相互存在直接或间接的依赖关系,这种依赖关系形成了一个环形结构。

㈠ 先看一看循环依赖的几种形态

单个Bean自身循环依赖

springboot开启支持循环依赖 spring是否支持循环依赖_java


两个Bean之间的循环依赖。

springboot开启支持循环依赖 spring是否支持循环依赖_springboot开启支持循环依赖_02


多个Bean之间的循环依赖,下图是3个Bean之间的循环依赖,有时候可能是四个、五个甚至更多个Bean。这种形态如果不熟悉代码,是比较难发现的。

springboot开启支持循环依赖 spring是否支持循环依赖_实例化_03

㈡ Spring对循环依赖的支持

Spring默认支持单例Bean的循环依赖,如果将BeanFactoryallowCircularReferences属性设置为false,则可以禁止循环依赖。

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
      implements AutowireCapableBeanFactory {

   private boolean allowCircularReferences = true;
   
   public void setAllowCircularReferences(boolean allowCircularReferences) {
       this.allowCircularReferences = allowCircularReferences;
   }
}

⑴ 三级缓存

Spring通过三级缓存的方式实现循环依赖,先看一下Spring的三级缓存,从缓存的名字就可以看出,只有单例Bean才会存入缓存中。

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
   /**
    * Cache of singleton objects: bean name to bean instance.
    * 单例对象的缓存:bean名称 -> bean实例 的映射关系。
    * 一级缓存
    * 保存的是实例化、属性填充、初始化、AOP代理都已完成的单例Bean。
    */
   private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

   /**
    * Cache of singleton factories: bean name to ObjectFactory.
    * 单例对象工厂的缓存:bean名称 -> 对象工厂 的映射关系。
    * 三级缓存
    * 存的是对象工厂(ObjectFactory),对象工厂可以返回原生Bean,也可以返回AOP代理对象,一般称为提前暴露的Bean引用。
    */
   private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

   /**
    * Cache of early singleton objects: bean name to bean instance.
    * 早期单例对象的缓存:bean名称 -> bean实例 的映射关系。
    * 二级缓存
    * 保存的是已实例化 或 已是AOP代理对象,但未完成属性填充、初始化的单例Bean,这种对象一般称为早期对象。
    */
   private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
}

单例Bean实例化完成之后,属性填充之前,将Bean包装成一个ObjectFactory对象存入三级缓存,用于提前暴露Bean的引用。

// DefaultSingletonBeanRegistry
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
   Assert.notNull(singletonFactory, "Singleton factory must not be null");
   synchronized (this.singletonObjects) {
      if (!this.singletonObjects.containsKey(beanName)) { // 一级缓存中没有Bean才能放入三级缓存
         this.singletonFactories.put(beanName, singletonFactory); // 将Bean工厂添加到三级缓存
         this.earlySingletonObjects.remove(beanName); // 删除二级缓存中的对象
         this.registeredSingletons.add(beanName); // 添加到已注册列表
      }
   }
}

单例Bean创建完成之后,会将Bean存到一级缓存,并删除二级、三级缓存中的值。

// DefaultSingletonBeanRegistry
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); // 添加到已注册列表
   }
}

如果正在创建的Bean依赖另外一个Bean时,会通过BeanFactory.getBean()方法从IoC容器中获取Bean,此时会先从缓存中获取,缓存中没有才会执行Bean的创建流程。

// DefaultSingletonBeanRegistry 从缓存中获取单例Bean
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
   // 先从一级缓存中找
   Object singletonObject = this.singletonObjects.get(beanName);

   // 一级缓存没有,判断beanName对应的Bean是不是当前正在创建的Bean,如果是,则先从二级和三级缓存中查找
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      synchronized (this.singletonObjects) {
         /*
          * 从二级缓存中找,二级缓存中保存了所有提前曝光的单例
          * 何为提前曝光的对象(早期对象):通过ObjectFactory创建出来的,还没给bean的属性进行赋值和初始化的对象就是早期对象
          */
         singletonObject = this.earlySingletonObjects.get(beanName);

         // 二级缓存也没有,并且允许早期依赖,则从三级缓存中找
         if (singletonObject == null && allowEarlyReference) {
            /*
             * 如果允许早期依赖,从三级缓存中找到Bean的ObjectFactory对象
             * 在getBean的过程中,当bean实例化完成之后,会将早期对象包裹成一个ObjectFactory缓存到三级缓存中
             */
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            // 存在于三级缓存,则取出提前暴露的对象放入二级缓存
            if (singletonFactory != null) {
               /*
                * 包装ObjectFactory存入三级缓存的源码在 AbstractAutowireCapableBeanFactory 类中,源码如下:
                * addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
                * 所以,ObjectFactory.getObject()会调用 AbstractAutowireCapableBeanFactory.getEarlyBeanReference()
                * 若启用了AOP功能,会执行 AbstractAutoProxyCreator.getEarlyBeanReference() 方法,如果Bean存在匹配的切点,最终返回一个AOP代理对象
                * 注意:能够生成AOP代理对象的处理类有多个,此处只能返回通过 AbstractAutoProxyCreator 生成的AOP对象,若是需要使用其它类生成AOP代理对象,这里是无法处理的,返回的是原对象
                */
               singletonObject = singletonFactory.getObject();
               this.earlySingletonObjects.put(beanName, singletonObject); // 早期对象存入二级缓存中
               this.singletonFactories.remove(beanName); // 删除三级缓存中的FactoryBean
            }
         }
      }
   }
   return singletonObject;
}

⑵ 创建Bean的简单流程

在介绍循环依赖之前,先看一看创建Bean的简单流程,循环依赖中会反复的用到下图中的一些步骤,在后续的流程图中,可能会省略掉其中的一些步骤,以便看起来比较简洁。

springboot开启支持循环依赖 spring是否支持循环依赖_java_04


绿色与缓存操作相关。

橙色与多例的循环依赖检查有关。

红色与代理对象的循环依赖检查有关。

黄色与构造函数循环依赖检查有关。

㈢ @DependsOn 注解

在验证循环依赖注入之前,先了解一下 @DependsOn 注解,因为在后面的验证过程中会用到。
使用方法如下代码所示,在创建Bean serviceA 之前,会先创建 @DependsOn 注解中指定的 Bean(serviceB, serviceC)。

@Component
@DependsOn(value = {"serviceB, serviceC"})
public class ServiceA {
   public ServiceA() {
   }
}

从注解的名字来看,是用于配置依赖关系的,从代码执行结果来看,此注解还可以人为配置Bean的创建顺序
@DependsOn 注解不支持循环依赖,否则创建Bean时会抛出异常(注意:是创建Bean时才会异常,不能说是启动时异常,例如将Bean配置成懒加载,启动并不会出现异常),如果配置的Bean不存在,也会抛出异常。如下代码所示:

@Component
@DependsOn(value = {"serviceB"})
public class ServiceA {
   public ServiceA() {
   }
}

@Component
@DependsOn(value = {"serviceA"})
public class ServiceB {
   public ServiceB() {
   }
}

@DependsOn循环依赖抛出异常的源码:

// AbstractBeanFactory  
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
      // 。。。。。。 省略大量代码 。。。。。。
      try {
         // 获取要实例化的Bean的BeanDefinition对象
         final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
         checkMergedBeanDefinition(mbd, beanName, args);

         // Guarantee initialization of beans that the current bean depends on.
         // 初始化 depends-on 指定依赖的 Bean
         String[] dependsOn = mbd.getDependsOn();
         if (dependsOn != null) {
            for (String dep : dependsOn) {
               // 判断是否存在循环依赖,depends-on 不能有循环依赖,否则抛出异常
               if (isDependent(beanName, dep)) {
                  throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
               }
               registerDependentBean(dep, beanName); // 注册Bean的依赖关系
               try {
                  getBean(dep); // 初始化 depends-on 依赖的 bean
               }
               catch (NoSuchBeanDefinitionException ex) {
                  throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
               }
            }
         }
         // 创建Bean 。。。。。。 省略大量代码 。。。。。。
}

㈣ 循环依赖分析

目前常用的注入方式有构造函数注入和setter方法注入,先验证一下Spring对这两种注入方式的支持。

⑴ 单例Bean全构造函数注入

测试代码如下,当创建Bean时,会抛出异常:

@Component
public class ServiceA {
   private ServiceB serviceB;

   @Autowired
   public ServiceA(ServiceB serviceB) {
      this.serviceB = serviceB;
   }
}

@Component
public class ServiceB {
   private ServiceA serviceA;

   @Autowired
   public ServiceB(ServiceA serviceA) {
      this.serviceA = serviceA;
   }
}

异常信息如下,可以看出原始异常是 BeanCurrentlyInCreationException

org.springframework.beans.factory.UnsatisfiedDependencyException: 
Error creating bean with name 'serviceA' defined in file [XXX\ServiceA.class]: 
Unsatisfied dependency expressed through constructor parameter 0; nested exception is 
org.springframework.beans.factory.UnsatisfiedDependencyException: 
Error creating bean with name 'serviceB' defined in file [XXX\ServiceB.class]: 
Unsatisfied dependency expressed through constructor parameter 0; nested exception is 
org.springframework.beans.factory.BeanCurrentlyInCreationException: 
Error creating bean with name 'serviceA': Requested bean is currently in creation: Is there an unresolvable circular reference?

抛出异常的源码:

// DefaultSingletonBeanRegistry
protected void beforeSingletonCreation(String beanName) {
   /*
    * inCreationCheckExclusions 从 singletonsCurrentlyInCreation 中排除的 beanName,在AOP的处理中可能会有值,一般情况下是空的
    * singletonsCurrentlyInCreation 是一个Set,用于保存当前正常创建的Bean的名称,当已存在相同的值时,Set.add()返回false
    */
   if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
   }
}

全构造函数循环依赖,抛出异常的流程:

springboot开启支持循环依赖 spring是否支持循环依赖_实例化_05


从上图中可以看出,构造函数循环依赖时,抛出异常的关键是 DefaultSingletonBeanRegistry 类中的 singletonsCurrentlyInCreation 属性:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    // 当前正在创建中的单例 bean 的 beanName 集合
    private final Set<String> singletonsCurrentlyInCreation =
          Collections.newSetFromMap(new ConcurrentHashMap<>(16));
}

从上面的分析可以看出,Spring不支持全构造函数的循环依赖。实例化serviceA时,要先实例化serviceB,实例化serviceB时,又发现要先实例化serviceA,这样就造成了死循环。

⑵ 单例Bean构造函数+setter方法注入

此模式也可以叫做部分构造函数注入。
如果将上面的注入方式修改成如下形式:一个是构造函数注入,一个是setter方法(或者字段)注入,使用 @DependsOn 或者 @Lazy 保证serviceB先被实例化,则可以循环依赖成功,因为实例化serviceA时,serviceB已经实例化了,并且已经保存在三级缓存中,直接取值即可。
如果是先实例化serviceA,仍然会抛出异常。

@DependsOn("serviceB")
// @Lazy
@Component
public class ServiceA {
   private ServiceB serviceB;

   @Autowired
   public ServiceA(ServiceB serviceB) {
      this.serviceB = serviceB;
   }
}

@Component
public class ServiceB {
   @Autowired
   private ServiceA serviceA;
}

上面代码的执行流程如下图所示,有几个点需要说明:

  1. 除黄色步骤以外,其它步骤都是由检查 serviceA 的 @DependsOn 触发的。
  2. 在检查 @DependsOn 的前后都会检查缓存中是否已有Bean实例,检查 @DependsOn 之前从三个缓存中查找Bean,检查 @DependsOn 之后只从一级缓存中查找Bean。
  3. 一个Bean的 @DependsOn 可能会被检查多次。

springboot开启支持循环依赖 spring是否支持循环依赖_实例化_06

在面试的时候,经常有人回答说Spring不支持构造函数循环依赖。其实这是不完全正确的,Spring只是不支持全构造函数的循环依赖,如果是部分构造函数的循环依赖,还是支持的,只要保证使用构造函数循环依赖的Bean后实例化。

⑶ 单例setter方法/字段注入

测试代码如下:

@Component
public class ServiceA {
   @Autowired
   private ServiceB serviceB;
}

@Component
public class ServiceB {
   @Autowired
   private ServiceA serviceA;
}

上面代码循环依赖的流程如下图所示:

springboot开启支持循环依赖 spring是否支持循环依赖_实例化_07

在面试时,问到Spring的循环依赖,大部分人都会回答Spring支持单例Bean的循环依赖。然后我会接着问:是单例就肯定能成功吗?得到的答案基本都是肯定的。其实这不完全正确,我们看一看下面的场景。

⑷ 存在代理的单例setter方法注入

测试代码如下:

@Component
public class ServiceA {
   @Autowired
   private ServiceB serviceB;

   @Async
   public void asyncExecute () {
   }
}

@Component
public class ServiceB {
   @Autowired
   private ServiceA serviceA;
}

上面的代码在创建Bean时,会抛出 BeanCurrentlyInCreationException 异常:

org.springframework.beans.factory.BeanCurrentlyInCreationException: 
Error creating bean with name 'serviceA': 
Bean with name 'serviceA' has been injected into other beans [serviceB] in its raw version as part of a circular reference,
but has eventually been wrapped. This means that said other beans do not use the final version of the bean. 
This is often the result of over-eager type matching - consider using 'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.

抛出异常的源码分析:

// AbstractAutowireCapableBeanFactory
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
      throws BeanCreationException {

   // Instantiate the bean.
   BeanWrapper instanceWrapper = null;
   if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }
   /*
    * 第一步:实例化Bean
    */
   if (instanceWrapper == null) {
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   // 实例化后的Bean对象,这里是早期对象,还没有进行属性注入
   final Object bean = instanceWrapper.getWrappedInstance();
   // 。。。。。。省略部分代码。。。。。。
   // 判断该对象是否允许暴露早期对象,解决循环依赖的关键步骤
   boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
         isSingletonCurrentlyInCreation(beanName)); // 如果当前是单例,且允许循环依赖而且当前bean处于创建状态,那么允许暴露早期对象

   // 如果需要提前暴露单例Bean的早期对象,则将该Bean放入singletonFactory 单例工厂(三级缓存)中,解决循环引用的问题
   if (earlySingletonExposure) {
      // 。。。。。。日志打印,省略。。。。。。
      /*
       * 解决循环引用的问题,将早期对象包装成一个ObjectFactory对象,并缓存到三级缓存中(key是beanName,value是一个ObjectFactory),提前暴露对象的引用
       * 当从三级缓存中获取早期对象,调用ObjectFactory.getObject()方法时,会执行 getEarlyBeanReference 方法,
       * 若启用了AOP功能,会执行 AbstractAutoProxyCreator.getEarlyBeanReference() 方法,如果Bean存在匹配的切点,最终返回一个AOP代理对象
       * 注意:能够生成AOP代理对象的处理类有多个,此处只能返回通过 AbstractAutoProxyCreator 生成的AOP对象,若是需要使用其它类生成AOP代理对象,这里是无法处理的,返回的是原对象
       * 例如,AsyncAnnotationBeanPostProcessor 为使用了 @Async 注解的Bean生成代理对象,此处是无法处理的,在Bean初始化之后通过 BeanPostProcessor 的后置处理方法生成代理
       */
      addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
   }

   Object exposedObject = bean;
   try {
      /*
       * 第二步:填充属性
       */
      populateBean(beanName, mbd, instanceWrapper);

      // 第三步:初始化Bean。调用初始化方法,完成bean的初始化操作(AOP发生在这个步骤)
      exposedObject = initializeBean(beanName, exposedObject, mbd);
   }
   catch (Throwable ex) {
      // 。。。。。。异常处理,省略 。。。。。。
   }

   // 是否允许提前暴露单例Bean,如果允许,则需要做一次循环依赖之间的校验
   if (earlySingletonExposure) {
      /*
       * 从缓存中获取Bean,由于传递的 allowEarlyReference = false,所以只会在一级和二级缓存查找,不会从三级缓存中查找提前暴露的引用。
       * 如果不存在循环依赖,此处获取的值为null,,反过来说,如果此处获取到了值,就存在循环依赖。
       *
       * 假设ClassA依赖ClassB,ClassB又依赖ClassA,而且先创建ClassA。
       * ClassA实例化,加入三级缓存,在ClassA属性填充的时候,需要注入ClassB,于是去创建ClassB,
       * ClassB实例化,加入三级缓存,在ClassB属性填充的时候,需要注入ClassA,于是从三级缓存中取出ClassA,这里取出的可能是ClassA的代理对象,将ClassA放入二级缓存,并返回ClassA,
       * ClassB属性填充完成,初始化完成,然后ClassB走到了这里,此时 exposedObject 是ClassB初始化之后的对象,它可能是一个AOP代理对象,
       * ClassB此时只是存在于三级缓存中,由于 allowEarlyReference = false,禁止查找三级缓存中的早期引用,
       * 所以,从缓存中是获取不到ClassB的实例,跳过这一步的校验,在外层ClassB会被放入一级缓存,然后删除二级、三级缓存中的ClassB实例。
       * 此时回到了ClassA的属性填充流程,从一级缓存拿到ClassB,ClassA属性填充完成,初始化完成,然后走到这里,
       * 此时,ClassA已经存在二级缓存,所以这里能够获取到ClassA提前暴露的实例,可能是ClassA的AOP代理对象。
       */
      Object earlySingletonReference = getSingleton(beanName, false);
      if (earlySingletonReference != null) { // 上面的例子,ClassA会进入这里
         /*
          * 进入这里,就说明存在循环依赖
          * exposedObject 是初始化之后的对象,它可能是一个 AbstractAutoProxyCreator 或者其它处理器(AsyncAnnotationBeanPostProcessor)生成的AOP代理对象。
          * 而 bean 指向的是刚实例化的对象,如果两者相等,说明bean在初始化的过程中没有产生代理对象,
          * 于是将 exposedObject 指向二级缓存中的对象,此对象可能是原生的bean,也可能是通过 AbstractAutoProxyCreator 生成的代理对象,
          * 所以这一步走完之后, exposedObject 是指向终的Bean实例了,如果需要代理则已经是代理对象,如果无需代理则是原生Bean。
          * 如果此处不相等,则说明在初始化的过程中产生了代理对象,并且不是 AbstractAutoProxyCreator 生成的,可能是 AsyncAnnotationBeanPostProcessor。
          */
         if (exposedObject == bean) {
            exposedObject = earlySingletonReference;
         }
         /*
          * allowRawInjectionDespiteWrapping 是否在循环依赖的情况下注入原始 bean 实例,默认为 false
          * hasDependentBean() 方法用于判断是否有Bean依赖了当前Bean,例如这里判断是否有Bean依赖ClassA
          * 进入这里,说明循环依赖中的Bean在初始化的时候,被其他BeanPostProcessor处理器生成了代理对象,
          * 所以这一步校验的是 依赖当前bean 的 其它bean是否已经创建完成,如果创建完成,那么它所依赖的Bean与实际的Bean不相同,需要抛出异常。
          * 对上面的例子做个扩展进行说明,ClassA中使用了@Async注解,所以在初始化的时候被 AsyncAnnotationBeanPostProcessor 生成了代理对象,
          * 而ClassB中注入的是ClassA的原生对象,两个对象不相同,所以抛出异常。
          */
         else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
            // 依赖当前 Bean (ClassA) 的 其它beanName(ClassB)
            String[] dependentBeans = getDependentBeans(beanName);
            // 用于保存已经创建完成的beanName,上面的例子中 ClassB 就会被保存到这里
            Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
            for (String dependentBean : dependentBeans) {
               /*
                * 判断ClassB是否已经创建完成
                * ClassB 已经创建完成,removeSingletonIfCreatedForTypeCheckOnly方法返回false,ClassB加入集合;
                * ClassB 未创建完成,尝试清除ClassB在三个缓存中的值,所以ClassB在后续会重新走一遍创建流程,removeSingletonIfCreatedForTypeCheckOnly返回true,ClassB不加入集合
                */
               if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                  actualDependentBeans.add(dependentBean);
               }
            }
            if (!actualDependentBeans.isEmpty()) {
               /*
                * 以上面的例子说明这个异常:
                * 名称为“ClassA”的 Bean 已作为循环依赖的一部分注入到其他 bean [ClassB] 中,但在注入之后,ClassA被生成了AOP代理对象。
                * 这意味着 ClassB 依赖的不是 ClassA 的最终版本。
                */
               throw new BeanCurrentlyInCreationException(beanName,
                     "Bean with name '" + beanName + "' has been injected into other beans [" +
                     StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                     "] in its raw version as part of a circular reference, but has eventually been " +
                     "wrapped. This means that said other beans do not use the final version of the " +
                     "bean. This is often the result of over-eager type matching - consider using " +
                     "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
            }
         }
      }
   }

   // 。。。。。。Register bean as disposable. 。。。。。。
   return exposedObject;
}

抛出异常的流程图:

springboot开启支持循环依赖 spring是否支持循环依赖_实例化_08


从上面的分析可以看出,存在代理对象的循环依赖抛出异常,主要关系到两个属性:

一个是 AbstractBeanFactory 类中的 alreadyCreated,用于先行标记 Bean 已经创建完成。

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
    // 已至少创建一次的 bean 的名称。
    private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
}

一个是 DefaultSingletonBeanRegistry 类中的 dependentBeanMap,用于记录Bean之间的依赖关系

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    // Bean的依赖关系。key:被依赖的bean的beanName(keyBean),value:需要依赖keyBean的 bean的beanName(valueBean)。valueBean 依赖 keyBean
    // 例如,beanA 依赖 beanX,beanB 依赖 beanX,那么 key 是 beanX,value 是存放 beanA 和 beanB 的集合。
    private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
}

和部分构造函数一样,如果我们使用 @DependsOn 或者 @Lazy 注解,让serviceB先实例化,在serviceB进行属性填充时,serviceA会进行实例化,并先进行初始化,此时就会产生代理对象,那么注入到serviceB中的就是最终的serviceA的代理对象,此时就不会出现异常。修改后的代码如下:

@DependsOn("serviceB")
// @Lazy
@Component
public class ServiceA {
   @Autowired
   private ServiceB serviceB;

   @Async
   public void asyncExecute () {
   }
}

@Component
public class ServiceB {
   @Autowired
   private ServiceA serviceA;
}

需要特别说明的点:
如果代理对象是通过 AbstractAutoProxyCreator 的子类生成的,例如我们自定义的AOP,不管哪个Bean先实例化、属性填充、初始化,循环依赖都没有问题的。
原因就在于提前暴露的 ObjectFactory,在执行getObject()方法时,会执行到 AbstractAutoProxyCreator.getEarlyBeanReference() 方法,先将原生的Bean缓存起来(earlyProxyReferences),然后生成一个代理对象返回,存入二级缓存中即是AOP代理对象了。在Bean初始化阶段,执行 AbstractAutoProxyCreator 子类的后置处理器时,删除缓存(earlyProxyReferences),并判断原生Bean是否存在缓存中,若存在,则直接返回,不再创建代理对象。
@Async注解是通过 AsyncAnnotationBeanPostProcessor 处理器生成代理对象,此处理器不是 AbstractAutoProxyCreator 的子类。

⑸ 多例Bean之间的循环依赖

前面分析的都是单例Bean的循环依赖,现在分析一下多例的循环依赖。测试代码如下:

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
public class ServiceA {
   @Autowired
   private ServiceB serviceB;
}

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
public class ServiceB {
   @Autowired
   private ServiceA serviceA;
}

上面的代码在获取Bean时,会抛出如下异常,可以看出,原始异常仍然是 BeanCurrentlyInCreationException

org.springframework.beans.factory.UnsatisfiedDependencyException: 
Error creating bean with name 'serviceA': Unsatisfied dependency expressed through field 'serviceB'; nested exception is 
org.springframework.beans.factory.UnsatisfiedDependencyException: 
Error creating bean with name 'serviceB': Unsatisfied dependency expressed through field 'serviceA'; nested exception is 
org.springframework.beans.factory.BeanCurrentlyInCreationException: 
Error creating bean with name 'serviceA': Requested bean is currently in creation: Is there an unresolvable circular reference?

异常是在 AbstractBeanFactory.doGetBean() 方法中抛出的:

// AbstractBeanFactory
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
   // 转换beanName
   final String beanName = transformedBeanName(name);
   // 省略大量代码
   if (sharedInstance != null && args == null) {
    // 缓存中已有Bean,走这个分支,省略 。。。。。。
   }
   else { // 没有缓存中没有Bean,会进入这个分支
      // 判断beanName是否是一个已经正在创建的多例Bean,成立返回true,抛出异常
      if (isPrototypeCurrentlyInCreation(beanName)) {
         throw new BeanCurrentlyInCreationException(beanName);
      }
      // 省略好多好多代码 。。。。。。
   }
    // 省略好多代码。。。。。。
   return (T) bean;
}

多例循环依赖抛出异常的流程图如下,由于多例不会添加到三个缓存中,所以关于缓存的部分就不画了:

springboot开启支持循环依赖 spring是否支持循环依赖_java_09


从上面的图中可以看出 AbstractBeanFactory 类中的 prototypesCurrentlyInCreation 属性发挥着重要的作用,此属性的源码如下:

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
    // 当前线程"正在创建中的多例Bean的缓存"
    private final ThreadLocal<Object> prototypesCurrentlyInCreation =
            new NamedThreadLocal<>("Prototype beans currently in creation");
}

多例Bean之间的循环依赖,如果不抛出异常,就有点子子孙孙无穷尽也的味道了。

如果是一个多例Bean和一个单例Bean之间的循环依赖,Spring是支持的,因为单例Bean在IoC容器启动的时候进行创建,实例化之后,存入三级缓存中,属性填充时发现依赖多例,于是创建一个多例Bean,多例Bean属性填充时从三级缓存中得到单例Bean,完成属性填充和初始化,接着单例Bean也能完成了属性填充和初始化,并存入一级缓存。

⑹ JavaConfig不能配置循环依赖

下面的这个示例代码,会出现 java.lang.StackOverflowError 异常:

@Configuration
public class CircularDependencyConfiguration {
   @Bean
   public ServiceA serviceA() {
      ServiceA serviceA = new ServiceA();
      serviceA.setServiceB(serviceB());
      return serviceA;
   }

   @Bean
   public ServiceB serviceB() {
      ServiceB serviceB = new ServiceB();
      serviceB.setServiceA(serviceA());
      return serviceB;
   }
}

如果改成下面这种形式,会抛出 BeanCurrentlyInCreationException 异常,这个好理解,因为这种方式配置的Bean,在创建过程中被识别为构造函数注入,代码执行流程也与构造函数注入相似。

@Configuration
public class CircularDependencyConfiguration {
   @Bean
   public ServiceA serviceA(ServiceB serviceB) {
      ServiceA serviceA = new ServiceA();
      serviceA.setServiceB(serviceB);
      return serviceA;
   }

   @Bean
   public ServiceB serviceB(ServiceA serviceA) {
      ServiceB serviceB = new ServiceB();
      serviceB.setServiceA(serviceA);
      return serviceB;
   }
}

这种情况的流程图就不画了,画得手疼。

㈤ 总结

对上面分析的场景做一个简单的总结:

  1. 全构造函数注入的循环依赖,Spring不支持。
  2. 一个使用构造函数注入,一个使用setter方法注入的循环依赖,借助 @DependsOn@Lazy 注解,让使用setter方法注入的Bean先实例化,则可以完成循环依赖。
  3. 全setter方法注入,在不会生成AOP代理对象,或者只会通过 AbstractAutoProxyCreator 的子类生成代理对象的场景下,Spring是支持的。
  4. 全setter方法注入,存在非 AbstractAutoProxyCreator 子类生成代理对象的场景,借助 @DependsOn@Lazy 注解,让代理对象先被创建出来,则可以完成循环依赖。
  5. 全多例Bean之间的循环依赖,Spring不支持。
  6. 一个多例Bean,一个单例Bean之间的循环依赖,Spring支持。
  7. JavaConfig中配置的循环依赖,Spring不支持。