循环依赖
2020-03-09 14:13:46 0 举报
由浅入深讲解spring循环依赖的处理机制
作者其他创作
大纲/内容
spring是如何解决循环依赖的
2 构造器循环依赖
(6)完成属性注入,以及一些其他的bean处理操作(如:后处理器,注册销毁实例方法destory-method等)后,说明bean实例化操作基本结束了,此时将之前添加到创建中的标志位清楚
singletonObjects:专门用来存储已完全加载好(完成属性注入等操作)的bean实例;singletonFactories:存储刚创建好的实例(尚未进行属性注入,初始化等后续操作),用来提前暴露对象,解决循环依赖;earlySingletonObjects:缓存被提前引用的bean实例。 这里要提一下这个缓存的作用,跟进解决循环依赖的逻辑,其实我们发现,通过一个singletonFactories便能解决,为何又要多出一个 earlySingletonObjects,要回答这个问题,其实我们要回到缓存存储的对象本身来思考,我们发现singletonFactories中存储的是一个ObjectFactory函数式接口的实现对象,如下图,也就是当我们从singletonFactories中获取bean实例的时候,其实还调用了getEarlyBeanReference进行了一次处理,进入到getEarlyBeanReference方法中发现,该方法其实是一个获取提前暴露引用的后处理器。我们知道,每一个单例bean创建的是否,都会先存储到singletonFactories中,但不是所有的单例bean都会被提前引用,只有当出现提前引用,才会去执行getEarlyBeanReference,并最终将经过后处理器处理过的对象保存到earlySingletonObjects中,后面再出现该对象的提前引用,则直接从earlySingletonObjects中获取。而singletonFactories中的getEarlyBeanReference只会执行一次,目的就是在确定该对象被提前引用的时候,调一下后处理器处理。registeredSingletons:保存已经完成加载的实例bean的名称;singletonsCurrentlyInCreation:保存正在创建中的单例bean的名称。非单例的bean加载也对应一个prototypeCurrentlyInCreation,只不过这个当出现prototype类型的bean循环依赖时,通过判断prototypeCurrentlyInCreation直接抛出异常。在下文中会讲解。
(7)将实例添加到单例缓存对象singletonObjects中,并清楚提前暴露的用于解决循环依赖的缓存
(3)开始创建实例:调用通过构造器或工厂bean(spring提供的一种创建实例的方式)创建实例对象;参考org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean方法
一.单例模式
循环依赖的解决过程分析: 从上面的加载过程可以看出,从第(5)步进行属性注入时,将可能出现setter循环依赖,举例说明(为了描述简单一点,就只使用两个bean的相互依赖):当加载CircleA实例到第(5)步进行属性注入时,发现需要注入CircleB,于是开始CircleB的实例加载,然后继续执行到CircleB的属性注入时,发现又需要注入CircleA的实例,于是又开始CircleA的实例加载,但这次加载执行到第(1)步时,你会发现已经能从singletonFactories中获取到之前暴露的对象了,于是顺利得完成了CircleB的属性注入->继而完成CircleB的实例加载->CircleA的属性注入->CircleA的实例加载。
先假设一下,如果是我们自己来管理对象,那像上面这种通过属性设置的循环引用我们可以像如下这样编码解决,但如果三个类都是通过构造器参数注入,则无法进行编码了。
CircleB
+ circleC:CircleC
+ setCircleC(CircleC circlec)
(5)进行属性注入以及初始化操作(如:init-method),此时如果发现需要注入bean,则开始对依赖bean的实例注入,从第(1)步开始。
表示通过构造器进行参数注入,此依赖是无法解决的,因为我们不能获取到一个尚未创建的实例引用(指针),spring只能抛出BeanCurrentlyInCreationException异常,具体处理逻辑如下:和setter循环依赖的一样,CircleA通过构造函数创建实例,需要CircleB,于是我们尝试CircleB的构造时,又需要引用CircleA,于是又开始CircleA的实例加载,当执行到第(1)步从缓存中获取实例,发现依旧获取不到(因为CircleA一开始尚未创建好实例,因此无法提前暴露到缓存中),于是Circle继续执行到第(2)步,尝试将CircleA添加到singletonsCurrentlyInCreation中,发现之前依旧添加过,所有返回添加失败,于是抛出BeanCurrentlyInCreationException异常。
二.非单例模式(如prototype,request,session等)
什么是循环依赖
单例模式源码解析:从上面的加载过程,spring实现单例模式,其实就是将加载好的实例缓存起来,这个缓存对象包含在DefaultSingletonBeanRegistry对象中,DefaultSingletonBeanRegistry是SingletonBeanRegistry的默认实现方式,SingletonBeanRegistry是spring容器中的顶级接口,专门用来实现单例模式的对象管理。阅读源码可知,DefaultSingletonBeanRegistry主要由以下几个属性值得关注。
(4)判断是否是单例,且允许循环依赖,如果是,则将创建号的实例对象封装成ObjectFactory添加到singletonFactories对象中进行缓存;
spring也面临同样的问题,我们先来把问题分解一下:
总结扩展:要解决循环依赖,假定思路也是提前暴露对象,你会发现在单线程环境下,貌似也可以实现,但是在复杂的多线程环境,实现起来就更加复杂了,也许也可以将提前暴露的对象缓存到ThreaLocal中,但实现逻辑也相对较复杂。其实我相信凭spring的能力也能解决,但反过来思考,spring本身主要就是用来管理单例,多例的应用场景虽然不少,但相对也不多,而循环依赖的场景的出现本身是否就应该考虑代码设计本身缺陷,我们本就应该在编码时尽量避免出现循环依赖,因此我觉得spring也没有必要花大功夫解决一个连鸡肋都算不上的应用场景。
(1)先从单例缓存中查询是否已经有相应实例,如果有,则返回,没有继续处理;
(2)设置状态,表示该bean正在创建中
CircleC
+ circleA:CircleA
+ setCircleA(CircleA circleA)
CircleA
+ circleB:CircleB
+ setCircleB(CircleB circleB)
1setter方法依赖
(8)对FactoryBean进行检查处理,以及类型检查等最后工作
类似于我们自己管理对象的编码方式,我们可以先通过构造函数生成实例对象,然后再对引用进行设置。spring便是通过提前在容器暴露已经创建好的实例对象(此时的实例对象尚未进行属性填充),来解决循环依赖。先讲一下spring中单例bean加载过程:
0 条评论
回复 删除
下一页