注:本系列源码剖析根据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/… ,限于作者个人水平,文中难免有过错之处,欢迎指正!原创不易,商业转载请联系作者取得授权,非商业转载请注明出处。
本系列的其他文章
【spring源码剖析】spring源码剖析系列目录