Spring — 循环依赖

读完这篇文章你将会收获到web

  • Spring 循环依赖能够分为哪两种
  • Spring 如何解决 setter 循环依赖
  • Spring 为什么是三级缓存 , 二级不行 ?
  • Spring 为啥不能解决构造器循环依赖

概述

循环依赖就是循环引用,两个或以上的 bean 相互持有对方。好比说 beanA 引用 beanB , beanB 引用 beanCbeanC 引用 beanA , 它们之间的引用关系构成一个环。缓存

Spring 如何解决循环依赖

Spring 中的循环依赖包括并发

  • 构造器循环依赖
  • setter 循环依赖

构造器的依赖

Spring 对于构造器的依赖、没法解决。只会抛出 BeanCurrentlyInCreationException 异常。编辑器

protected void beforeSingletonCreation(String beanName) {
 if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {  throw new BeanCurrentlyInCreationException(beanName);  }  } 复制代码

setter 的循环依赖

不论是 autowireByName 仍是 autowireByType 都是属于这种。Spring 默认是可以解决这种循环依赖的,主要是经过 Spring 容器提早暴露刚完成构造器注入但未完成其余步骤的 bean 来完成的。并且只能解决 singleton 类型的循环依赖、对于 prototype 类型的是不支持的,由于 Spring 没有缓存这种类型的 bean函数

Spring 是如何解决的

其实很简单、在 Spring 获取单例流程(一) 中咱们曾说起过三级缓存this

@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {   Object singletonObject = this.singletonObjects.get(beanName);  // 这个bean 正处于 建立阶段  if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {  // 并发控制  synchronized (this.singletonObjects) {  // 单例缓存是否存在  singletonObject = this.earlySingletonObjects.get(beanName);  // 是否运行获取 bean factory 建立出的 bean  if (singletonObject == null && allowEarlyReference) {  // 获取缓存中的 ObjectFactory  ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);  if (singletonFactory != null) {  singletonObject = singletonFactory.getObject();  // 将对象缓存到 earlySingletonObject中  this.earlySingletonObjects.put(beanName, singletonObject);  // 从工厂缓冲中移除  this.singletonFactories.remove(beanName);  }  }  }  }  return singletonObject; } 复制代码

Spring 解决 setter 循环依赖的关键点就是在这里,主要是 singletonFactories 这个 Mapurl

咱们能够先梳理一下总体的流程spa

beanA --> beanB --> beanC -->beanA
复制代码

以上面为例子、咱们先假设它们是构造器的循环依赖prototype

  1. Spring 初始化完成以后、接收到一个 getBean 的调用请求、请求 beanA
  2. Spring 发现 三级缓存中都没有 beanA 的存在、因此开始建立 beanA 的流程
  3. beanA 放入到 singletonsCurrentlyInCreation 集合中去、表明着 beanA 正在建立中
  4. 兜兜转转,发现我要 new 一个 beanA 的对象、我要先得到一个 beanB 的对象、好、咱们就进行一个 getBean(beanB)
  5. Spring 发现 三级缓存中都没有 beanB 的存在、因此开始建立 beanB 的流程
  6. beanB 放入到 singletonsCurrentlyInCreation 集合中去、表明着 beanB 正在建立中
  7. 兜兜转转,发现我要 new 一个 beanB 的对象、我要先得到一个 beanC 的对象、好、咱们就进行一个 getBean(beanC)
  8. Spring 发现 三级缓存中都没有 beanC 的存在、因此开始建立 beanC 的流程
  9. beanC 放入到 singletonsCurrentlyInCreation 集合中去、表明着 beanC 正在建立中
  10. 兜兜转转,发现我要 new 一个 beanC 的对象、我要先得到一个 beanA 的对象、好、咱们就进行一个 getBean(beanA)
  11. Spring 发现 三级缓存中都没有 beanA 的存在、因此开始建立 beanA 的流程
  12. beanA 放入到 singletonsCurrentlyInCreation 集合中去、可是在这个时候、插入到集合中失败、直接抛出异常

而假如咱们是一个 setter 的循环依赖3d

  1. Spring 初始化完成以后、接收到一个 getBean 的调用请求、请求 beanA
  2. 先判断 三级缓存中有没有 beanA ,若是没有则往下进行
  3. beanA 放入到 singletonsCurrentlyInCreation 集合中去、表明着 beanA 正在建立中
  4. 兜兜转转,终于建立了一个 beanA , 可是这个时候的 beanA 是一个不完整的状态、由于不少属性没有被赋值、好比说 beanA 中的成员变量 beanB 如今仍是一个 null 的状态
  5. 而后判断是否须要将当前建立的不完整的 beanA 加入到 第三级缓存中,正常来讲都是会被加入到 第三级缓存中的
  6. 加入 第三级缓存之后、进行一个属性填充,这个时候发现须要填充一个 beanB 对象
  7. 而后如上面那样、先看看 三级缓存有没有 beanB ,若是没有则建立一个并不完整的 beanB、而后加入到 第三级缓存中、而后发现须要填充一个 beanC 的属性
  8. 而后如上面那样、先看看 三级缓存有没有 beanC ,若是没有则建立一个并不完整的 beanC、而后加入到 第三级缓存中、而后发现须要填充一个 beanA 的属性
  9. 这个时候,先看看 三级缓存中有没有 beanA ,发如今 第三级缓冲中有不完整的 beanA、将其从 第三级缓存中移除出来、放入到 第二级缓存中,而后返回给 beanC 用于填充属性
  10. 而后 beanC 的 属性填充完毕,则将其从 singletonsCurrentlyInCreation 集合中移除掉,表明 beanC 已经真正的建立好了
  11. 而后将 beanC 加入到 第一级缓存中,并将其从 第三级缓存中移除,并返回给 beanBbeanB 也如 beanC 那样处理
  12. beanA 也如 beanBbeanC 那样处理、加入到 第一级缓存中、而后从 第二级缓存中移除
  13. 结束

其实上面的屁话又长又臭,可是流程仍是很是简单的

为啥是三级缓存,二级不行吗?

/**  * Cache of singleton objects: bean name to bean instance.  * 存放的是单例 bean、对应关系是 bean Name --> bean instance  */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /**  * Cache of early singleton objects: bean name to bean instance.  * 存放的早期的 bean、对应的关系 也是 beanName --> bean instance  * 与 singletonObjects 区别在于 earlySingletonObjects 中存放的bean 不必定是完整的、  * bean 在建立过程当中就加入到 earlySingletonObjects 中了、因此在bean建立过程当中就能够经过getBean 方法获取、  * 这个Map 也是解决循环依赖的关键所在  **/ private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);  /**  * Cache of singleton factories: bean name to ObjectFactory.  * 存放的是 ObjectFactory 、能够理解为建立单例bean的factory、对应关系是 bean name --> objectFactory  */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);  复制代码

