Spring面试
Spring相关面试
什么是循环依赖
循环依赖就是A依赖B,然后B又依赖A了,无法确定加载模块顺序
Spring如何解决循环依赖
Spring主要用三级缓存来解决循环依赖
一级缓存:用于存储完全初始化完成的单例Bean
二级缓存:用于存储尚未完全初始化,但已经实例化的Bean
三级缓存:用于存储对象工厂,比如AOP代理对象创建的时候就需要工厂了。
解决步骤就是:
步骤 1:开始创建 Bean A
- Spring 调用
A的构造方法,实例化 A 对象(此时 A 的属性还未注入) - 将 A 的 ObjectFactory放入三级缓存(singletonFactories)
singletonFactories.put("a", () -> getEarlyBeanReference("a", a));
步骤 2:为 A 填充属性(发现依赖 B)
- Spring 发现 A 需要注入
B - 于是转去创建
B
步骤 3:开始创建 Bean B
- 实例化 B 对象
- 将 B 的 ObjectFactory 放入三级缓存
- 为 B 填充属性 → 发现依赖
A
步骤 4:B 尝试获取 A(此时 A 还未创建完!)
- Spring 先查一级缓存 → 没有
- 查二级缓存 → 没有
- 查三级缓存 → 有!
- 于是调用ObjectFactory.getObject() 获取 A 的早期引用
- 如果 A 不需要 AOP 代理:直接返回原始 A 对象
- 如果 A 需要 AOP 代理:通过工厂创建代理对象(这就是三级缓存存在的核心原因!)
- 将这个早期 A(或代理)放入二级缓存,并从三级缓存中移除 A 的工厂
- 把这个早期 A 注入给 B
步骤 5:B 继续完成初始化
- B 的属性填充完成(拿到了 A)
- 执行 B 的初始化方法(
@PostConstruct、InitializingBean等) - B 完全初始化好后:
- 放入一级缓存
- 从二级、三级缓存中清除 B 的条目
步骤 6:回到 A 的创建流程
- A 拿到已初始化好的 B,完成自己的属性注入
- 执行 A 的初始化方法
- A 完全初始化好后:
- 放入一级缓存
- 清除 A 在二级/三级缓存中的残留
✅ 最终:A 和 B 都成功创建,且互相持有对方的引用。
Spring 不能解决的情况
| 场景 | 是否支持 | 原因 |
|---|---|---|
| 单例 + setter 注入 | ✅ 支持 | 三级缓存可解决 |
| 单例 + 构造器注入 | ❌ 不支持 | 实例化前就要依赖,无法提前暴露引用 |
| prototype 作用域循环依赖 | ❌ 不支持 | 每次都创建新实例,无法缓存 |
| 多例(request/session) | ❌ 不支持 | 生命周期不由 Spring 单例容器管理 |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 oyy0v0😼!
评论





