循环依赖主要是因为两个组件互相引用导致的,在Spring boot中提供了以下几种常用的解决方案。
1.通过Setter注入
setter注入方式相比构造器注入更加灵活,因为它允许在对象创建后再设置依赖。
当存在循环依赖时,Spring可以先实例化一个Bean,然后通过Setter注入的方式引入需要的Bean
2.使用@Lazy注解
@Lazy注解可以延迟Bean的创建,只有在被使用时才会被实例化。
3.使用代理对象
对于循环依赖问题,Springboot可以在一个对象需要一个引用时创建一个代理对象代替真实的Bean,当Bean实例化完成后再将这个Bean提供给需要的对象。
4.三级缓存机制
Springboot的三级缓存分别为:一级缓存(singletonObjects),二级缓存(earlysingletonObjects),三级缓存(singletonFactories);
一级缓存用来存放初始化完成后的Bean,称为单例池,二级缓存可以用来存放实例化完成,但尚未进行属性赋值和初始化方法尚未调用的对象。以便可以被其他Bean发现。三级缓存为实例化Bean的工厂,创建中的Bean会存放在其中。
解决循环依赖的流程:
当需要对象A时,Springboot首先会在缓存中寻找是否存在对象A,从一级缓存开始查找,然后是二级缓存三级缓存都没有,Springboot会调用singletonFactories实例化对象A,放在三级缓存中,在创建过程中发现需要对象B,同样会在缓存中查找,发现不存在,随后在三级缓存中实例化对象B,对象B的创建同样也依赖了对象A,此时再在缓存中查找时在三级缓存中发现了对象A,然后对象A就被复制到二级缓存提前将实例化后的对象暴露出去,然后将三级缓存中的对象A删除。此时对象B就可以使用对象A了,对象B继续执行创建过程并顺利初始化完成被缓存到一级缓存,然后对象A也可以初始化完成同样缓存在一级缓存。
所以三级缓存解决循环依赖的根本措施就是拆分Bean加载的过程,提前暴露早期实例化Bean,以便引用这个Bean的其他Bean能够顺利加载,常规方法创建对象出现循环依赖的原因就是实例化过程和初始化过程是连续的,无法被其他Bean发现。