1. 场景与要解决的问题

  • 在业务代码里,常见诉求是:只有当数据库事务真正提交成功后,才去执行某些“后置动作”,例如:

    • 发送 MQ、推送消息、写审计/埋点日志、刷新缓存、通知外部系统等。

  • 如果这些动作在事务提交前就执行,一旦事务最后回滚,就会出现数据与副作用不一致(例如:数据库没落库,但 MQ 已发出)。

  • Spring 给出两类工具来“跟随事务走”:

    • TransactionSynchronization直接注册事务回调,在 afterCommit() 等时点执行。

    • @TransactionalEventListener发布事件 + 事务阶段监听,在 AFTER_COMMIT 等阶段触发监听方法。

2. TransactionSynchronization 使用方法

2.1 它是什么

  • 事务同步回调接口(Transaction Synchronization Callback Interface)。

  • 它能让你在事务的关键节点(提交前、提交后、回滚后、完成后)挂接同步逻辑

  • 本质是 “钩子/回调”,属于 Spring 事务 SPI(Service Provider Interface)扩展点

2.2 使用方法

@Service
public class WithdrawService {@Transactional(rollbackFor = Exception.class)public void createWithdrawOrder(Long userId, BigDecimal amount) {// 1) 业务数据更新(示例)//   - 扣可用余额、加冻结余额、插入订单等//   - 此处略…Long orderId = 123L; // 假设是插入订单后拿到的IDBigDecimal income = amount;// 2) 绑定到“当前事务”的提交后回调TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {@Overridepublic void afterCommit() {try {// 3) 事务真正提交成功后才会执行到这里withdrawProducer.sendMessage(orderId);log.info("已发送提现MQ, orderId={}", orderId);} catch (Exception ex) {// 注意:此时事务已提交,失败不会回滚主事务log.error("提交后发送MQ失败, orderId={}", orderId, ex);// 可在此触发重试/告警/记录Outbox补偿等}}});}
}

2.3 最佳实践

  • 必须在活动事务中注册:可用 TransactionSynchronizationManager.isSynchronizationActive() 检查。

  • 不要在 afterCommit 里做重 IO/耗时操作,以免拉长请求尾延迟;建议再丢到自定义线程池执行。

  • 异常处理afterCommit 里异常不会回滚主事务(已提交),要自处置(告警/重试/Outbox)。

  • 事务传播:在哪一层注册,就跟随那一层的事务。若内部有 REQUIRES_NEW 子事务,你在子事务里注册的回调只跟随子事务。

  • 多层回调:如果需要控制多个回调的顺序,可让回调实现 org.springframework.core.Ordered 接口,getOrder() 返回值越小优先级越高。


3. @TransactionalEventListener 使用方法

3.1 它是什么

  1. 发布-订阅风格的事务阶段监听。业务方法里发布事件,监听方法用 @TransactionalEventListener 声明在指定事务阶段运行(常用 AFTER_COMMIT)。

  2. 事件可以被多个监听器消费;可配 @Async 在提交后异步执行。

3.2 使用方法(同步监听模板)

// 1) 自定义事件(POJO 即可)
public record WithdrawOrderCreatedEvent(Long orderId, Long userId, BigDecimal amount) {}// 2) 事务中发布事件
@Service
public class WithdrawService {@Autowired private ApplicationEventPublisher publisher;@Transactional(rollbackFor = Exception.class)public void createWithdrawOrder(Long userId, BigDecimal amount) {// 业务入库…(略)Long orderId = 123L;publisher.publishEvent(new WithdrawOrderCreatedEvent(orderId, userId, amount));}
}// 3) 监听端:仅在“提交成功后”触发
@Component
public class WithdrawEventListener {@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)public void onCreated(WithdrawOrderCreatedEvent evt) {try {withdrawProducer.sendMessage(evt.orderId());log.info("提交后发送MQ成功, orderId={}", evt.orderId());} catch (Exception ex) {log.error("提交后发送MQ失败, orderId={}", evt.orderId(), ex);}}
}

3.3 提交后异步执行(可选)

@Configuration
@EnableAsync
public class AsyncCfg implements AsyncConfigurer {@Override public Executor getAsyncExecutor() {return Executors.newFixedThreadPool(8);}
}@Component
public class WithdrawAsyncListener {@Async@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)public void onCreated(WithdrawOrderCreatedEvent evt) {withdrawProducer.sendMessage(evt.orderId()); // 提交后异步发送}
}

4. 对比与选型

  • 一致性保障

    • 二者都能保证:只有事务提交成功后才执行(afterCommit / AFTER_COMMIT)。

