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通过第二级缓存,将实例化完成,还未初始化属性值的对象,提前暴露给其它对象,解决了循环依赖的问题。

不足

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

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

相关推荐
李崧正13 分钟前
Java技术分享:Lambda表达式与函数式编程
java·开发语言·python
老了,不知天命15 分钟前
鳶尾花項目JAVA
java·开发语言·机器学习
永远不会的CC21 分钟前
浙江华昱欣实习(4月23日~ 4月19日)
后端·学习
二哈赛车手21 分钟前
新人笔记---实现简易版的rag的bm25检索(利用ES),以及RAG上传时的ES与向量数据库双写
java·数据库·笔记·spring·elasticsearch·ai
winner888124 分钟前
从零吃透C++命名空间、std、#include、string、vector
java·开发语言·c++
AI人工智能+电脑小能手33 分钟前
【大白话说Java面试题】【Java基础篇】第26题:Java的抽象类和接口有哪些区别
java·开发语言·面试
蜜獾云40 分钟前
rocketmq traceId重复问题
spring·rocketmq·java-rocketmq
bzmK1DTbd42 分钟前
SOLID原则在Java中的实践:单一职责与开闭原则
java·开发语言·开闭原则
winner88811 小时前
C++ 命名空间、虚函数、抽象类、protected 权限全套通俗易懂精讲(附与 Java 对比)
java·开发语言·c++
直奔標竿1 小时前
Java开发者AI转型第二十五课!Spring AI 个人知识库实战(四)——RAG来源追溯落地,拒绝AI幻觉
java·开发语言·人工智能·spring boot·后端·spring