Spring IOC如何解决循环依赖的问题?

Spring IOC如何解决循环依赖的问题?

引言

Spring根据IOC控制反转的思想,将Bean(对象)的生命周期交由Spring IOC容器来管理,当你需要使用一个对象的时候,Spring IOC容器会自动帮你创建该对象的实例并初始化,大大降低的代码的耦合性。

我们知道,如果对象A引用了对象B,对象B又引用了对象A,那么在创建对象A的时候,就会去创建对象B,创建对象B的时候又会去创建对象A,...如此便产生了对象的循环依赖的问题。Spring IOC是如何解决的呢?

三级缓存

Spring IOC是通过三级缓存机制来解决循环依赖问题的,每级缓存都是一个Map:

  • 一级缓存:singletonObjects,key为BeanName,value为Bean,是放置完全体Bean的地方,所有的Bean最终都要放在一级缓存里,获取Bean也先从一级缓存开始获取
  • 二级缓存:earlySingletonObjects,key为BeanName,value为Bean,是放置已经实例化完成,但还没有填充属性值的Bean的地方
  • 三级缓存:singletonFactories,key为BeanName,value为对象工厂(ObjectFactory),Spring IOC在实例化一个Bean时,为这个Bean创建一个对象工厂,并将对象工厂放置在第三缓存中

解决循环依赖

  1. 当我们在程序中使用到了对象A,Spring IOC容器就会先创建对象A:
    • 在对对象A实例化的过程中,还会为对象A创建一个对象工厂,并将对象工厂放置在第三缓存中:
      • 对象工厂用于在进行代理等操作的时候,可以通过第三级缓存获取对象的包装对象。
    • 在实例化对象A的过程中,会发现引用了对象B:
      • Spring IOC容器并未在缓存中找到对象B,就会走对象B的创建流程:
        • Spring IOC容器在实例化对象B的时候,又发现了对象B引用了对象A,此时,Spring IOC在第二级缓存中找到了已经实例化但没有初始化的对象A,
        • 将第二级缓存中的对象A赋值给对象B,便可以继续完成对象B的实例化,并将实例化完的对象B放到第二级缓存中
    • 此时,Spring IOC容器就可以在第二级缓存中找到对象B,将其赋值给对象A,完成对象A的实例化
  2. 实例化完成的对象A,会被放到第二级缓存中,此时,对象A还没有初始化属性赋值
  3. Spring IOC容器将对象A的属性值初始化完成后,便会将对象A放到第一级缓存中,提供给程序使用。

可以看到,Spring IOC通过第二级缓存,将实例化完成,还未初始化属性值的对象,提前暴露给其它对象,解决了循环依赖的问题。

不足

由于放在缓存中的额对象都是同一个对象,每次获取的都是同一个对象,也就是单例模式,在这种单例模式下,才可以通过缓存来解决循环依赖的问题。

对于多例模式,每次获取的都是不同的对象,也就无法通过缓存的方式来解决循环依赖问题。

相关推荐
青山的青衫5 分钟前
【JavaWeb】Tlias后台管理系统
java·web
星释9 分钟前
Rust 练习册 :Minesweeper与二维数组处理
开发语言·后端·rust
蒟蒻的工具人11 分钟前
SSE实时推送订单状态
java·eventsource·sse协议
小蒜学长15 分钟前
springboot基于Java的校园导航微信小程序的设计与实现(代码+数据库+LW)
java·spring boot·后端·微信小程序
微学AI36 分钟前
基于openEuler操作系统的Docker部署与AI应用实践操作与研究
后端
王元_SmallA39 分钟前
IDEA + Spring Boot 的三种热加载方案
java·后端
LCG元40 分钟前
实战:用 Shell 脚本自动备份网站和数据库,并上传到云存储
后端
Yeats_Liao41 分钟前
时序数据库系列(四):InfluxQL查询语言详解
数据库·后端·sql·时序数据库
小苏兮43 分钟前
【把Linux“聊”明白】编译器gcc/g++与调试器gdb/cgdb:从编译原理到高效调试
java·linux·运维·学习·1024程序员节
Java天梯之路43 分钟前
04 数据类型转换
java