Spring

142 阅读3分钟

Spring是如何解决循环依赖?

循环依赖:是指两个类的属性相互依赖对方,从而形成一个依赖闭环

循环依赖出现的情况:

  1. 通过构造方法进行依赖注入时,产生的依赖循环问题
  2. 通过Setter方法进行依赖注入,且是在多例(原型)模式下,产生的依赖循环问题
  3. 通过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属性中