SpringBean的循环依赖

springbean的循环依赖绕不过bean的生命周期(生命周期复杂),之所以复杂,是因为spring中有一系列不同于平常java对象而言的一个复杂的生命周期

spring中是有三个Map来解决循环依赖

spring中是怎么解决循环依赖的?

对于spring整个Framework体系而言的话,spring的bean是由一个BeanDefinition(spring当中的一个建模类)来的。
首先,spring容器启动,启动完之后会做一个扫描,扫描完后会变成一个BeanDefinition存到一个BeanDefinition Map当中,然后会再去对这个BeanDefinitionMap做一个遍历,遍历完做一些验证(比如,验证是否单例,是否原型,是否懒加载,是否有DepensOn,是否抽象,是否Factorybean,是否这个Bean的名字符合等等),验证完之后会spring容器会继续执行,然后会获取一遍当前实例化这个类有没有存在单例池当中,有没有被提前暴露(存到一个二级缓存中 一个map当中,这个map一般称之为二级缓存),如果没有被提前暴露的话,那么spring就会创建这个Bean;

创建过程:首先会通过一个叫做推断构造方法这么一个过程,把我们的当前这个spring所代表的的类当中的构造方法,得到一个最佳的构造方法,因为我们的spring的一个bean代表一个java类,java类中可能有很多个构造方法,spring实例化和这个bean的时候会去推断,不同的一个模型会有不同的构造方法,推断完成之后会通过反射去实例化一个java对象,实例化好Java对象之后会根据这个java对象 接着在对这个bean做一个初始化工作,比如说是否要去对这个bean做一些BeanDefinition的合并,比如说spring容器是否支持循环依赖,如果支持的话,他会提前暴露一个当前java对象(也就是半成品所对应的一个ObjectFactory,一个工厂类)就是把这个工厂类暴露,接着会往下执行,暴露完成之后的话回去做属性填充就是自动注入(比如说,a 依赖了b , 他就会把b set进去),填充完之后继续执行,执行各种Aware接口的一个会回调,比如说springFramework框架当中有一些ApplicationContextAware,BeanNameAware,ClassloadAware,然后会做生命周期初始化的回调,比如说我们加了@PostConstrout的,如果说有AOP的话,会生成代理,没有就会不会生成,那么他会去做一些比如事件的发布, 回调完成之后会把我们的这个bean差不多就走完了 ,接下来会放到单例池当中。生命周期OK

再说怎么解决循环依赖。

在spring实例化a的时候首先会做一些基本的验证,验证完会看一下我们的a有没有提前暴露(a对应的那个ObjectFactory),这个时候是肯定没有的,接着往下执行,推断出来一个最佳的构造方法,通过这个构造方法把a实例化出来,实例化出来之后就会走到是否提前暴露,当前容器,默认容器的话都需要提前暴露的 , a就会被提前暴露,这个时候暴露的并不是一个的单纯的a 而是一个由a生成创建的一个ObjectFactory对象,一个工厂对象,暴露完之后的话会继续往下执行,就会去做a的属性填充,然后就会填充b,此时就会发现b并没有在spring容器中,所以就会去做b的一个生命周期,b就会同样的做这么一套流程(从BeanDefinition Map当中取出b,拿出来之后会对b进行一个验证,验证完之后去判断b有没有提前暴露,也是没有提前暴露的),b就会继续往下执行,推断出来一个b的最佳构造方法,把b实例化,实例化之后的话会继续往下执行,把b提前暴露,暴露之后做b的属性填充,b里面填充了a,此时就会发现a也并不存在我们的单例池当中,(a也没有被实例化完,因为这个流程是a走到一半的时候走到了a的这里,所以a并没有走完,所以a不在单例池中;那么就又会去走a的声明周期流程,a就会又重复一遍,(拿出a 做验证,验证完判断有没有暴露,这个时候a已经被提前暴露了 ,所以它能拿到a对应的ObjectFactory))。

