文章目录
- [一、Spring 怎么解决循环依赖?(经典面试题🔥)](#一、Spring 怎么解决循环依赖?(经典面试题🔥))
-
- 场景
- [Spring 解决方案:三级缓存](#Spring 解决方案:三级缓存)
- 创建过程(关键理解)
-
- [第一步:创建 A(还没初始化)](#第一步:创建 A(还没初始化))
- [第二步:A 需要 B → 开始创建 B](#第二步:A 需要 B → 开始创建 B)
- [第三步:B 需要 A](#第三步:B 需要 A)
- [第四步:B 创建完成 → 回去继续 A](#第四步:B 创建完成 → 回去继续 A)
- 核心本质
- 为什么要三级缓存?
- [二、AOP 是怎么实现的?(核心机制🔥)](#二、AOP 是怎么实现的?(核心机制🔥))
- [三、@Transactional 为什么会失效?(高频坑🔥)](#三、@Transactional 为什么会失效?(高频坑🔥))
- [四、Spring 为什么要用代理?(设计哲学)](#四、Spring 为什么要用代理?(设计哲学))
- [五、循环依赖 + AOP 的关系(高级点)](#五、循环依赖 + AOP 的关系(高级点))
- 六、你现在的认知应该升级到:
- 七、一句高级总结(建议记)
- 八、如果你还想再进一层(更硬核)
一、Spring 怎么解决循环依赖?(经典面试题🔥)
场景
java
@Component
class A {
@Autowired
private B b;
}
@Component
class B {
@Autowired
private A a;
}
👉 A 依赖 B,B 又依赖 A ------ 死循环?
Spring 解决方案:三级缓存
Spring 用了 3 个缓存(核心设计):
text
一级缓存:singletonObjects(最终Bean)
二级缓存:earlySingletonObjects(半成品Bean)
三级缓存:singletonFactories(Bean工厂)
创建过程(关键理解)
第一步:创建 A(还没初始化)
text
A 实例化(空对象)
👉 放入三级缓存(工厂)
第二步:A 需要 B → 开始创建 B
text
B 实例化
👉 B 也放入三级缓存
第三步:B 需要 A
此时:
text
A 还没创建完成
怎么办?
👉 从三级缓存拿到 A 的"早期对象"
👉 放入二级缓存
👉 注入给 B
第四步:B 创建完成 → 回去继续 A
此时:
text
B 已经好了
👉 注入给 A
👉 A 完成
核心本质
👉 允许"半成品对象"提前暴露
为什么要三级缓存?
不是多此一举,是为了解决 AOP:
👉 如果有代理(AOP):
text
三级缓存:提供"代理对象"
二级缓存:存放"早期Bean"
👉 避免注入原始对象 vs 代理对象不一致
二、AOP 是怎么实现的?(核心机制🔥)
你写的:
java
@Transactional
public void createOrder() {}
你以为:
text
直接执行方法
实际上:
text
执行的是"代理对象"
核心原理:动态代理
Spring 会生成一个"替身对象":
text
你调用 → 代理对象 → 原对象
两种代理方式
| 方式 | 条件 |
|---|---|
| JDK动态代理 | 有接口 |
| CGLIB | 没接口(继承类) |
执行流程
text
调用方法
↓
代理对象拦截
↓
执行增强逻辑(事务、日志等)
↓
调用原方法
↓
提交/回滚
本质一句话
👉 AOP = 在方法执行前后"插代码"
三、@Transactional 为什么会失效?(高频坑🔥)
❌ 场景1:同类内部调用
java
@Service
class OrderService {
public void A() {
B(); // ❌ 事务失效
}
@Transactional
public void B() {}
}
为什么?
text
this.B()
👉 没走代理对象!
👉 直接调用原方法
👉 AOP 根本没机会介入
正确写法:
java
@Autowired
private OrderService self;
public void A() {
self.B(); // ✅ 走代理
}
❌ 场景2:方法不是 public
java
@Transactional
private void test() {}
👉 Spring AOP 默认只代理 public 方法
❌ 场景3:异常被吞掉
java
try {
...
} catch (Exception e) {
// 不抛出
}
👉 Spring 以为没出错 → 不回滚
❌ 场景4:数据库不支持事务
比如:
text
MyISAM(MySQL)
👉 根本没事务
四、Spring 为什么要用代理?(设计哲学)
如果不用 AOP,你会写:
java
public void createOrder() {
startTransaction();
try {
// 业务逻辑
commit();
} catch (Exception e) {
rollback();
}
}
👉 到处都是重复代码
用了 AOP:
java
@Transactional
public void createOrder() {}
👉 业务和技术解耦
五、循环依赖 + AOP 的关系(高级点)
关键点:
👉 Spring 必须在"还没完全初始化时"就能生成代理
所以:
text
三级缓存存在的原因 = 支持 AOP + 循环依赖
六、你现在的认知应该升级到:
Spring 做的不是简单 DI,而是:
text
对象构建 + 依赖解析 + 生命周期 + 动态增强
七、一句高级总结(建议记)
👉
Spring 通过三级缓存解决循环依赖,通过动态代理实现 AOP,通过代理机制实现事务、缓存等横切逻辑,从而实现业务与基础设施解耦。
八、如果你还想再进一层(更硬核)
我可以继续带你拆:
👉 BeanPostProcessor 是怎么改造对象的(AOP入口)
👉 @Transactional 底层源码流程(调用链)
👉 Spring 如何解决"提前暴露代理对象"
👉 为什么构造器注入无法解决循环依赖
👉 Spring Boot 自动配置原理(@EnableAutoConfiguration)