  • 耦合与扩展

    • TransactionSynchronization:代码直接注册回调,与业务方法耦合,适合单一后置动作

    • @TransactionalEventListener发布-订阅,天然解耦,一个事件可被多个监听器消费,易扩展。

  • 异步能力

    • TransactionSynchronization:天生同步;可在回调内手动丢线程池异步。

    • @TransactionalEventListener:可直接叠加 @Async提交后异步执行。

  • 无事务时的行为

    • TransactionSynchronization必须存在活动事务,否则注册失败。

    • @TransactionalEventListener:默认无事务不触发fallbackExecution = true 可强制执行(会失去“提交后”语义)。

  • 性能与代码复杂度

    • TransactionSynchronization:路径最短、开销最小、代码最少。

    • @TransactionalEventListener:有事件派发的轻微开销,换来更好的解耦与可维护性。

  • 测试与团队协作

    • TransactionSynchronization:更贴近底层钩子,单一动作简单直观。

    • @TransactionalEventListener:语义清晰(业务事件),多人协作模块解耦更友好,监听可独立单测。

  • 推荐选型(面向常见场景)

    • 只需一个消费方,追求超轻量 → 选 TransactionSynchronization.afterCommit()

    • 需要解耦/多个消费方/可异步 → 选 @TransactionalEventListener(phase = AFTER_COMMIT)

    • 需要强可靠(不能丢消息) → 在以上任一方案外,叠加 Outbox 模式(业务表 + 出站表同事务写入,后台可靠投递 MQ/补偿重试)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/95699.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/95699.shtml
英文地址,请注明出处:http://en.pswp.cn/web/95699.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Clickhouse MCP@Mac+Cherry Studio部署与调试

一、需求背景 已经部署测试了Mysql、Drois的MCP Server,想进一步测试Clickhouse MCP的表现。 二、环境 1)操作系统 MacOS+Apple芯片 2)Clickhouse v25.7.6.21-stable、Clickhouse MCP 0.1.11 3)工具Cherry Studio 1.5.7、Docker Desktop 4.43.2(199162) 4)Python 3.1…

Java Serializable 接口:明明就一个空的接口嘛

对于 Java 的序列化,我之前一直停留在最浅层次的认知上——把那个要序列化的类实现 Serializbale 接口就可以了嘛。 我似乎不愿意做更深入的研究,因为会用就行了嘛。 但随着时间的推移,见到 Serializbale 的次数越来越多,我便对它产生了浓厚的兴趣。是时候花点时间研究研…

野火STM32Modbus主机读取寄存器/线圈失败(三)-尝试将存贮事件的地方改成数组(非必要解决方案)(附源码)

背景 尽管crc校验正确了,也成功发送了EV_MASTER_EXECUTE事件,但是eMBMasterPoll( void )中总是接收的事件是EV_MASTER_FRAME_RECEIVED或者EV_MASTER_FRAME_SENT,一次都没有执行EV_MASTER_EXECUTE。EV_MASTER_EXECUTE事件被别的事件给覆盖了&…

微信小程序校园助手程序(源码+文档)

源码题目:微信小程序校园助手程序(源码文档)☑️ 文末联系获取(含源码、技术文档)博主简介:10年高级软件工程师、JAVA技术指导员、Python讲师、文章撰写修改专家、Springboot高级,欢迎高校老师、…

59-python中的类和对象、构造方法

1. 认识一下对象 世间万物皆是"对象" student_1{ "姓名":"小朴", "爱好":"唱、跳、主持" ......... }白纸填写太落伍了 设计表格填写先进一些些 终极目标是程序使用对象去组织数据程序中设计表格,我们称为 设计类…

向成电子惊艳亮相2025物联网展,携工控主板等系列产品引领智造新风向

2025年8月27-29日,IOTE 2025 第二十四届国际物联网展深圳站在深圳国际会展中心(宝安)盛大启幕!作为全球规模领先的物联网盛会之一,本届展会以“生态智能,物联全球”为核心,汇聚超1000家全球头部…

阵列信号处理之均匀面阵波束合成方向图的绘制与特点解读

