Spring声明式事务管理从原理到实战示例

 更新时间:2025年11月01日 09:34:35   作者:lang20150928  
本文主要介绍了Spring声明式事务管理的概念、实现原理、配置方法、异常回滚机制、不同类的事务配置以及响应式事务的支持,声明式事务通过配置或注解简化了事务管理,使用AOP实现,支持多种传播行为和隔离级别,感兴趣的朋友跟随小编一起看看吧

🌟 一、什么是声明式事务管理?

✅ 简单说:

你不用在代码里手动写 beginTransaction()commit()rollback(),而是通过配置或注解来告诉 Spring:“这个方法需要事务”,Spring 自动帮你管理事务的开始、提交或回滚。

🔁 对比:编程式 vs 声明式

类型特点代码侵入性
编程式事务手动控制事务(如使用 TransactionTemplate高(代码里到处是事务逻辑)
声明式事务用配置或注解声明事务行为低(业务代码干净)

📌 Spring 推荐使用 声明式事务,因为它更符合“轻量级容器”的理念 —— 业务代码不依赖事务框架。

🧠 二、声明式事务是如何实现的?(核心原理)

Spring 的声明式事务是基于 AOP(面向切面编程) 实现的。

🔍 工作流程如下:

  1. 你有一个服务类,比如 DefaultFooService
  2. 你在配置中声明了哪些方法需要事务(通过 <tx:advice>@Transactional)。
  3. Spring 在启动时,会为这个类创建一个 代理对象(Proxy)
  4. 当你调用 fooService.insertFoo(...) 时,实际上是调用了代理对象的方法。
  5. 代理对象在方法执行前开启事务,执行后根据结果决定提交或回滚。
你调用:   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 name="get*" read-only="true"/>  <!-- 所有get开头的方法:只读事务 -->
        <tx:method name="*" />                     <!-- 其他方法:默认事务(读写) -->
    </tx:attributes>
</tx:advice>
<!-- 将事务通知应用到指定的切点 -->
<aop:config>
    <!-- 切点:匹配 FooService 接口的所有方法 -->
    <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):自动回滚(如 NullPointerExceptionIllegalArgumentException
  • 检查型异常(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),事务也支持响应式。

关键区别:

特性传统(Imperative)响应式(Reactive)
返回类型Foo, voidMono<Foo>, Flux<Foo>
事务管理器PlatformTransactionManagerReactiveTransactionManager
事务传播基于 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✅ 是-方法名(支持 * 通配符)
propagationREQUIRED传播行为(如 REQUIRED, REQUIRES_NEW, NEVER 等)
isolationDEFAULT隔离级别(如 READ_COMMITTED
timeout-1(无超时)超时时间(秒)
read-onlyfalse是否只读事务
rollback-for-哪些异常触发回滚(可写多个,逗号分隔)
no-rollback-for-哪些异常触发回滚

🧩 八、重要注意事项

  • 代理机制限制
    • 只有 外部调用 代理对象的方法才会触发事务。
    • 如果你在同一个类中调用 this.method()事务不会生效(因为没走代理)。
  • 事务不会跨远程调用
    • Spring 不支持像 EJB 那样将事务上下文传播到远程服务。
    • 如果你需要分布式事务,考虑使用 SeataSpring Cloud Alibaba 等方案。
  • 异常必须抛出
    • 如果你在方法内部 catch 了异常但没有重新抛出,事务不会回滚
    • 正确做法:catchthrow 或调用 setRollbackOnly()
  • @Transactional 注解更常用
    • 现代 Spring 开发通常使用 @Transactional 注解代替 XML。
  • 例如:
@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 上下文。

📚 建议学习路径

  1. 先理解 AOP 基本概念(代理、切点、通知)
  2. 动手写一个 XML 配置的声明式事务例子
  3. 改造成 @Transactional 注解方式
  4. 尝试 自定义回滚规则
  5. 了解 事务传播行为(如 REQUIRED, REQUIRES_NEW
  6. (进阶)学习 响应式事务分布式事务

如果你希望,我可以:

  • 把这个内容转成中文思维导图
  • 写一个完整的可运行代码示例(Java + Spring + 注解)
  • 解释 @Transactional 的各种传播行为

欢迎继续提问!

到此这篇关于Spring声明式事务管理从原理到实战示例的文章就介绍到这了,更多相关Spring声明式事务管理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 解决SpringBoot整合ElasticSearch遇到的连接问题

    解决SpringBoot整合ElasticSearch遇到的连接问题

    这篇文章主要介绍了解决SpringBoot整合ElasticSearch遇到的连接问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • Java实现普通类注入service对象

    Java实现普通类注入service对象

    这篇文章主要介绍了Java实现普通类注入service对象,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • 已解决:No ''Access-Control-Allow-Origin''跨域问题

    已解决:No ''Access-Control-Allow-Origin''跨域问题

    这篇文章主要介绍了已解决:No 'Access-Control-Allow-Origin' 跨域,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • 解析Java中的队列和用LinkedList集合模拟队列的方法

    解析Java中的队列和用LinkedList集合模拟队列的方法

    这篇文章主要介绍了解析Java中的队列和用LinkedList集合模拟队列的方法,相关算法的实现也是ACM中的常见题目,需要的朋友可以参考下
    2015-08-08
  • java整合微信支付功能详细示例

    java整合微信支付功能详细示例

    这篇文章主要给大家介绍了关于java整合微信支付功能的相关资料,支付是一个复杂且测试起来需要的配置特别复杂的模块,文中给出了详细的示例代码,需要的朋友可以参考下
    2023-07-07
  • MyBatis Mapper XML中比较操作符转义问题解决

    MyBatis Mapper XML中比较操作符转义问题解决

    在使用MyBatis编写Mapper XML时,有时会遇到比较操作符需要进行转义的情况,本文主要介绍了MyBatis Mapper XML中比较操作符转义问题解决,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • java实现请求缓冲合并的示例代码

    java实现请求缓冲合并的示例代码

    我们对外提供了一个rest接口给第三方业务进行调用,但是由于第三方框架限制,导致会发送大量相似无效请求,这篇文章主要介绍了java实现请求缓冲合并,需要的朋友可以参考下
    2024-04-04
  • java 远程文件url如何转为输入流

    java 远程文件url如何转为输入流

    这篇文章主要介绍了java 远程文件url如何转为输入流方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • java 字符串内存分配的分析与总结(推荐)

    java 字符串内存分配的分析与总结(推荐)

    下面小编就为大家带来一篇java 字符串内存分配的分析与总结(推荐)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-08-08
  • Java程序开发环境配置图文教程

    Java程序开发环境配置图文教程

    这篇文章主要为大家详细介绍了Java程序开发环境配置图文教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-07-07

最新评论