03.Spring如何解决循环依赖

Spring思维导图
SpringBean加载流程
Spring如何解决循环依赖

一、什么是循环依赖

例如:A 依赖 B,B 又依赖 A,它们之间形成了循环依赖。或者是 A 依赖 B,B 依赖 C,C 又依赖 A。
image-20211122145748343

二、如何解决循环依赖

流程图如下:
在这里插入图片描述

三级缓存

/** 一级缓存 单例缓存池 用于保存所有的单实例 bean */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** 三级缓存 该map用户缓存 key为 beanName  value 为 ObjectFactory(包装为早期对象) */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** 二级缓存 ,用户缓存我们的key为beanName value是早期对象(对象属性还没进行赋值) */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

/** 该集合用户缓存当前正在创建bean的名称 */
private final Set<String> singletonsCurrentlyInCreation =
    Collections.newSetFromMap(new ConcurrentHashMap<>(16));
/**
 * 该方法是一个空壳方法
 * @param beanName bean的名称
 * @return 缓存中的对象(有可能是一个单例完整对象,也有可能是一个早期对象(用于解决循环依赖))
 */
@Override
@Nullable
public Object getSingleton(String beanName) {
   // 在这里 系统一般是允许早期对象引用的 allowEarlyReference通过这个参数可以控制解决循环依赖
   return getSingleton(beanName, true);
}
/**
 * (二级缓存可不可以能够解决循环依赖) 答案是:可以, 但是没有很好的扩展性
 * 原因: 获取三级缓存--getEarlyBeanReference()经过一系列的后置处理来给早期对象进行特殊化处理
 * 从三级缓存中获取包装对象的时候 ,他会经过一次后置处理器的处理对我们早期对象的bean进行
 * 特殊化处理,但是spring的原生后置处理器没有经过处理,而是留给了我们程序员进行扩展
 * singletonObject = singletonFactory.getObject();
 * 把三级缓存移植到二级缓存中
 * this.earlySingletonObjects.put(beanName, singletonObject);
 * 删除三级缓存中的
 * this.singletonFactories.remove(beanName);
 * @param beanName bean的名称
 * @param allowEarlyReference 是否允许暴露早期对象  通过该参数能控制是否能够解决循环依赖的.
 * @return
 *         这里可能返回一个null(IOC容器加载单实例bean的时候,第一次进来是返回null)
 *         也有可能返回一个单例对象(IOC容器加载了单实例了,第二次来获取当前的Bean)
 *         也可能返回一个早期对象(用于解决循环依赖问题)
 */
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
   /**
    * 第一步:我们尝试去一级缓存(单例缓存池中去获取对象,一般情况从该map中获取的对象是直接可以使用的)
    * IOC容器初始化加载单实例bean的时候第一次进来的时候 该map中一般返回空
    */
   Object singletonObject = this.singletonObjects.get(beanName);
   /**
    * 若在第一级缓存中没有获取到对象,并且singletonsCurrentlyInCreation这
    * 个list包含该beanName(即:正在创建,循环依赖了)
    * IOC容器初始化加载单实例bean的时候第一次进来的时候 该list中一般返回空,但是循环依赖
    * 的时候可以满足该条件
    */
   if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
      // 加锁,防止多线程情况下获取对象不完整
      synchronized (this.singletonObjects) {
         /**
          * 尝试去二级缓存中获取对象(二级缓存中的对象是一个早期对象)
          * 早期对象就是bean刚刚调用了构造方法,还来不及给bean的属性进行赋值的对象
          */
         singletonObject = this.earlySingletonObjects.get(beanName);
         /**
          * 二级缓存中也没有获取到对象,allowEarlyReference为true(参数是有上一个方法传递进来的true)
          */
         if (singletonObject == null && allowEarlyReference) {
            /**
             * 直接从三级缓存中获取 ObjectFactory对象 这个对接就是用来解决循环依赖的关键所在
             * 在ioc后期的过程中,当bean调用了构造方法的时候,把早期对象包裹成一个ObjectFactory
             * 暴露到三级缓存中
             */
            ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
            // 从三级缓存中获取到对象不为空
            if (singletonFactory != null) {
               /**
                * 在这里通过暴露的ObjectFactory 包装对象中,通过调用他的getObject()来获取我们的早期对象
                * 在这个环节中会调用到 getEarlyBeanReference()来进行后置处理
                */
               singletonObject = singletonFactory.getObject();
               // 把早期对象放置在二级缓存,
               this.earlySingletonObjects.put(beanName, singletonObject);
               // ObjectFactory 包装对象从三级缓存中删除掉
               this.singletonFactories.remove(beanName);
            }
         }
      }
   }
   return singletonObject;
}
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 + "'");
         }

         /**
          * 标记当前的bean马上就要被创建了
          * singletonsCurrentlyInCreation 在这里会把beanName加入进来,
          * 若第二次循环依赖(构造器注入会抛出异常)
          */
         beforeSingletonCreation(beanName);
         boolean newSingleton = false;
         boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
         if (recordSuppressedExceptions) {
            this.suppressedExceptions = new LinkedHashSet<>();
         }
         try {
            // <3> 初始化 bean
            // 这个过程其实是调用 createBean() 方法
            singletonObject = singletonFactory.getObject();
            newSingleton = true;
         }
         catch (IllegalStateException ex) {
            // 回调我们singletonObjects的get方法,进行正在的创建bean的逻辑
            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;
            }
            // <4> 后置处理
            // 主要做的事情就是把singletonsCurrentlyInCreation标记正在创建的bean从集合中移除
            afterSingletonCreation(beanName);
         }
         if (newSingleton) {
            // <5> 加入缓存中
            addSingleton(beanName, singletonObject);
         }
      }
      return singletonObject;
   }
}