阵列信号处理之均匀面阵波束合成方向图的绘制与特点解读 文章目录前言一、方向图函数二、方向图绘制三、副瓣电平四、阵元个数对主瓣宽度的影响五、阵元间距对主瓣宽度的影响六、MATLAB源代码总结前言 \;\;\;\;\;均匀面阵(Uniform Planar Array,UPA&…

算法在前端框架中的集成

引言 算法是前端开发中提升性能和用户体验的重要工具。随着 Web 应用复杂性的增加,现代前端框架如 React、Vue 和 Angular 提供了强大的工具集,使得将算法与框架特性(如状态管理、虚拟 DOM 和组件化)无缝集成成为可能。从排序算法…

网络爬虫是自动从互联网上采集数据的程序

网络爬虫是自动从互联网上采集数据的程序网络爬虫是自动从互联网上采集数据的程序,Python凭借其丰富的库生态系统和简洁语法,成为了爬虫开发的首选语言。本文将全面介绍如何使用Python构建高效、合规的网络爬虫。一、爬虫基础与工作原理 网络爬虫本质上是…

Qt Model/View/Delegate 架构详解

Qt Model/View/Delegate 架构详解 Qt的Model/View/Delegate架构是Qt框架中一个重要的设计模式,它实现了数据存储、数据显示和数据编辑的分离。这种架构不仅提高了代码的可维护性和可重用性,还提供了极大的灵活性。 1. 架构概述 Model/View/Delegate架构将…

光谱相机在手机行业的应用

在手机行业,光谱相机技术通过提升拍照色彩表现和扩展健康监测等功能,正推动摄像头产业链升级,并有望在AR/VR、生物医疗等领域实现更广泛应用。以下为具体应用场景及技术突破的详细说明:‌一、光谱相机在手机行业的应用场景‌‌拍照…

FASTMCP中的Resources和Templates

Resources 给 MCP 客户端/LLM 读取的数据端点(只读、按 URI 索引、像“虚拟文件系统”或“HTTP GET”); Templates 可带参数的资源路由(URI 里占位符 → 运行函数动态生成内容)。 快速要点 • 用途:把文件…

OpenBMC之编译加速篇

加快 OpenBMC 的编译速度是一个非常重要的话题,因为完整的构建通常非常耗时(在高性能机器上也需要数十分钟,普通电脑上可能长达数小时)。以下是从不同层面优化编译速度的详细策略,您可以根据自身情况组合使用。 一、核心方法:利用 BitBake 的缓存和共享机制(效果最显著…

Kafka面试精讲 Day 8:日志清理与数据保留策略

【Kafka面试精讲 Day 8】日志清理与数据保留策略 在Kafka的高吞吐、持久化消息系统中,日志清理与数据保留策略是决定系统资源利用效率、数据可用性与合规性的关键机制。作为“Kafka面试精讲”系列的第8天,本文聚焦于日志清理机制(Log Cleani…

基于Hadoop的网约车公司数据分析系统设计(代码+数据库+LW)

摘 要 本系统基于Hadoop平台,旨在为网约车公司提供一个高效的数据分析解决方案。随着网约车行业的快速发展,平台上产生的数据量日益增加,传统的数据处理方式已无法满足需求。因此,设计了一种基于Hadoop的大规模数据处理和分析方…

Python反向迭代完全指南:从基础到高性能系统设计

引言:反向迭代的核心价值在数据处理和算法实现中,反向迭代是解决复杂问题的关键技术。根据2024年Python开发者调查报告:85%的链表操作需要反向迭代78%的时间序列分析依赖反向处理92%的树结构遍历需要后序/逆序访问65%的加密算法使用反向计算P…

ClickHouse使用Docker部署

OLTP和OLAP介绍基本业务量到达分库分表量级,则离不开数据大屏、推荐系统、画像系统等搭建,需要搭建以上系统,则离不开海量数据进行存储-分析-统计。 而海量数据下 TB、PB级别数据存储,靠Mysql进行存储-分析-统计无疑是灾难。所以就…

Python 算数运算练习题

计算数字特征值题目描述 编写一个程序,接收用户输入的两个整数 a 和 b(a > b > 0),计算并输出以下结果:a 与 b 的和的平方a 除以 b 的商和余数a 与 b 的平均数(保留 2 位小数)示例请输入整…

【物种分布模型】R语言物种气候生态位动态量化与分布特征模拟——气候生态位动态检验、质心转移可视化、适生区预测等

R语言是一种广泛用于统计分析和图形表示的编程语言,强大之处在于可以进行多元数据统计分析,以及丰富的生态环境数据分析的方法,在生态学领域得到广泛应用。本次教程将通过R语言多个程序包与GIS融合应用,提升物种气候生态位动态量化…

【算法速成课2 | 题单】背包问题

专栏指路:《算法速成课》 前导: 动态规划问题中最入门、也最多变的,当属背包问题。 简单来说,就是在有限的空间,(花费最小的代价)达成最大的收益。 本文会讲一些常见的背包问题(可…