spring 在依赖注入时,可能会出现相互注入的情况:
@Service
public class Service1 {
@Autowired
private Service2 service2;
}
@Service
public class Service2 {
@Autowired
private Service1 service1;
}
如以上代码,在 Service1
中通过 @Autowird
注入了 Service2
,在 Service2
中通过 @Autowird
注入了 Service1
,这种相互注入的情况,就叫做循环依赖。
实际上,这种 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
的循环依赖的,因此我们重点关注与循环依赖相关的步骤。
了解了 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
的创建、属性注入过程。
到这里,我们惊喜地发现,循环出现了!
我们分析下,循环出现的原因在于,在 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
中。
经过以上步骤,我们发现,循环依赖得到了解决。
经过上面的分析,我们发现只要额外引入一个 earlySingletonObjects
后,循环依赖就能得到解决。但是,循环依赖真的得到了解决吗?spring 除了 ioc 外,还有另一个重大功能:aop,我们来看看 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 操作。
前面我们主要说明了代理对象的创建过程,接下来我们来看看在 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 下,循环依赖的问题又出现了!
前面我们提到,在 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 解决循环依赖的完整流程了。
在正式分析源码前,我们首先模拟循环下依赖解决的过程,代码如下:
/**
* 准备一个类,内部有一个属性 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
准备工作已经完成了,接下来我们就开始进行模拟了。
要求:
- 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
的创建流程;- ...
如此循环往复。
模拟结果:未达到预期目标,本次模拟宣告失败。
要求:
- Obj1 与 Obj2 必须以下两种流程之一创建:
- “创建 --> 属性注入 --> 生成代理对象 --> 保存到容器中” 的流程创建
- “创建 (原始对象)--> 生成代理对象 --> 对原始对象进行属性注入 --> 将代理对象保存到容器中” 的流程创建
- 两个对象的创建流程可以交替进行
目标:
- 最终放入容器的对象分别是
proxyObj1
,proxyObj2
- 注入到
obj1
中的是proxyObj2
,注入到obj2
中的是proxyObj2
### 1. 什么是循环依赖?
spring 在依赖注入时,可能会出现相互注入的情况:
@Service
public class Service1 {
@Autowired
private Service2 service2;
}
@Service
public class Service2 {
@Autowired
private Service1 service1;
}
如以上代码,在 Service1
中通过 @Autowird
注入了 Service2
,在 Service2
中通过 @Autowird
注入了 Service1
,这种相互注入的情况,就叫做循环依赖。
实际上,这种 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
的循环依赖的,因此我们重点关注与循环依赖相关的步骤。
了解了 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
的创建、属性注入过程。
到这里,我们惊喜地发现,循环出现了!
我们分析下,循环出现的原因在于,在 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
中。
经过以上步骤,我们发现,循环依赖得到了解决。
经过上面的分析,我们发现只要额外引入一个 earlySingletonObjects
后,循环依赖就能得到解决。但是,循环依赖真的得到了解决吗?spring 除了 ioc 外,还有另一个重大功能:aop,我们来看看 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 操作。
前面我们主要说明了代理对象的创建过程,接下来我们来看看在 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 下,循环依赖的问题又出现了!
前面我们提到,在 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 解决循环依赖的完整流程了。
在正式分析源码前,我们首先模拟循环下依赖解决的过程,代码如下:
/**
* 准备一个类,内部有一个属性 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
准备工作已经完成了,接下来我们就开始进行模拟了。
要求:
- 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
的创建流程;- ...
如此循环往复。
模拟结果:未达到预期目标,本次模拟宣告失败。
要求:
- 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:“创建 --> 属性注入 --> 生成代理对象 --> 保存到容器中”
最终两者都存入了容器中,达到了预期的目标。
对比上面两个模拟代码,发现模拟 2 之 所以能达到预期目标,主要是因为在注入 obj2
的 obj1
属性时,提前生成了 obj1
的代理对象 proxyObj1
,使得 obj2
能完成整个创建流程。这里再次证明,提供进行 aop 对循环依赖的解决起到至关重要的作用!
限于篇幅,本文就先到这里了,本文主要分析了循环依赖的产生,介绍了 spring 解决循环依赖的步骤,最后通过两段代码模拟了循环依赖的解决,下一篇文章我们将从 spring 源码分析 spring 是如何解决循环依赖的。
本文原文链接:https://my.oschina.net/funcy/blog/4659555 ,限于作者个人水平,文中难免有错误之处,欢迎指正!原创不易,商业转载请联系作者获得授权,非商业转载请注明出处。
示例代码如下:
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:“创建 --> 属性注入 --> 生成代理对象 --> 保存到容器中”
最终两者都存入了容器中,达到了预期的目标。
对比上面两个模拟代码,发现模拟 2 之 所以能达到预期目标,主要是因为在注入 obj2
的 obj1
属性时,提前生成了 obj1
的代理对象 proxyObj1
,使得 obj2
能完成整个创建流程。这里再次证明,提供进行 aop 对循环依赖的解决起到至关重要的作用!
限于篇幅,本文就先到这里了,本文主要分析了循环依赖的产生,介绍了 spring 解决循环依赖的步骤,最后通过两段代码模拟了循环依赖的解决,下一篇文章我们将从 spring 源码分析 spring 是如何解决循环依赖的。
本文原文链接:https://my.oschina.net/funcy/blog/4659555 ,限于作者个人水平,文中难免有错误之处,欢迎指正!原创不易,商业转载请联系作者获得授权,非商业转载请注明出处。