注:本系列源码剖析根据spring 5.2.2.RELEASE,本文的剖析仅针对于beanscopesingleton的状况,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的创立进程(留意:这儿咱们仅剖析beanscopesingleton的状况,也便是scope单例的状况):

spring探秘之循环依赖的解决(一):理论基石

这个进程中有几点需求阐明下:

  1. 创立方针:这个其实便是运用jdk供给的反射机制创立java方针,以第1节提到的Service1为例,可简略理解为Service1 service = new Service1()
  2. 注入依靠方针:还是以第1节提到的Service1为例,Service1中经过@Autowired主动注入Service2,这一步便是给Service2赋值的进程,可简略理解为service1.setService2(service2)
  3. singletonObjects:经过上面两步后,一个java方针就变成了一个spring bean,然后保存到singletonObjects了,这是个mapkey是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的创立进程如下:

spring探秘之循环依赖的解决(一):理论基石

对上图阐明如下:

  1. service1方针创立完结后,spring发现service1需求注入service2,然后就去singletonObjects中查找service2,此刻是找不到service2,然后就开端了service2的创立进程;
  2. service2方针创立完结后,spring发现service2需求注入service1,然后就去singletonObjects中查找service1,此刻是找不到service1,由于第一步中service1并没有创立成功 ,然后就开端了service1的创立进程;
  3. 流程跳回到1,再次开端了service1的创立、特点注入进程。

到这儿,咱们惊喜地发现,循环呈现了!

2.2 引进earlySingletonObjects处理循环依靠

咱们剖析下,循环呈现的原因在于,在service2获取service1时,由于singletonObjects中此刻并不存在service1,因而会再走service1的创立进程,重新创立service1,因而,咱们有个斗胆的想法:假如在service1实例化后就把它保存起来,后边再再找service1时,就回来这个未进行依靠注入的service1,像下面这样:

spring探秘之循环依赖的解决(一):理论基石

上图中,引进了earlySingletonObjects,这也是个map,同singletonObjects相同,key是bean的称号,value 是一个未完结依靠注入的方针。

对上图阐明如下:

  1. service1方针创立完结后,先将service1放入earlySingletonObjects,然后进行依靠注入;
  2. service1进行依靠注入时,spring发现service1需求注入service2,然后先去earlySingletonObjects查找service2,未找到;再去singletonObjects中查找service2,还是未找到,所以就开端了service2的创立进程;
  3. service2方针创立完结后,先将service2放入earlySingletonObjects,然后进行依靠注入;
  4. service2进行依靠注入时,spring发现service2需求注入service1,然后就去earlySingletonObjects查找service1,找到了,就将service1注入到service2中,此刻service2便是一个spring bean了,将其保存到singletonObjects中;
  5. 经过第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是怎么创立方针的:

spring探秘之循环依赖的解决(一):理论基石

比较于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)模仿方针添加到容器中的进程

模仿的流程如下:

  1. 创立obj2方针
  2. Obj2 中注入 obj1,但此刻并没有obj1,因而先要创立obj1,再将其注入到Obj2
  3. 生成Obj2的署理方针proxyObj2proxyObj2中持有 Obj2的原始方针
  4. 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); 
}

上述代码的流程如下:

  1. 创立obj2方针
  2. 生成Obj2的署理方针,署理方针中持有 Obj2的原始方针
  3. 往 Obj2 中注入 obj1,但此刻并没有obj1,因而先要创立obj1,再将其注入到Obj2
  4. proxyObj2现已走完了完好的生命周期,因而将署理方针添加到容器时

从代码上看,proxyObj2(署理方针)中持有ob2(原始方针),生成署理方针后,继续对原始方针进行特点注入,依然能影响署理方针,终究署理方针持有的原始方针也完结了依靠注入,整个进程用图形暗示如下:

spring探秘之循环依赖的解决(一):理论基石

这儿咱们再次申明,从java方针到spring bean的进程有好多,这儿咱们仅关注与循环依靠相关的进程,假如想了解spring bean具体的初始化进程,可查看 spring发动流程之发动流程概览。

