Spring 使用 三级缓存 来解决循环依赖问题,原理如下:

1️⃣ 三级缓存 就是分成三步保存对象:

一级缓存:存储已经完全准备好的对象(就像已经做好的菜)。
二级缓存:存储正在做的一半完成的对象(半成品)。
三级缓存:存储用来制造半成品的工具或方法(可以随时做半成品)。
2️⃣ 解决方式:
当两个对象互相依赖时,Spring 会先把它们放到 三级缓存 中,这样它们就可以先用“半成品”凑合着工作,而不会一直等待对方的完成。

3️⃣ 最后完成:
等到所有对象都准备好了,Spring 就会把它们放到 一级缓存 中,这样它们就可以完全使用了。

简单说,Spring 用“先用半成品”的方法巧妙打破了循环依赖。

📚知识内容
Spring 解决循环依赖的核心是 三级缓存机制。下面详细解析它是如何一步步完成的:

1️⃣ 什么是三级缓存?
在 Spring 中,Bean 的创建分为三个阶段:实例化、属性注入、初始化。

一级缓存(singletonObjects):存储完全初始化完成的 Bean。
二级缓存(earlySingletonObjects):存储已实例化但未完成属性注入的“半成品”对象。
三级缓存(singletonFactories):存储创建 Bean 的工厂,当需要时生成“半成品”。
三级缓存的目的是通过延迟暴露“半成品”对象,打破循环依赖。

2️⃣ 解决循环依赖的完整流程
假设有 A 依赖 B,B 又依赖 A 的场景:

🔍 创建过程分为以下几个关键步骤:
实例化 Bean A

Spring 首先实例化 A(通过构造函数创建对象)。
然后,将 A 的工厂(ObjectFactory)放入三级缓存(singletonFactories)。此时 A 是个“半成品”。
处理 A 的依赖:注入 B

A 需要注入 B,Spring 开始创建 B。
B 还没初始化,于是 B 的工厂也被放入三级缓存。
处理 B 的依赖:注入 A

B 需要注入 A。此时 Spring 先检查一级缓存(完整对象),没找到。
检查二级缓存(半成品),还是没找到。
最后到三级缓存,通过 A 的工厂生成一个“半成品 A”。
将生成的“半成品 A”存入二级缓存(earlySingletonObjects),并注入到 B 中。
B 初始化完成

完成 B 的属性注入和初始化,将 B 移入一级缓存。
A 初始化完成

A 获取到完整的 B 后,完成自身的属性注入和初始化,移入一级缓存。
整个流程逻辑图如下:

  1. 实例化 A,放入三级缓存(工厂)。
  2. A -> 依赖 B,开始创建 B。
  3. 实例化 B,放入三级缓存(工厂)。
  4. B -> 依赖 A,从三级缓存生成“半成品 A”。
  5. 完成 B 的初始化,放入一级缓存。
  6. A 注入完整 B,完成初始化,放入一级缓存。

3️⃣ 核心代码解析
以下是 Spring 内部处理缓存的简化逻辑:

// 获取单例 Bean 的方法
public Object getSingleton(String beanName) {
// 1. 从一级缓存获取完整的 Bean
Object singleton = singletonObjects.get(beanName);
if (singleton == null) {
// 2. 从二级缓存获取“半成品” Bean
singleton = earlySingletonObjects.get(beanName);
if (singleton == null) {
// 3. 从三级缓存获取工厂并生成 Bean
ObjectFactory<?> factory = singletonFactories.get(beanName);
if (factory != null) {
singleton = factory.getObject();
earlySingletonObjects.put(beanName, singleton);
singletonFactories.remove(beanName);
}
}
}
return singleton;
}

🌟 知识拓展
1️⃣ 为什么只支持单例循环依赖?
因为原型(Prototype)模式每次都创建新对象,不能复用“半成品”。而单例对象可以复用同一个“半成品”,这才是循环依赖能解决的关键!

2️⃣ 构造器注入为什么会失败?
构造器注入必须在对象完全实例化前解决所有依赖。如果 A 和 B 都用构造器注入,会互相等待,Spring 无法通过缓存处理这种死循环。

3️⃣ AOP 如何影响循环依赖?
AOP 会生成代理对象,代理对象必须在初始化阶段创建。所以 Spring 会在三级缓存中保存工厂,用于延迟生成代理对象,支持 AOP 的循环依赖。

🌟 总结
Spring 的三级缓存机制,通过提前暴露“半成品对象”,巧妙地解决了单例循环依赖的问题,同时支持 AOP 等复杂场景。简单、灵活又高效,值得细细品味! 🎉