Spring声明式事务管理从原理到实战示例
目录
- 一、什么是声明式事务管理?
- ✅ 简单说:
- 对比:编程式 vs 声明式
- 二、声明式事务是如何实现的?(核心原理)
- 工作流程如下:
- ️ 关键组件:
- 三、如何配置声明式事务?(XML 配置方式)
- 示例配置解析:
- 关键点:
- 四、事务回滚机制(Rollback Rules)
- 默认规则:
- 自定义回滚规则:
- 1. 某个检查型异常也要回滚:
- 2. 某个运行时异常不要回滚:
- 3. 多规则优先级:
- 五、响应式事务(Reactive Transaction Management)
- 关键区别:
- ️ 六、不同 Bean 使用不同事务配置
- 示例:
- 七、<tx:method>的所有配置项总结
- 八、重要注意事项
- ✅ 总结:如何理解这段内容?
- 建议学习路径
一、什么是声明式事务管理?
✅ 简单说:
你不用在代码里手动写 beginTransaction()、commit()、rollback(),而是通过配置或注解来告诉 Spring:“这个方法需要事务”,Spring 自动帮你管理事务的开始、提交或回滚。
对比:编程式 vs 声明式
| 类型 | 特点 | 代码侵入性 |
|---|---|---|
| 编程式事务 | 手动控制事务(如使用 TransactionTemplate) | 高(代码里到处是事务逻辑) |
| 声明式事务 | 用配置或注解声明事务行为 | 低(业务代码干净) |
Spring 推荐使用 声明式事务,因为它更符合“轻量级容器”的理念 —— 业务代码不依赖事务框架。
二、声明式事务是如何实现的?(核心原理)
Spring 的声明式事务是基于 AOP(面向切面编程) 实现的。
工作流程如下:
- 你有一个服务类,比如
DefaultFooService。 - 你在配置中声明了哪些方法需要事务(通过
<tx:advice>或@Transactional)。 - Spring 在启动时,会为这个类创建一个 代理对象(Proxy)。
- 当你调用
fooService.insertFoo(...)时,实际上是调用了代理对象的方法。 - 代理对象在方法执行前开启事务,执行后根据结果决定提交或回滚。
你调用: fooService.insertFoo()
↓
实际执行: Proxy → 开启事务 → 调用真实方法 → 成功则提交,异常则回滚
️ 关键组件:
TransactionInterceptor:事务拦截器,负责在方法前后插入事务逻辑。TransactionManager:事务管理器,真正执行开启、提交、回滚操作(如DataSourceTransactionManager)。- AOP 代理:JDK 动态代理 或 CGLIB,用于织入事务逻辑。
三、如何配置声明式事务?(XML 配置方式)
文档中的例子使用 XML 配置,虽然现在更多用注解,但理解 XML 有助于理解底层机制。
示例配置解析:
<!-- 定义数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> ... </bean>
<!-- 定义事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 定义事务通知(规则) -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method njsame="get*" read-only="true"/> <!-- 所有get开头的方法:只读事务 -->
<tx:method name="*" /> <!-- 其他方法:默认事务(读写) -->
</tx:attributes>
</tx:advice>
<!-- 将事务通知应用到指定的切点 -->
<aop:config>
<!-- 切点:匹配 FooService 接口的所有方法 -->
javascript <aop:pointcut id="fooServiceOperation"
expression="execution(* x.y.service.FooService.*(..))"/>
<!-- 绑定通知和切点 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation"/>
</aop:config>
关键点:
tx:method name="get*":所有以get开头的方法使用 只读事务(性能优化)。tx:method name="*":其他方法使用默认事务(读写)。aop:pointcut使用 ASPectJ 表达式 匹配方法。
四、事务回滚机制(Rollback Rules)
这是 Spring 事务的一大亮点:你可以精确控制哪些异常触发回滚。
默认规则:
- ✅ 运行时异常(RuntimeException):自动回滚(如
NullPointerException、IllegalArgumentException) - ❌ 检查型异常(Checked Exception):不回滚(如
IOException、自定义的MyException)
⚠️ 这与 EJB CMT 不同!EJB 中检查型异常也不回滚,但 Spring 允许你自定义。
自定义回滚规则:
1. 某个检查型异常也要回滚:
<tx:method name="updateStock" rollback-for="NoProductInStockException"/>
2. 某个运行时异常不要回滚:
<tx:method name="updateStock" no-rollback-for="InstrumentNotFoundException"/>
3. 多规则优先级:
<tx:method name="*"
rollback-for="Throwable"
no-rollback-for="InstrumentNotFoundException"/>
✅ 所有异常都回滚,除了
InstrumentNotFoundException。
五、响应式事务(Reactive Transaction Management)
Spring 5+ 支持响应式编程(如 WebFlux),事务也支持响应式。
关键区别:
| 特性 | 传统(Imppythonerative) | 响应式(Reactive) |
|---|---|---|
| 返回类型 | Foo, void | Mono<Foo>, Flux<Foo> |
| 事务管理器 | PlatformTransactionManager | ReactiveTransactionManager |
| 事务传播 | 基于 ThreadLocal | 基于 Reactor Context |
| 事务触发 | 方法调用立即开始 | 返回的 Publisher 被订阅时才开始 |
响应式事务是“惰性的”:调用方法不立即开启事务,而是等到
.subscribe()时才开始。
️ 六、不同 Bean 使用不同事务配置
你可以为不同的服务类设置不同的事务规则。
示例:
<aop:pointcut id="defaultServiceOperation"
expression="execution(* x.y.service.*Service.*(..))"/>
<aop:pointcut id="noTxServiceOperation"
expression="execution(* x.y.service.ddl.DefaultDdlManager.*(..))"/>
<aop:advisor pointcut-ref="defaultServiceOperation" advice-ref="defaultTxAdvice"/>
<aop:advisor pointcut-ref="noTxServiceOperation" advice-ref="noTxAdvice"/>
*Service类:使用默认事务(可读写)DefaultDdlManager类:使用propagation="NEVER"(禁止在事务中运行)
这很实用,比如 DDL 操作(建表、删表)通常不应在事务中运行。
七、<tx:method>的所有配置项总结
| 属性 | 是否必需 | 默认值 | 说明 |
|---|---|---|---|
name | ✅ 是 | - | 方法名(支持 * 通配符) |
propagation | 否 | REQUIRED | 传播行为(如 REQUIRED, REQUIRES_NEW, NEVER 等) |
isolation | 否 | DEFAULT | 隔离级别(如 READ_COMMITTED) |
timeout | 否 | -1(无超时) | 超时时间(秒) |
read-only | 否 | false | 是否只读事务 |
rollback-for | 否 | - | 哪些异常触发回滚(可写多个,逗号分隔) |
no-rollback-for | 否 | - | 哪些异常不触发回滚 |
八、重要注意事项
- 代理机制限制:
- 只有 外部调用 代理对象的方法才会触发事务。
- 如果你在同一个类http://www.devze.com中调用
this.method(),事务不会生效(因为没走代理)。
- 事务不会跨远程调用:
- Spring 不支持像 EJB 那样将事务上下文传播到远程服务。
- 如果你需要分布式事务,考虑使用 Seata 或 Spring Cloud Alibaba 等方案。
- 异常必须抛出:
- 如果你在方法内部
catwww.devze.comch了异常但没有重新抛出,事务不会回滚。 - 正确做法:
catch后throw或调用setRollbackOnly()。
- 如果你在方法内部
@Transactional注解更常用:- 现代 Spring 开发通常使用
@Transactional注解代替 XML。
- 现代 Spring 开发通常使用
- 例如:
@Service
public class FooService {
@Transactional(readOnly = true)
public Foo getFoo(String name) { ... }
@Transactional(rollbackFor = MyException.class)
public void updateFoo(Foo foo) { ... }
}
需要启用:@EnableTransactionManagement
✅ 总结:如何理解这段内容?
| 问题 | 回答 |
|---|---|
| 声明式事务是什么? | 用配置或注解声明事务,无需手动控制。 |
| 怎么实现的? | 基于 AOP,Spring 创建代理对象,在方法前后自动加事务逻辑。 |
| 怎么配置? | 用 <tx:advice> 定义规则,用 <aop:config> 应用到目标方法。 |
| 异常怎么回滚? | 默认运行时异常回滚,可通过 rollback-for 自定义。 |
| 不同类不同配置? | 可以,定义多个 <tx:advice> 和 <aop:advisor>。 |
| 响应式支持吗? | 支持,基于 ReactiveTransactionManager 和 Reactor 上下文。 |
建议学习路径
- 先理解 AOP 基本概念(代理、切点、通知)
- 动手写一个 XML 配置的声明式事务例子
- 改造成
@Transactional注解方式 - 尝试 自定义回滚规则
- 了解 事务传播行为(如
REQUIRED,REQUIRES_NEW) - (进阶)学习 响应式事务 和 分布式事务
如果你希望,我可以:
- 把这个内容转成中文思维导图
- 写一个完整的可运行代码示例(Java + Spring + 注解)
- 解释
@Transactional的各种传播行为
欢迎继续提问!
到此这篇关于Spring声明式事务管理从原理到实战示例的文章就介绍到这了,更多相关Spring声明式事务管理内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
加载中,请稍侯......
精彩评论