到这儿,咱们探索到署理方针的生命周期能够有两种:

  • 创立–>特点注入–>生成署理方针–>将署理方针保存到容器中
  • 创立(原始方针)–>生成署理方针(提早进行aop)–>对原始方针进行特点注入–>将署理方针保存到容器中

这两种都能到达终究意图,即保存到容器中的是署理方针,且署理方针对应的原始方针完结了依靠注入。请牢记这两个创立流程,这是后边处理aop下循环依靠问题的中心,说白了,aop下的循环依靠问题之所以能处理,便是由于方针能够提早进行aop操作

2. 为什么用earlySingletonObjects无法处理循环依靠?

前面咱们首要阐明了署理方针的创立进程,接下来咱们来看看在aop下,运用earlySingletonObjects来处理循环依靠有什么问题:

spring探秘之循环依赖的解决(一):理论基石

咱们来剖析上图的流程:

  1. service1方针创立完结后,先将service1放入earlySingletonObjects,然后进行依靠注入;
  2. service1进行依靠注入时,spring发现service1需求注入service2,然后先去earlySingletonObjects查找service2,未找到;再去singletonObjects中查找service2,还是未找到,所以就开端了service2的创立进程;
  3. service2方针创立完结后,先将service2放入earlySingletonObjects,然后进行依靠注入;
  4. service2进行依靠注入时,spring发现service2需求注入service1,然后就去earlySingletonObjects查找service1,找到了,就将service1注入到service2中,然后再进行aop,此刻service2是一个署理方针,将其保存到singletonObjects中;
  5. 经过第4步后,咱们得到了service2的署理方针,然后将其注入到service1中,接着再对service1进行aop,此刻service1也成了一个spring bean,将其保存到singletonObjects中。

上述进程有什么问题呢?仔细看第4步,就会发现,注入到service2service1并不是署理方针!纵观大局,终究得到的service1service2都是署理方针,注入到service2service1应该也是署理方针才对。因而,在aop下,循环依靠的问题又呈现了!

2.3 spring 的处理方案

前面咱们提到,在aop下,引进earlySingletonObjects并不能处理循环依靠的问题,那spring是怎么处理的呢?spring再次引进了一个map来处理这个问题,这也是人们常说的spring三级缓存,对这三个map阐明如下:

  • 一级缓存singletonObjects:类型为ConcurrentHashMap<String, Object>,坐落DefaultSingletonBeanRegistry类中,keybeanNamevalue是完好的spring bean,即完结特点注入、初始化的bean,假如bean需求aop,存储的便是署理方针;
  • 二级缓存earlySingletonObjects:类型为HashMap<String, Object>,坐落DefaultSingletonBeanRegistry类中,keybeanNamevalue是实例化完结,但未进行依靠注入的bean,假如bean需求aop,这儿存储的便是署理方针,只不过署理方针所持有的原始方针并未进行依靠注入;
  • 三级缓存singletonFactories:类型为HashMap<String, ObjectFactory>,坐落DefaultSingletonBeanRegistry类中,keybeanNamevalue存储的是一个lambda表达式:() -> getEarlyBeanReference(beanName, mbd, bean)getEarlyBeanReference中的bean是刚创立完结的java bean,没有进行spring依靠注入,也没进行aop(关于这个lambda表达式,后边会继续剖析)。

为了阐明便利,下面对singletonObjectsearlySingletonObjectssingletonFactories别离称为一级缓存二级缓存三级缓存

spring处理aop下的循环依靠流程如下:

spring探秘之循环依赖的解决(一):理论基石

这个图看着比较复杂,其实分隔来看就比较简略了,上述操作中,1~8是获取service1的流程,5.1~5.8是获取service2的流程,5.5.1是再次获取service1的流程,只不过在处理service1的初始化进程中,会触发service2的初始化进程,而service2的初始化时,又会依靠到service1,因而才看着像是连在一起,比较复杂。

