02.3 — @Transactional 原理
定位: @Transactional 注解的 AOP 实现原理和同方法调用失效问题 面试高频度: ⭐⭐⭐⭐⭐ 考查方式: @Transactional 底层机制、失效场景、事务传播
一、这是什么?为什么需要它?
@Transactional 是声明式事务的核心注解。它基于 AOP 代理机制,在方法执行前后自动创建/提交/回滚事务,让开发者无需编写事务管理代码。
二、原理拆解
2.1 @Transactional 执行流程
调用 Bean 方法
-> 代理对象? -> 是
-> TransactionInterceptor (@Around 环绕通知)
-> 获取事务属性
-> PlatformTransactionManager.getTransaction
-> 执行目标方法
-> 成功: commit | 异常: rollback
-> 返回结果
-> 否 (直接调用原始对象) -> 事务不生效!核心流程:
- TransactionInterceptor 实现 MethodInterceptor
- 在 invoke() 方法中,根据 @Transactional 配置获取事务属性
- 调用 PlatformTransactionManager.getTransaction() 创建/加入事务
- 通过反射执行目标方法
- 无异常 -> commit();异常 -> rollback()
2.2 同方法内 @Transactional 失效
java
@Service
public class UserService {
@Transactional
public void methodA() {
// 事务生效 — 因为通过代理调用
methodB(); // B 上的事务不生效!
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
// 这里的事务配置不会生效
// 因为 methodB 是通过 this.methodB() 调用的,不是代理对象
}
}原因
Spring AOP 基于代理,方法内 this.methodB() 调用的是原始对象的方法,而非代理对象,代理拦截未生效。
三种解决方案
| 方案 | 代码 | 优缺点 |
|---|---|---|
| AopContext.currentProxy() | ((UserService) AopContext.currentProxy()).methodB() | 需要 @EnableAspectJAutoProxy(exposeProxy = true) |
| 自注入 | @Autowired self; self.methodB() | 造成循环依赖(可用 @Lazy 缓解) |
| 拆分为两个 Service | 将 B 移到另一个 Service 类 | 最干净,但增加类数量 |
同样的问题也影响 @Async
java
@Service
public class NotificationService {
public void sendBatch() {
this.sendEmail(); // @Async 不生效!
}
@Async
public void sendEmail() { ... }
}三、面试视角
| 追问 | 答案要点 |
|---|---|
| @Transactional 在 private 方法上为什么不生效? | 因为 CGLIB/JDK 无法代理 private 方法 |
| 同方法调用 @Transactional 什么情况不生效? | this.method() 调用原始对象,代理不拦截 |
| 三种解决方案哪个最好? | 拆分为两个 Service 最干净 |
| @Transactional 默认回滚什么异常? | RuntimeException 和 Error,checked exception 不回滚 |
📚 相关链接
- **事务管理板块** — 深入学习事务传播行为和源码
- **JDK动态代理与CGLIB** — 为什么需要代理才能拦截
- ← 返回 **返回 AOP 索引**