2025年Java面试宝典点击获取
(提取码:9b3g)

Spring是如何解决Bean的循环依赖
大家在面试中是不是经常被问到Spring循环依赖的问题?比如"Bean A依赖Bean B,Bean B又依赖Bean A,Spring怎么处理?"这种问题听起来简单,但想要答到面试官心坎里,得说清楚三级缓存和提前暴露对象这两个关键点。
什么是Bean的循环依赖
举个例子,开发时定义了两个Service:
@Service
public class UserService {
@Autowired
private OrderService orderService;
}
@Service
public class OrderService {
@Autowired
private UserService userService;
}
Spring在启动时会发现:创建UserService需要先有OrderService,而OrderService又需要UserService。这种"鸡生蛋,蛋生鸡"的问题就是典型的循环依赖。

三级缓存机制揭秘
Spring通过三级缓存破解这个死循环:
- 一级缓存(成品池):存放完全初始化好的Bean
- 二级缓存(半成品池):存放已实例化但未初始化的Bean
- 三级缓存(工厂池):存放生成Bean的工厂对象
具体流程是这样的:
- 创建UserService实例(此时还是空对象)
- 把UserService的工厂对象存入三级缓存
- 填充UserService属性时发现需要OrderService
- 同样的流程创建OrderService实例
- 填充OrderService时通过三级缓存拿到UserService的早期引用
- 完成OrderService初始化后放回一级缓存
- 最后补全UserService的属性
这个过程中,二级缓存主要解决代理对象的循环依赖问题。比如AOP代理对象如果提前暴露原始对象,会导致后续注入的类型不一致,这时候就需要二级缓存来暂存代理对象。
哪些场景无法解决
虽然三级缓存很强大,但有两种情况会失效:
- 构造器注入循环依赖:Bean必须通过其他Bean的构造参数创建
- Prototype作用域的Bean:每次请求都创建新实例,无法缓存
比如下面这种写法就会报错:
@Service
public class ServiceA {
private final ServiceB serviceB;
public ServiceA(ServiceB serviceB) {
this.serviceB = serviceB;
}
}

面试加分技巧
- 能画图说明三级缓存的交互流程
- 区分Bean实例化与初始化的阶段
- 解释为什么构造器注入无法解决
- 提到Spring5中二级缓存的变化(某些版本合并了二三级缓存)
如果需要系统准备面试题,可以到面试鸭返利网获取最新题库。通过本站购买面试鸭会员可返利25元,助你高效备战金三银四!
最后提醒大家,合理设计代码结构才是避免循环依赖的根本。Spring的解决方案更像是一剂"后悔药",开发者还是要从模块划分入手,减少强耦合的依赖关系。