其他

关闭循环依赖

applicationContext.setAllowCircularReferences(false);
applicationContext.register(AppConfig.class);
applicationContext.refresh();

如何扩展

bean 可以通过实现 SmartInstantiationAwareBeanPostProcessor 接口的getEarlyBeanReference()方法进行拓展
在这里插入图片描述

思考

  • Spring 如何解决循环依赖

    三级缓存,没有循环依赖直接创建对象后加入一级缓存 (单例池)。发生循环依赖后实例化(调用完构造方法,还未属性赋值等操作)后加入三级缓存,后置处理完成后加入二级缓存,同时移除三级缓存中的。完整 Bean 后加入到一级缓存 (单例池),同时移除二三及缓存中的

  • 为什么需要二级缓存和三级缓存

    二级缓存只要是为了分离成熟 Bean 和纯净 Bean (未注入属性) 的存放, 防止多线程中在 Bean 还未创建完成时读取到的Bean时不完整的。保证我们 getBean() 获取到的 Bean 的完整性。

    三级缓存主要为例解决代码的耦合和多线程情况下的读取性能问题。二级缓存完全可以解决循环依赖问题,但试想一下,在 AOP 的循环依赖中,每次都要去重新创建代理对象,代码耦合性太高。三级缓存存的是函数式接口,调用 Bean 的后置处理器

  • Spring 是否解决了构造函数的循环依赖

    没有,不调用构造函数,连纯净的 Bean (未注入属性) 都没有

  • Spring 是否解决了多例模式下的循环依赖

    没有,多例模式下,每次都会重新创建对象,没有缓存池,出现循环依赖会直接抛出异常

  • 只用一级缓存或二级缓存解决循环依赖可以吗

    如果只用一级缓存解决循环依赖也是可以的,只不过性能极低,扩展性、耦合性极差。需要加很多锁,在循环依赖下动态代理放到一级缓存,如果存在重复依赖,需要多次创建代理,为例避免 Bean 获取不完整,在创建的整个过程中都需要上锁。比如有 ABC… 等很多 Bean ,存在循环依赖正在创建,哪怕某一个创建好了也需要等待,性能极低。

    如果只用二级缓存,在特殊化处理某些 Bean 的时候,又会多次调用动态代理,而且这样做 getBean 和 createBean 必须放在 getSingleton 中,没有完全解耦

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值