Spring 事务管理是构建健壮企业应用的核心,其核心原理、@Transactional
注解的工作机制、传播行为及失效场景是开发者必须掌握的关键点。下面我将逐一深入解析:
一、Spring 事务管理的核心原理
Spring 事务的本质是 通过 AOP(面向切面编程) 对目标方法进行增强,具体流程如下:
-
代理对象创建:
- 当使用
@Transactional
注解时,Spring 会为目标 Bean(如UserServiceImpl
)创建代理对象(JDK 动态代理或 CGLIB 代理)。 - 调用
userService.updateUser()
时,实际调用的是代理对象的方法。
- 当使用
-
事务拦截器(TransactionInterceptor):
- 代理对象内部包含一个
TransactionInterceptor
。 - 在目标方法执行前,拦截器通过
PlatformTransactionManager
(如DataSourceTransactionManager
)开启事务(获取数据库连接,设置autoCommit=false
)。
- 代理对象内部包含一个
-
目标方法执行:
- 执行原始方法逻辑(包含数据库操作)。
-
事务提交/回滚:
- 方法成功执行 → 提交事务(
connection.commit()
)。 - 抛出未捕获的异常 → 回滚事务(
connection.rollback()
)。
- 方法成功执行 → 提交事务(
java
// 伪代码:TransactionInterceptor 逻辑
public Object invoke(MethodInvocation invocation) {
TransactionStatus status = transactionManager.beginTransaction(); // 开启事务
try {
Object result = invocation.proceed(); // 执行目标方法
transactionManager.commit(status); // 提交事务
return result;
} catch (RuntimeException e) {
transactionManager.rollback(status); // 回滚事务
throw e;
}
}
二、@Transactional
注解工作机制
-
注解解析:
- Spring 扫描 Bean 时识别
@Transactional
,为类或方法生成代理。 - 注解属性(如
propagation
,isolation
,rollbackFor
)被解析为TransactionAttribute
。
- Spring 扫描 Bean 时识别
-
事务属性绑定:
- 方法执行时,
TransactionInterceptor
根据注解属性配置事务行为(如传播机制、隔离级别)。
- 方法执行时,
-
事务管理器协调:
- 通过
PlatformTransactionManager
管理事务生命周期(开启、提交、回滚)。
- 通过
三、事务传播机制(Propagation Behavior)
定义多个事务方法相互调用时的事务边界规则,共 7 种类型:
传播行为类型 | 说明 | 适用场景 |
---|---|---|
REQUIRED (默认) | 当前有事务则加入,没有则新建 | 大多数业务逻辑(如订单创建) |
REQUIRES_NEW | 无论当前是否有事务,都新建事务(原事务挂起) | 日志记录(需独立提交) |
SUPPORTS | 当前有事务则加入,没有则以非事务运行 | 查询方法(可适应事务环境) |
NOT_SUPPORTED | 以非事务方式执行,挂起当前事务(存在时) | 发送消息(避免事务阻塞) |
MANDATORY | 必须在已有事务中运行,否则抛异常 | 强制要求事务上下文 |
NEVER | 必须在非事务环境下执行,否则抛异常 | 禁止事务的方法(如初始化操作) |
NESTED | 在当前事务内嵌套子事务(可独立回滚),需 JDBC 3.0 驱动支持 | 复杂业务的分步操作(如订单子项) |
经典场景对比:
java
@Service
public class OrderService {
@Transactional(propagation = Propagation.REQUIRED)
public void createOrder() {
// 主订单逻辑
orderItemService.addItem(); // 调用子事务方法
}
}
@Service
public class OrderItemService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void addItem() {
// 订单项逻辑(独立事务,失败不影响主订单)
}
}
四、事务失效的 12 个常见场景
-
非 public 方法
- 原因:Spring AOP 无法代理私有方法(CGLIB 通过继承实现代理)。
- 解决:将方法改为
public
。
-
自调用问题
-
原因:类内部方法调用(如
this.update()
)不经过代理对象。 -
解决:
java@Autowired private UserService selfProxy; // 注入自身代理 public void save() { selfProxy.update(); // 通过代理调用 }
-
-
异常类型不匹配
-
原因:默认只回滚
RuntimeException
和Error
,捕获Exception
不会回滚。 -
解决:明确指定回滚异常:
java@Transactional(rollbackFor = Exception.class)
-
-
手动捕获异常未抛出
-
错误示例:
javatry { userDao.insert(); } catch (Exception e) { // 未抛出 → 事务提交! }
-
-
多线程调用
- 原因:事务信息存储在
ThreadLocal
,新线程无法获取上下文。 - 解决:避免跨线程事务操作。
- 原因:事务信息存储在
-
数据库引擎不支持事务
- 如 MySQL 的 MyISAM 引擎不支持事务(需使用 InnoDB)。
-
未启用事务管理
- 缺失注解:
@EnableTransactionManagement
(Spring Boot 中自动配置)。
- 缺失注解:
-
Bean 未被 Spring 管理
- 原因:类未标注
@Service
/@Component
。
- 原因:类未标注
-
Checked Exception 未配置回滚
- 原因:Checked Exception(如
IOException
)默认不回滚。 - 解决:显式配置
@Transactional(rollbackFor = IOException.class)
。
- 原因:Checked Exception(如
-
传播行为配置为 NOT_SUPPORTED/NEVER
- 强制以非事务方式运行。
-
方法内启动新线程异步操作
- 异步操作脱离原事务上下文(需用
@Async
+@Transactional
单独管理)。
- 异步操作脱离原事务上下文(需用
-
嵌套事务回滚不当
- 嵌套事务(NESTED)需外层捕获内层异常,否则整体回滚。
关键总结
维度 | 要点 |
---|---|
代理机制 | JDK 动态代理或 CGLIB 生成代理对象 |
事务管理器 | PlatformTransactionManager 是核心接口 |
传播行为 | REQUIRED(默认)、REQUIRES_NEW、NESTED 最常用 |
失效场景 | 自调用、异常处理、非 public 方法是高频踩坑点 |
调试技巧 | 开启 debug 日志:logging.level.org.springframework.transaction=DEBUG |
最佳实践:
- 始终在
@Service
层的 public 方法上使用@Transactional
。- 明确指定
rollbackFor
(如rollbackFor = Exception.class
)。- 对嵌套事务使用
Propagation.NESTED
时,确保数据库驱动支持保存点(Savepoint)。- 避免在事务方法中处理耗时操作(如 RPC 调用),减少数据库连接占用时间。
理解这些机制能有效避免生产环境的事务陷阱,确保数据一致性。