小鱿鱼 2020-06-05
读完这篇文章你将会收获到
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 获取单例流程(一) 中我们曾提及过三级缓存
@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
中
我们可以先梳理一下整体的流程
beanA --> beanB --> beanC -->beanA
以上面为例子、我们先假设它们是构造器的循环依赖
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
的循环依赖
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
在一个构造函数依赖的关系中,参数不全,再牛皮也不能把