为什么不直接缓存一个a呢?而是缓存一个a所对应的一个ObjectFactory? (太复杂了)
简单解答:如果我们直接缓存一个a,那么拿出来的就是一个a,我们很难做到扩展,很难做出改变,但是如果我们暴露的一个ObjectFactory,springframework内部他通过了这个beanPostProcessor这个接口能够对这个ObjectFactory在产生这个a的过程当中,程序员是可以自己扩展可以去干预的 ,可以得到我们自己想要的一个a对象,这就是为什么不直接暴露一个a而是暴露一个a对应的ObjectFactory

概括:

如何理解spring的循环依赖?

首先在springframework整个体系当中,我们的一个bean它是有一个BeanDefinition来构建的(可以理解为springBean的一个建模)

要理解循环依赖的话 要说到springBean的生命周期,springbean的生命周期大体分为这么几步

​ 首先,spring容器启动,把类变成我们的BeanDefinition存到我们的BeanDefinition Map中进行遍历,遍历之后,对我们的spring这个BeanDefinition做一些基本的验证,是否单例,是否抽象,是否懒加载等等,验证完之后spring就会去从spring单例池当中获取一遍看他存不存在,有咩有被创建,如果没有被创建,再去看一下他有么有存在二级缓存当中,就是有没有被提前暴露,如果都么有,就会继续往下执行创建a对象,创建a对象之后会做一些初始化工作(属性填充 (填充属性就会发现a依赖了b,那么就会走b的生命周期)和a一样,首先去做验证 判断b有没有在单例池当中,如果没有的话在判断b有没有被提前暴露,这个时候b是没有被提前暴露的,那么b也会继续往下执行,就会把b实例化,实例化好做初始化,填充,填充的时候发现要填充a,这个时候发现a并没有被完整的实例化好,所以不能填充,所以它要再走一遍a的流程,走的时候会发现a已经提前暴露了,所以它能够拿到我们一个已经被提前暴露的ObjectFactory所产生的一个a对象)这样就完成了循环依赖

这个过程中会发现一个问题,就是我们的spring的循环依赖只支持单例,为什么只支持单例?

如果不支持单例的话第一遍

我们的a就不会走生命周期流程,单例会在spring容器初始化的时候就会走我们的声明周期流程,如果是原型的话他一开始是不会走的只有在用到时候才会去走。默认情况下只支持单例。

是不是只支持非构造方法的注入?

如果说 a b 是通过一个特殊的构造方法来循环依赖的 a的构造方法传入b b的构造方法传入a,这是肯定不行的 (第一遍:在实例化a的时候需要b 去拿b 创建b的时候又需要a)构造方法不能创建对象

spring进行扫描-反射后封装成beanDefinition对象-放入beanDefinitionMap-遍历map-验证(是否单例、是否延迟加载、是否抽象)-推断构造方法-准备开始进行实例-去单例池中查,没有-去二级缓存中找,没有提前暴露-生成一个objectFactory对象暴露到二级缓存中-属性注入,发现依赖Y-此时Y开始它的生命周期直到属性注入,发现依赖X-X又走一遍生命周期,当走到去二级缓存中找的时候找到了-往Y中注入X的objectFactory对象-完成循环依赖。
1、为什么要使用X的objectFacory对象而不是直接使用X对象?
利于拓展,程序员可以通过beanPostProcess接口操作objectFactory对象生成自己想要的对象
2、是不是只能支持单例(scope=singleton)而不支持原型(scope=prototype)?
是。因为单例是spring在启动时进行bean加载放入单例池中,在依赖的bean开始生命周期后,可以直接从二级缓存中取到它所依赖的bean的objectFactory对象从而结束循环依赖。而原型只有在用到时才会走生命周期流程,但是原型不存在一个已经实例化好的bean,所以会无限的创建-依赖-创建-依赖-...。
3、循环依赖是不是只支持非构造方法?
是。类似死锁问