咱们来看看从第三级缓存升级到第二级缓存究竟发生了什么

protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
 Object exposedObject = bean;  if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {  for (BeanPostProcessor bp : getBeanPostProcessors()) {  if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {  SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;  exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);  }  }  }  return exposedObject; } // 默认实现 default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {  return bean; } 复制代码

其实只要有二级缓存也是能够的,虽然能够达到解决 setter 循环依赖的问题、可是却没法给用户提供一个扩展接口(当存在循环依赖的)。

就比如说、上面的例子、在循环依赖的关系中,当 beanA第三级缓存升级到第二级缓存的时候,咱们能够在其升级的时候去设置一些 beanA 的属性或者作一些其余事情,咱们只须要在 beanA 的类中实现 SmartInstantiationAwareBeanPostProcessor 接口便可

可是单纯只有二级缓存的话,当咱们建立好一个没有完成初始化的 bean 的时候、要么就直接调用 ObjectFactorygetObject 方法获取通过回调的 bean 放入到第二级缓存(无论这个 bean 存不存在一个循环引用的关系链中),要么就直接放刚刚建立好的没有完成初始化的 bean 放入到第二级缓存。不管是哪一种状况,都没法达到这样一个需求:当存在循环依赖的时候,咱们做为用户须要对其进行一些设置或者一些其余的操做

为啥不能解决构造函数的循环依赖

若是按照解决 setter 循环依赖的流程、是否可以解决?先将一个不完整的 bean 放入到第三级缓存中,而后提供出去给其余 bean 依赖。可是呢,问题是我没法建立出这么一个不完整的 bean 在一个构造函数依赖的关系中,参数不全,再牛皮也不能把

此次必定?
此次必定?
群聊
群聊

本文使用 mdnice 排版

相关文章
相关标签/搜索