spring探秘之循环依赖的解决(一):理论基石
注:本系列源码分析基于spring 5.2.2.RELEASE,本文的分析仅针对于
bean
的scope
为singleton
的情况,gitee仓库链接:gitee.com/funcy/sprin….
1. 什么是循环依赖?
spring 在依赖注入时,可能会出现相互注入的情况:
@Service
public class Service1 {
@Autowired
private Service2 service2;
}
@Service
public class Service2 {
@Autowired
private Service1 service1;
}
如以上代码,在Service1
中通过@Autowird
注入了Service2
,在Service2
中通过@Autowird
注入了Service1
,这种相互注入的情况,就叫做循环依赖。
2. 循环依赖会有什么问题
实际上,这种A持有B对象,B也持有A对象
的情况,java代码是完全支持的:
/**
* 准备service1
*/
public class Service1 {
private Service2 service2;
public void setService2(Service2 service2) {
this.service2 = service2;
}
public Service2 getService2() {
return this.service2;
}
}
/**
* 准备service2
*/
public class Service2 {
private Service1 service1;
public void setService1(Service1 service1) {
this.service1 = service1;
}
public Service1 getService1() {
return this.service1;
}
}
/**
* 主方法中调用
*/
public class Main {
public void main(String[] args) {
// 准备两个对象
Service1 service1 = new Service1();
Service2 service2 = new Service2();
// 相互设置
service1.setService2(service2);
service2.setService1(service1);
}
}
那么,在spring中,两个类相互注入对方实例的情况,会有什么问题呢?我们来看spring bean
的创建过程(注意:这里我们仅分析bean
的scope
为singleton
的情况,也就是scope
为单例
的情况):
这个过程中有几点需要说明下:
- 创建对象:这个其实就是使用jdk提供的反射机制创建java对象,以第1节提到的
Service1
为例,可简单理解为Service1 service = new Service1()
; - 注入依赖对象:还是以第1节提到的
Service1
为例,Service1
中通过@Autowired
自动注入Service2
,这一步就是给Service2
赋值的过程,可简单理解为service1.setService2(service2)
; singletonObjects
:经过上面两步后,一个java对象就变成了一个spring bean,然后保存到singletonObjects
了,这是个map
,key
是bean的名称,value
是bean,它只保存spring bean
,不会只在java实例。
实际上,java
对象变成spring bean
,不仅仅只是依赖注入,还有初始化、执行beanPorcessor
方法等,由于本文是分析spring bean
的循环依赖的,因此我们重点关注与循环依赖相关的步骤。
2.1 循环依赖产生的问题
了解了spring bean的产生过程之后,接下来我们就来分析下循环依赖产生的问题,在正式分析前,我们先来明确两个概念:
java对象
:实际上,java中一切对象都可以称之为java
对象,为了说明方便,以下提到的java对象
仅指实例化完成、但未进行spring bean的生命周期对象;spring bean
:是一个java对象,并且进行了完整的spring bean的生命周期对象;
spring bean的创建过程如下:
对上图说明如下:
- 在
service1
对象创建完成后,spring
发现service1
需要注入service2
,然后就去singletonObjects
中查找service2
,此时是找不到service2
,然后就开始了service2
的创建过程; - 在
service2
对象创建完成后,spring
发现service2
需要注入service1
,然后就去singletonObjects
中查找service1
,此时是找不到service1
,因为第一步中service1
并没有创建成功 ,然后就开始了service1
的创建过程; - 流程跳回到
1
,再次开始了service1
的创建、属性注入过程。
到这里,我们惊喜地发现,循环出现了!
2.2 引入earlySingletonObjects
解决循环依赖
我们分析下,循环出现的原因在于,在service2
获取service1
时,由于singletonObjects
中此时并不存在service1
,因此会再走service1
的创建过程,重新创建service1
,因此,我们有个大胆的想法:如果在service1
实例化后就把它保存起来,后面再再找service1
时,就返回这个未进行依赖注入的service1
,像下面这样:
上图中,引入了earlySingletonObjects
,这也是个map,同singletonObjects
一样,key
是bean的名称,value
是一个未完成依赖注入的对象。
对上图说明如下:
- 在
service1
对象创建完成后,先将service1
放入earlySingletonObjects
,然后进行依赖注入; - 对
service1
进行依赖注入时,spring
发现service1
需要注入service2
,然后先去earlySingletonObjects
查找service2
,未找到;再去singletonObjects
中查找service2
,还是未找到,于是就开始了service2
的创建过程; - 在
service2
对象创建完成后,先将service2
放入earlySingletonObjects
,然后进行依赖注入; - 对
service2
进行依赖注入时,spring
发现service2
需要注入service1
,然后就去earlySingletonObjects
查找service1
,找到了,就将service1
注入到service2
中,此时service2
就是一个spring bean
了,将其保存到singletonObjects
中; - 经过第4步后,我们得到了
service2
,然后将其注入到service1
中,此时service1
也成了一个spring bean
,将其保存到singletonObjects
中。
经过以上步骤,我们发现,循环依赖得到了解决。
2.2 aop下的循环依赖
经过上面的分析,我们发现只要额外引入一个earlySingletonObjects
后,循环依赖就能得到解决。但是,循环依赖真的得到了解决吗?spring除了ioc外,还有另一个重大功能:aop,我们来看看aop情况下出现循环依赖会怎样。
1. aop对象的创建过程
在正式介绍aop下的循环依赖前,我们先来明确两个个概念:
原始对象
:区别于代理对象,指未进行过aop的对象,可以是java对象,也可以是未进行aop的spring bean;代理对象
:进行过aop的对象,可以是java对象仅进行过aop得到的对象(仅进行过aop,未进行依赖注入,也未进行初始化),也可以是进行过aop的spring bean
.
我们先来看看aop是如何创建对象的:
相比于2.1
中的流程,aop多了"生成代理对象"的操作,并且最终保存到singletonObjects
中的对象也是代理对象。
原始对象与代理对象之间是什么关系呢?用代码示意下,大致如下:
public class ProxyObj extends Obj {
// 原始对象
private Obj obj;
...
}
实际上,两者之间的关系并没有这么简单,但为了说明问题,这里对两者关系做了简化,小伙伴们只需要明白,代理对象持有原始对象的引用即可。
关于原始对象如何变成代理对象的,可以参考spring aop 之 AnnotationAwareAspectJAutoProxyCreator 分析(下)。
对以上创建过程,用java代码模拟如下:
/**
* 准备一个类
*/
public class Obj1 {
}
/**
* 准备一个类,内部有一个属性 Obj1
*/
public class Obj2 {
private Obj1 obj1;
// 省略其他方法
...
}
/**
* 准备Obj2的代理类,内部持有obj2的对象
*/
public class ProxyObj2 extends Obj2 {
private Obj2 obj2;
public ProxyObj2(Obj2 obj2) {
this.obj2 = obj2;
}
// 省略其他方法
...
}
接着,就是模拟“创建-->属性注入-->生成代理对象-->保存到容器中”的 流程了:
public static main(String[] args) {
// 准备一个容器,这里保存的是完成上述生命周期的对象
// 1. 如果元素是原始对象,则该对象已经完成了属性注入
// 2. 如果元素是代理对象,则该对象持有的原有对象已经完成了属性注入
Collection<?> collection = new ArrayList();
// 开始 Obj2 的创建流程
// 1. 创建 Obj2 对象
Obj2 obj2 = new Obj2();
// 2. 往 Obj2 中注入 obj1,但此时并没有obj1,因此先要创建obj1,再将其注入到Obj2中
Obj1 obj1 = new Obj1();
obj2.setObj1(obj1);
// 3. 生成Obj2的代理对象,代理对象中持有 Obj2的原始对象
ProxyObj2 proxyObj2 = new ProxyObj2(obj2);
// 4. proxyObj2已经走完了完整的生命周期,因此将代理对象添加到容器时
collection.add(proxyObj2);
}
上述代码中,
- 以
new Obj2()
模拟对象的创建 - 以
obj2.setObj1(xxx)
模拟依赖注入 - 以
new ProxyObj2(xxx)
模拟代理对象的生成 - 以
collection.add(xxx)
模拟对象添加到容器中的过程
模拟的流程如下:
- 创建
obj2
对象 - 往
Obj2
中注入obj1
,但此时并没有obj1
,因此先要创建obj1
,再将其注入到Obj2
中 - 生成
Obj2
的代理对象proxyObj2
,proxyObj2
中持有Obj2
的原始对象 proxyObj2
已经走完了完整的生命周期,因此将代理对象添加到容器时
仔细分析上面的步骤,就会发现,上面的第2步与第3步完全调换顺序也没问题,代码模拟如下:
public static main(String[] args) {
// 准备一个容器,这里保存的是完成上述生命周期的对象
// 1. 如果元素是原始对象,则该对象已经完成了属性注入
// 2. 如果元素是代理对象,则该对象持有的原有对象已经完成了属性注入
Collection<?> collection = new ArrayList();
// 开始 Obj2 的创建流程
// 1. 创建 Obj2 对象
Obj2 obj2 = new Obj2();
// 2. 生成Obj2的代理对象,代理对象中持有 Obj2的原始对象
ProxyObj2 proxyObj2 = new ProxyObj2(obj2);
// 3. 往 obj2 中注入 obj1,但此时并没有obj1,因此先要创建obj1,再将其注入到Obj2中
Obj1 obj1 = new Obj1();
// 这里是注入到原始对象中
obj2.setObj1(obj1);
// 4. proxyObj2已经走完了完整的生命周期,因此将代理对象添加到容器时
collection.add(proxyObj2);
}
上述代码的流程如下:
- 创建obj2对象
- 生成Obj2的代理对象,代理对象中持有 Obj2的原始对象
- 往 Obj2 中注入 obj1,但此时并没有obj1,因此先要创建obj1,再将其注入到Obj2
- proxyObj2已经走完了完整的生命周期,因此将代理对象添加到容器时
从代码上看,proxyObj2(代理对象)
中持有ob2(原始对象)
,生成代理对象后,继续对原始对象进行属性注入,依然能影响代理对象,最终代理对象持有的原始对象也完成了依赖注入,整个过程用图形示意如下:
这里我们再次申明,从java对象到spring bean的步骤有好多,这里我们仅关注与循环依赖相关的步骤,如果想了解spring bean详细的初始化过程,可查看 spring启动流程之启动流程概览。
到这里,我们探索到代理对象的生命周期可以有两种:
- 创建-->属性注入-->生成代理对象-->将代理对象保存到容器中
- 创建(原始对象)-->生成代理对象(提前进行aop)-->对原始对象进行属性注入-->将代理对象保存到容器中
这两种都能达到最终目的,即保存到容器中的是代理对象,且代理对象对应的原始对象完成了依赖注入。请牢记这两个创建流程,这是后面解决aop下循环依赖问题的核心,说白了,aop下的循环依赖问题之所以能解决,就是因为对象可以提前进行aop操作。
2. 为什么用earlySingletonObjects
无法解决循环依赖?
前面我们主要说明了代理对象的创建过程,接下来我们来看看在aop下,使用earlySingletonObjects
来解决循环依赖有什么问题:
我们来分析上图的流程:
- 在
service1
对象创建完成后,先将service1
放入earlySingletonObjects
,然后进行依赖注入; - 对
service1
进行依赖注入时,spring
发现service1
需要注入service2
,然后先去earlySingletonObjects
查找service2
,未找到;再去singletonObjects
中查找service2
,还是未找到,于是就开始了service2
的创建过程; - 在
service2
对象创建完成后,先将service2
放入earlySingletonObjects
,然后进行依赖注入; - 对
service2
进行依赖注入时,spring
发现service2
需要注入service1
,然后就去earlySingletonObjects
查找service1
,找到了,就将service1
注入到service2
中,然后再进行aop,此时service2
是一个代理对象,将其保存到singletonObjects
中; - 经过第4步后,我们得到了
service2
的代理对象,然后将其注入到service1
中,接着再对service1
进行aop,此时service1
也成了一个spring bean
,将其保存到singletonObjects
中。
上述步骤有什么问题呢?仔细看第4步,就会发现,注入到service2
的service1
并不是代理对象!纵观全局,最终得到的service1
与service2
都是代理对象,注入到service2
的service1
应该也是代理对象才对。因此,在aop下,循环依赖的问题又出现了!
2.3 spring 的解决方案
前面我们提到,在aop下,引入earlySingletonObjects
并不能解决循环依赖的问题,那spring是怎么解决的呢?spring再次引入了一个map
来解决这个问题,这也是人们常说的spring三级缓存,对这三个map
说明如下:
- 一级缓存
singletonObjects
:类型为ConcurrentHashMap<String, Object>
,位于DefaultSingletonBeanRegistry
类中,key
为beanName
,value
是完整的spring bean
,即完成属性注入、初始化的bean,如果bean需要aop,存储的就是代理对象; - 二级缓存
earlySingletonObjects
:类型为HashMap<String, Object>
,位于DefaultSingletonBeanRegistry
类中,key
为beanName
,value
是实例化完成,但未进行依赖注入的bean
,如果bean
需要aop
,这里存储的就是代理对象,只不过代理对象所持有的原始对象并未进行依赖注入; - 三级缓存
singletonFactories
:类型为HashMap<String, ObjectFactory>
,位于DefaultSingletonBeanRegistry
类中,key
为beanName
,value
存储的是一个lambda
表达式:() -> getEarlyBeanReference(beanName, mbd, bean)
,getEarlyBeanReference
中的bean
是刚创建完成的java bean
,没有进行spring依赖注入,也没进行aop(关于这个lambda
表达式,后面会继续分析)。
为了说明方便,下面对singletonObjects
、earlySingletonObjects
和singletonFactories
分别称为一级缓存、二级缓存和三级缓存。
spring解决aop下的循环依赖流程如下:
这个图看着比较复杂,其实分开来看就比较简单了,上述操作中,1~8
是获取service1
的流程,5.1~5.8
是获取service2
的流程,5.5.1
是再次获取service1
的流程,只不过在处理service1
的初始化过程中,会触发service2
的初始化过程,而service2
的初始化时,又会依赖到service1
,因此才看着像是连在一起,比较复杂。
对上图的过程,这里说明如下(建议:如果觉得流程比较复杂,可以先看1~8
的操作,再看5.1~5.8
的操作,最后两者联合起来看,这样会清晰很多):
-
service1
:获取service1
,从一级缓存中获取,此时是获取不到的;
-
service1
:创建service1
的实例;
-
service1
:获取需要注入的属性与方法(在原始对象上进行获取);
-
service1
:如果开启了支持循环依赖的配置,就将service1
放到三级缓存中(是否支持循环依赖,是可以配置的);
-
service1
:对service1
进行依赖注入,需要service2
,然后就开始了service2
的获取流程;
- 5.1
service2
:获取service2
,从一级缓存中获取,此时是获取不到的; - 5.2
service2
:创建service2
的实例; - 5.3
service2
:获取需要注入的属性与方法(在原始对象上进行获取); - 5.4
service2
:如果开启了支持循环依赖的配置,就将service2
放到三级缓存中(是否支持循环依赖,是可以配置的); - 5.5
service2
:对service2
进行依赖注入,需要service1
,然后就开始了service1
的获取流程; - 5.5.1
service1
: 获取service1
,从一级缓存中获取,获取不到;此时发现service1
正在创建中,于是继续从二、三级缓存中获取,最终从三级缓存中获取到了,将其放入二级缓存。从三级缓存获取的过程中,会判断service1
是否需要进行aop,然后开始aop操作,因此放入二级缓存中的是service1
代理代理,提前进行aop是解决循环依赖的关键; - 5.6
service2
:得到了service1
后(这里的service1
是代理对象),将其注入到service2
中,接着对service2
进行aop,得到service2
的代理对象; - 5.7
service2
:如果支持循环依赖,先从一、二级缓存中再次获取service2
,都未获取到,就使用当前service2
(当前service2
是代理对象); - 5.8
service2
:将service2的代理对象放入一级缓存中,删除二、三级缓存,至此,service2
初始化完成,注入的service1
是代理对象,一级缓存中的service2
也是代理对象; -
service1
:回到service1
的生命周期,拿到service2
(这里的service2
是代理对象)后,将其注入到service1
,service1
的依赖注入完成,进行初始化,这里会判断service1
是否需要进行aop,虽然service1
是需要进行aop的,但由于在5.5.1
已经进行过aop了,因此,这里直接返回(到这一步,service1
还是原始对象);
-
service1
:如果支持循环依赖,先从一级缓存中获取service1
,获取不到;再从二缓存中获取service1
,可以获取到(从5.5.1
可知,二级缓存里是service1
代理对象),返回;
-
service1
:将二级缓存中获取的对象注册到一级缓存中,删除二、三级缓存,至此,service1
初始化完成,注入的service2
是代理对象,一级缓存中的service1
也是代理对象。
以上流程,虽然步骤较多,但service1
与service2
的获取步骤是相同的,只要弄清了其中之一的获取流程,另一个bean的获取流程就很雷同了。
在上述流程中,还有两个数据结构需要说明下:
singletonsCurrentlyInCreation
:类型为SetFromMap<String>
,位于DefaultSingletonBeanRegistry
,创建方式为Collections.newSetFromMap(new ConcurrentHashMap<>(16))
,表明这是个由ConcurrentHashMap
实现的set,存储的是正在创建中的对象,判断当前对象是否在创建中就是通过查找当前对象是否在这个set中做到的;earlyProxyReferences
:类型为ConcurrentHashMap<Object, Object>
,位于AbstractAutoProxyCreator
,存储的是提前进行aop的对象,如果一个对象提前进行了aop,在后面再次aop时,会通过判断对象是否在earlyProxyReferences
中而确定要不要进行aop,以此来保证每个对象只进行一次aop。
至此,spring一共提供了5个数据结构来辅助解决循环依赖问题,总结如下:
结构 | 说明 |
---|---|
singletonObjects | 一级缓存,类型为ConcurrentHashMap<String, Object> ,位于DefaultSingletonBeanRegistry 类中,key 为beanName ,value 是完整的spring bean ,即完成属性注入、初始化的bean,如果bean需要aop,存储的就是代理对象 |
earlySingletonObjects | 二级缓存,类型为HashMap<String, Object> ,位于DefaultSingletonBeanRegistry 类中,key 为beanName ,value 是实例化完成,但未进行依赖注入的bean ,如果bean 需要aop ,这里存储的就是代理对象,只不过代理对象所持有的原始对象并未进行依赖注入 |
singletonFactories | 三级缓存,类型为HashMap<String, ObjectFactory> ,位于DefaultSingletonBeanRegistry 类中,key 为beanName ,value 存储的是一个lambda 表达式:() -> getEarlyBeanReference(beanName, mbd, bean) ,getEarlyBeanReference(xxx) 中的bean 是刚创建完成的java bean ,没有进行spring依赖注入,也没进行aop |
singletonsCurrentlyInCreation | 类型为SetFromMap<String> ,位于DefaultSingletonBeanRegistry ,创建方式为 Collections.newSetFromMap(new ConcurrentHashMap<>(16)) ,表明这是个由ConcurrentHashMap 实现的set,存储的是正在创建中的对象,可以用来判断当前对象是否在创建中 |
earlyProxyReferences | 类型为ConcurrentHashMap<Object, Object> ,位于AbstractAutoProxyCreator ,存储的是提前进行aop的对象,可以用来判断bean是否进行过aop,保证每个对象只进行一次aop |
以上就是spring 解决循环依赖的完整流程了。
3. 代码模拟
在正式分析源码前,我们首先模拟循环下依赖解决的过程,代码如下:
/**
* 准备一个类,内部有一个属性 Obj2
*/
public class Obj1 {
// 需要注入 obj2
private Obj2 obj2;
// 省略其他方法
...
}
/**
* 准备一个类,内部有一个属性 Obj1
*/
public class Obj2 {
// 需要注入 ob1
private Obj1 obj1;
// 省略其他方法
...
}
/**
* 准备Obj2的代理类,内部持有obj2的对象
*/
public class ProxyObj2 extends Obj2 {
// obj2代理类内部持有obj2的原始对象
private Obj2 obj2;
public ProxyObj2(Obj2 obj2) {
this.obj2 = obj2;
}
// 省略其他方法
...
}
/**
* 准备Obj1的代理类,内部持有obj1的对象
*/
public class ProxyObj1 extends Obj1 {
// obj2代理类内部持有obj1的原始对象
private Obj1 obj1;
public ProxyObj1(Obj1 obj1) {
this.obj1 = obj1;
}
// 省略其他方法
...
}
- 首先准备了两个类:
Obj1
与Obj2
, 其中Obj1
有个属性为Obj2
,Obj2
中有个属性为Obj1
; - 接着准备了
Obj1
与Obj2
的代理类ProxyObj1
、ProxyObj2
,并且ProxyObj1
、ProxyObj2
分别有一个属性:Obj1
、Obj2
; - 我们依旧以
new ObjX()
模拟对象的创建; - 我们依旧以
objX.setObjX(xxx)
模拟依赖注入; - 我们依旧以
new ProxyObjX(xxx)
模拟代理对象的生成; - 我们依旧以
collection.add(xxx)
模拟对象添加到容器中的过程;
我们模拟最终得到的结果为:
- 最终放入容器的对象分别是
proxyObj1
,proxyObj2
- 注入到
obj1
中的是proxyObj2
,注入到obj2
中的是proxyObj2
准备工作已经完成了,接下来我们就开始进行模拟了。
3.1 模拟1
要求:
- Obj1与Obj2必须严格按照“创建-->属性注入-->生成代理对象-->保存到容器中”的流程创建
- 两个对象的创建流程可以交替进行
目标:
- 最终放入容器的对象分别是
proxyObj1
,proxyObj2
- 注入到
obj1
中的是proxyObj2
,注入到obj2
中的是proxyObj2
代码如下:
public static main(String[] args) {
// 准备一个容器,这里保存的是完成上述生命周期的对象
// 1. 如果元素是原始对象,则该对象已经完成了属性注入
// 2. 如果元素是代理对象,则该对象持有的原有对象已经完成了属性注入
Collection<?> collection = new ArrayList();
// 1. 创建 Obj1 对象
Obj1 obj1 = new Obj1();
// 接下来需要将obj2的代理对象注入到obj1中,但此时容器中并没有obj2的代理对象,于是切换到obj2的创建流程
// 一. 创建 Obj2 对象
Obj2 obj2 = new Obj2();
// 到这里,obj2需要注入obj1的代理对象,但此时容器中并没有obj2的代理对象,于是又要切到obj1的创建流程
}
在执行以上流程中 ,发现创建 Obj2 对象后,流程就进行不下去了:
obj1
需要注入obj2
的代理对象,但找不到,于是切换到obj2
的创建流程;obj2
需要注入obj1
的代理对象,但找不到,于是切换到obj1
的创建流程;obj1
需要注入obj2
的代理对象,但找不到,于是切换到obj2
的创建流程;- ...
如此循环往复。
模拟结果:未达到预期目标,本次模拟宣告失败。
3.1 模拟2
要求:
- Obj1与Obj2必须以下两种流程之一创建:
- “创建-->属性注入-->生成代理对象-->保存到容器中”的流程创建
- “创建(原始对象)-->生成代理对象-->对原始对象进行属性注入-->将代理对象保存到容器中”的流程创建
- 两个对象的创建流程可以交替进行
目标:
- 最终放入容器的对象分别是
proxyObj1
,proxyObj2
- 注入到
obj1
中的是proxyObj2
,注入到obj2
中的是proxyObj2
示例代码如下:
public static main(String[] args) {
// 准备一个容器,这里保存的是完成上述生命周期的对象
// 1. 如果元素是原始对象,则该对象已经完成了属性注入
// 2. 如果元素是代理对象,则该对象持有的原有对象已经完成了属性注入
Collection<?> collection = new ArrayList();
// 1. 创建 Obj1 对象
Obj1 obj1 = new Obj1();
// 接下来需要将obj2的代理对象注入到obj1中,但此时容器中并没有obj2的代理对象,于是切换到obj2的创建流程
// 一. 创建 Obj2 对象
Obj2 obj2 = new Obj2();
// 2. 对 Obj1 提前代理
ProxyObj1 proxyObj1 = new ProxyObj1(obj1);
// 二. 将 proxyObj1 注入到 obj2 中
obj2.setObj1(proxyObj1);
// 三. 生成 obj2的代理对象
ProxyObj2 proxyObj2 = new ProxyObj2(obj2);
// 四. proxyObj2 已经走完了完整的生命周期,将代理对象添加到容器时
collection.add(proxyObj2);
// 此时容器中已经有 obj2 的代理对象了,继续obj1的生命周期
// 3. 将 proxyObj2 注入到 obj1 中
obj1.setObj2(proxyObj2);
// 4. proxyObj1 已经走完了完整的生命周期,将代理对象添加到容器时
collection.add(proxyObj1);
}
上面的代码中,obj1的流程用“1,2,3,4”标识,obj2的流程用“一,二,三,四”标识,两者流程如下:
- obj1:“创建(原始对象)-->生成代理对象-->对原始对象进行属性注入-->将代理对象保存到容器中”
- obj2:“创建-->属性注入-->生成代理对象-->保存到容器中”
最终两者都存入了容器中,达到了预期的目标。
3.3 从模拟中得到的结论
对比上面两个模拟代码,发现模拟2之 所以能达到预期目标,主要是因为在注入obj2
的obj1
属性时,提前生成了obj1
的代理对象proxyObj1
,使得obj2
能完成整个创建流程。这里再次证明,提供进行aop对循环依赖的解决起到至关重要的作用!
限于篇幅,本文就先到这里了,本文主要分析了循环依赖的产生,介绍了spring解决循环依赖的步骤,最后通过两段代码模拟了循环依赖的解决,下一篇文章我们将从spring源码分析spring是如何解决循环依赖的。
本文原文链接:my.oschina.net/funcy/blog/… ,限于作者个人水平,文中难免有错误之处,欢迎指正!原创不易,商业转载请联系作者获得授权,非商业转载请注明出处。
本系列的其他文章
转载自:https://juejin.cn/post/7160113160718909454