对上图的进程,这儿阐明如下(主张:假如觉得流程比较复杂,能够先看1~8的操作,再看5.1~5.8的操作,最终两者联合起来看,这样会清晰很多):

    1. service1:获取service1,从一级缓存中获取,此刻是获取不到的;
    1. service1:创立service1的实例;
    1. service1:获取需求注入的特点与办法(在原始方针上进行获取);
    1. service1:假如敞开了支撑循环依靠的装备,就将service1放到三级缓存中(是否支撑循环依靠,是能够装备的);
    1. 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也是署理方针;
    1. service1:回到service1的生命周期,拿到service2(这儿的service2是署理方针)后,将其注入到service1service1的依靠注入完结,进行初始化,这儿会判别service1是否需求进行aop,尽管service1是需求进行aop的,但由于在5.5.1现已进行过aop了,因而,这儿直接回来(到这一步,service1还是原始方针);
    1. service1:假如支撑循环依靠,先从一级缓存中获取service1,获取不到;再从二缓存中获取service1,能够获取到(从5.5.1可知,二级缓存里是service1署理方针),回来;
    1. service1:将二级缓存中获取的方针注册到一级缓存中,删去二、三级缓存,至此,service1初始化完结,注入的service2是署理方针,一级缓存中的service1也是署理方针。

以上流程,尽管进程较多,但service1service2的获取进程是相同的,只要弄清了其间之一的获取流程,另一个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类中,keybeanNamevalue是完好的spring bean,即完结特点注入、初始化的bean,假如bean需求aop,存储的便是署理方针
earlySingletonObjects 二级缓存,类型为HashMap<String, Object>,坐落DefaultSingletonBeanRegistry类中,keybeanNamevalue是实例化完结,但未进行依靠注入的bean假如bean需求aop,这儿存储的便是署理方针,只不过署理方针所持有的原始方针并未进行依靠注入
singletonFactories 三级缓存,类型为HashMap<String, ObjectFactory>,坐落DefaultSingletonBeanRegistry类中,keybeanNamevalue存储的是一个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;
    }
    // 省掉其他办法
    ...
}
  • 首要预备了两个类:Obj1Obj2, 其间Obj1有个特点为Obj2Obj2中有个特点为Obj1
  • 接着预备了Obj1Obj2的署理类ProxyObj1ProxyObj2,并且ProxyObj1ProxyObj2别离有一个特点:Obj1Obj2
  • 咱们仍旧以new ObjX()模仿方针的创立;
  • 咱们仍旧以objX.setObjX(xxx)模仿依靠注入;
  • 咱们仍旧以new ProxyObjX(xxx)模仿署理方针的生成;
  • 咱们仍旧以collection.add(xxx)模仿方针添加到容器中的进程;

咱们模仿终究得到的成果为:

  • 终究放入容器的方针别离是proxyObj1proxyObj2
  • 注入到obj1中的是proxyObj2,注入到obj2中的是proxyObj2

预备工作现已完结了,接下来咱们就开端进行模仿了。

3.1 模仿1

要求:

  • Obj1与Obj2有必要严格依照“创立–>特点注入–>生成署理方针–>保存到容器中”的流程创立
  • 两个方针的创立流程能够替换进行

方针:

  • 终究放入容器的方针别离是proxyObj1proxyObj2
  • 注入到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有必要以下两种流程之一创立:
    • “创立–>特点注入–>生成署理方针–>保存到容器中”的流程创立
    • “创立(原始方针)–>生成署理方针–>对原始方针进行特点注入–>将署理方针保存到容器中”的流程创立
  • 两个方针的创立流程能够替换进行

方针:

  • 终究放入容器的方针别离是proxyObj1proxyObj2
  • 注入到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之 所以能到达预期方针,首要是由于在注入obj2obj1特点时,提早生成了obj1的署理方针proxyObj1,使得obj2能完结整个创立流程。这儿再次证明,供给进行aop对循环依靠的处理起到至关重要的效果!

限于篇幅,本文就先到这儿了,本文首要剖析了循环依靠的发生,介绍了spring处理循环依靠的进程,最终经过两段代码模仿了循环依靠的处理,下一篇文章咱们将从spring源码剖析spring是怎么处理循环依靠的。


本文原文链接:my.oschina.net/funcy/blog/… ,限于作者个人水平,文中难免有过错之处,欢迎指正!原创不易,商业转载请联系作者取得授权,非商业转载请注明出处。

本系列的其他文章

【spring源码剖析】spring源码剖析系列目录