读完这篇文章你将会收获到web
Spring
循环依赖能够分为哪两种
Spring
如何解决
setter
循环依赖
Spring
为什么是三级缓存 , 二级不行 ?
Spring
为啥不能解决构造器循环依赖
循环依赖就是循环引用,两个或以上的 bean
相互持有对方。好比说 beanA
引用 beanB
, beanB
引用 beanC
, beanC
引用 beanA
, 它们之间的引用关系构成一个环。缓存
Spring
中的循环依赖包括并发
setter
循环依赖
Spring
对于构造器的依赖、没法解决。只会抛出 BeanCurrentlyInCreationException
异常。编辑器
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } } 复制代码
不论是 autowireByName
仍是 autowireByType
都是属于这种。Spring
默认是可以解决这种循环依赖的,主要是经过 Spring
容器提早暴露刚完成构造器注入但未完成其余步骤的 bean 来完成的。并且只能解决 singleton
类型的循环依赖、对于 prototype
类型的是不支持的,由于 Spring
没有缓存这种类型的 bean
函数
其实很简单、在 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
这个 Map
中url
咱们能够先梳理一下总体的流程spa
beanA --> beanB --> beanC -->beanA
复制代码
以上面为例子、咱们先假设它们是构造器的循环依赖prototype
Spring
初始化完成以后、接收到一个
getBean
的调用请求、请求
beanA
Spring
发现
三级缓存中都没有
beanA
的存在、因此开始建立
beanA
的流程
beanA
放入到
singletonsCurrentlyInCreation
集合中去、表明着
beanA
正在建立中
new
一个
beanA
的对象、我要先得到一个
beanB
的对象、好、咱们就进行一个
getBean(beanB)
Spring
发现
三级缓存中都没有
beanB
的存在、因此开始建立
beanB
的流程
beanB
放入到
singletonsCurrentlyInCreation
集合中去、表明着
beanB
正在建立中
new
一个
beanB
的对象、我要先得到一个
beanC
的对象、好、咱们就进行一个
getBean(beanC)
Spring
发现
三级缓存中都没有
beanC
的存在、因此开始建立
beanC
的流程
beanC
放入到
singletonsCurrentlyInCreation
集合中去、表明着
beanC
正在建立中
new
一个
beanC
的对象、我要先得到一个
beanA
的对象、好、咱们就进行一个
getBean(beanA)
Spring
发现
三级缓存中都没有
beanA
的存在、因此开始建立
beanA
的流程
beanA
放入到
singletonsCurrentlyInCreation
集合中去、可是在这个时候、插入到集合中失败、直接抛出异常
而假如咱们是一个 setter
的循环依赖3d
Spring
初始化完成以后、接收到一个
getBean
的调用请求、请求
beanA
beanA
,若是没有则往下进行
beanA
放入到
singletonsCurrentlyInCreation
集合中去、表明着
beanA
正在建立中
beanA
, 可是这个时候的
beanA
是一个不完整的状态、由于不少属性没有被赋值、好比说
beanA
中的成员变量
beanB
如今仍是一个
null
的状态
beanA
加入到
第三级缓存中,正常来讲都是会被加入到
第三级缓存中的
beanB
对象
beanB
,若是没有则建立一个并不完整的
beanB
、而后加入到
第三级缓存中、而后发现须要填充一个
beanC
的属性
beanC
,若是没有则建立一个并不完整的
beanC
、而后加入到
第三级缓存中、而后发现须要填充一个
beanA
的属性
beanA
,发如今
第三级缓冲中有不完整的
beanA
、将其从
第三级缓存中移除出来、放入到
第二级缓存中,而后返回给
beanC
用于填充属性
beanC
的 属性填充完毕,则将其从
singletonsCurrentlyInCreation
集合中移除掉,表明
beanC
已经真正的建立好了
beanC
加入到
第一级缓存中,并将其从
第三级缓存中移除,并返回给
beanB
,
beanB
也如
beanC
那样处理
beanA
也如
beanB
、
beanC
那样处理、加入到
第一级缓存中、而后从
第二级缓存中移除
其实上面的屁话又长又臭,可是流程仍是很是简单的
/** * 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
的时候、要么就直接调用 ObjectFactory
的 getObject
方法获取通过回调的 bean
放入到第二级缓存(无论这个 bean
存不存在一个循环引用的关系链中),要么就直接放刚刚建立好的没有完成初始化的 bean
放入到第二级缓存。不管是哪一种状况,都没法达到这样一个需求:当存在循环依赖的时候,咱们做为用户须要对其进行一些设置或者一些其余的操做
若是按照解决 setter
循环依赖的流程、是否可以解决?先将一个不完整的 bean
放入到第三级缓存中,而后提供出去给其余 bean
依赖。可是呢,问题是我没法建立出这么一个不完整的 bean
在一个构造函数依赖的关系中,参数不全,再牛皮也不能把
本文使用 mdnice 排版