Spring是如何解决循环依赖?
循环依赖:是指两个类的属性相互依赖对方,从而形成一个依赖闭环
循环依赖出现的情况:
- 通过构造方法进行依赖注入时,产生的依赖循环问题
- 通过Setter方法进行依赖注入,且是在多例(原型)模式下,产生的依赖循环问题
- 通过Setter方法进行依赖注入,且是在单例模式下,产生的依赖循环问题
只有第三种方式的循环依赖被Spring解决了,其它两种在遇到循环依赖时,Spring都会产生异常
Spring解决单例模式下的Setter方法的循环依赖,主要的方式是通过三级缓存解决循环依赖
三级缓存:指的是Spring在创建Bean的过程中,通过三级缓存来缓存正在创建的Bean,以及已经创建完成的Bean实例
具体实现:
- 实例化Bean:Spring在实例化Bean时,会先创建一个空的Bean对象,并将其放入一级缓存中
- 属性赋值:Spring开始对Bean进行属性赋值,如果发现循环依赖,会将Bean对象提前暴露给后续需要依赖的Bean
- 通过提前暴露的方式,来解决依赖循环
- 初始化Bean:完成属性赋值后,Spring将Bean进行初始化,并将其放入二级缓存中
- 注入依赖:Spring继续对Bean进行依赖注入,如果发现循环依赖,会从二级缓存中获取已经完成初始化的Bean实例
通过三级缓存机制,Spring在处理循环依赖时,确保及时暴露正在创建的Bean对象,并能够正确地注入已经初始化的Bean实例,从而解决循环依赖问题,保证应用程序的正常运行
如何避免循环依赖?
- 避免双向依赖,改为单向依赖
- 使用
@Lazy延迟加载(将依赖标记为惰性初始化) - 通过事件驱动(
ApplicationEvent)解耦Bean
总结:
- 三级缓存:通过缓存不同状态的 Bean 解决循环依赖
- 提前暴露引用:在 Bean 未完成初始化前,提前将引用暴露给其他 Bean 使用
- 单例限制:仅支持单例 Bean,原型 Bean 和构造器注入的循环依赖无法解决
Spring三级缓存的数据结构是什么?
都是Map类型的缓存
一级缓存(Singleton Objects):存储的已经是完全初始化好的Bean,即完全准备好的可以使用的Bean实例
- 键是Bean的名称,值是Bean的实例
- 这个缓存在
DefaultSingletonBeanRegistry类中的singletonObjects属性中
二级缓存(Early Singleton Objects):存储的是早期的Bean引用,即已经实例化但还未完全初始化的Bean
- 这些bean已经被实例化,但是可能还没有进行属性注入等操作
- 这个缓存在
DefaultSingletonBeanRegistry类中的earlySingletonObjects属性中
三级缓存(Singleton Factories):存储的是ObjectFactory对象,这些对象可以生成早期的Bean引用
- 当一个bean正在创建过程中,如果它被其他bean依赖,那么这个正在创建的bean,就会通过这个ObjectFactory来创建一个早期引用,从而解决循环依赖的问题
- 这个缓存在
DefaultSingletonBeanRegistry类中的singletonFactories属性中