1.Spring怎么解决循环依赖
我们都知道,单例Bean初始化完成,要经历三步:
注入就发生在第二步,属性赋值,结合这个过程,Spring 通过三级缓存解决了循环依赖:
一级缓存 : Map<String,Object> singletonObjects,单例池,用于保存实例化、属性赋值(注入)、初始化完成的 bean 实例
二级缓存 : Map<String,Object> earlySingletonObjects,早期曝光对象,用于保存实例化完成的 bean 实例
三级缓存 : Map<String,ObjectFactory<?>> singletonFactories,早期曝光对象工厂,用于保存 bean 创建工厂,以便于后面扩展有机会创建代理对象。二级缓存中存储的就是从这个工厂中获取到的对象。
当 A、B 两个类发生循环依赖时:
创建A实例,实例化的时候把A对象⼯⼚放⼊三级缓存,表示A开始实例化了,虽然我这个对象还不完整,但是先曝光出来让大家知道。
A注⼊属性时,发现依赖B,此时B还没有被创建出来,所以去实例化B
同样,B注⼊属性时发现依赖A,它就会从缓存里找A对象。依次从⼀级到三级缓存查询A,从三级缓存通过对象⼯⼚拿到A,发现A虽然不太完善,但是存在,把A放⼊⼆级缓存,同时删除三级缓存中的A,此时,B已经实例化并且初始化完成,把B放入⼀级缓存。
接着A继续属性赋值,顺利从⼀级缓存拿到实例化且初始化完成的B对象,A对象创建也完成,删除⼆级缓存中的A,同时把A放⼊⼀级缓存
最后,⼀级缓存中保存着实例化、初始化都完成的A、B对象
所以,我们就知道为什么Spring能解决setter注入的循环依赖了,因为实例化和属性赋值是分开的,所以里面有操作的空间。如果都是构造器注入的化,那么都得在实例化这一步完成注入,所以自然是无法支持了。
2.为什么要二级缓存和三级缓存
只要两个缓存确实可以做到解决循环依赖的问题,但是有一个前提这个bean没被AOP进行切面代理,如果这个bean被AOP进行了切面代理,那么只使用两个缓存是无法解决问题,
如果加上AOP,两级缓存是无法解决的,不可能每次执行singleFactory.getObject()方法都给我产生一个新的代理对象,所以还要借助另外一个缓存来保存产生的代理对象。
如果出现循环依赖+aop时,多个地方注入这个动态代理对象需要保证都是同一个对象,而三级缓存中的取出来的动态代理对象每次都是新对象,地址值不一样。
三级缓存的作用是为了解决spring中Bean依赖注入时发生的循环依赖。如果不需要AOP,那么只需要二级缓存即可实现,如果有AOP,其实二级缓存也能够实现,但是会打破Bean的生命周期,不符合spring的原则,因为需要把AOP对象放入二级缓存中,那么就必须在所有需要AOP处理的Bean对象初始化之前就对Bean对象进行后置处理(生成AOP对象),即使没有发生循环依赖!这并不是spring想看到的,所以spring引入了三级缓存,而且存入的是结构,ObjectFactory是一个lambda表达式,相当于一个回调函数,当发生循环依赖的时候,会进行lambda表达式的执行,获取到Bean对象或者 AOP代理对象,再将Bean对象或者 AOP代理对象存入二级缓存中,如果之后还有循环依赖指向该对象(类似 A 依赖 B , B 依赖 A和C , C 依赖 A这种情况),就直接从二级缓存里面获取,从而解决了循环依赖。(这里解释了为什么不直接在二级缓存里存放lambda表达式,因为同一个lambda表达式每执行一次,就会生成一个新的代理对象,不能保证单例)
3.Spring有没有解决构造函数的循环依赖
主bean对象不能通过构造函数的方式注入所依赖的bean对象,而所依赖的bean对象则不受限制,即可以通过三种注入方式的任意一种注入主bean对象。
如果主bean对象通过构造函数方式注入所依赖的bean对象,则无论所依赖的bean对象通过何种方式注入主bean,都无法解决循环依赖问题,程序无法启动。
原因主要是主bean对象通过构造函数注入所依赖bean对象时,无法创建该所依赖的bean对象,获取该所依赖bean对象的引用。
创建主bean对象,调用顺序为:
1.调用构造函数,2. 放到三级缓存,3. 属性赋值,其中调用构造函数时会触发所依赖的bean对象的创建。
createBeanInstance是调用构造函数创建主bean对象,在里面会注入构造函数中所依赖的bean,而此时并没有执行到addSingletonFactory方法来添加主bean对象的创建工厂到三级缓存singletonFactories中。
故在createBeanInstance内部,注入和创建该主bean对象时,如果在构造函数中存在对其他bean对象的依赖,并且该bean对象也存在对主bean对象的依赖,则会出现循环依赖问题,
主bean对象为A,A对象依赖于B对象,B对象也存在对A对象的依赖,创建A对象时,会触发B对象的创建,则B无法通过三级缓存机制获取主bean对象A的引用(即B如果通过构造函数注入A,则无法创建B对象;如果通过属性注入或者setter方法注入A,则创建B对象后,对B对象进行属性赋值,会卡在populateBean方法也无法返回)。
故无法创建主bean对象所依赖的B,创建主bean对象A时,createBeanInstance方法无法返回,出现代码死锁,程序报循环依赖错误。
4.Spring有没有解决多例下的循环依赖
单例不会被提前初始化(非抽象、单例 并且非懒加载的类才能被提前初始bean)这种循环依赖问题是无法解决的,因为它没有用缓存,每次都会生成一个新对象。
多实例并不会调用getSingleton方法,自然也没有标记。也就是说,无论多少次创建多实例bean都不会调用beforeSingletonCreation,