解决事务提交延迟问题:Spring中的事务绑定事件监听机制解析
目录
- 一、背景
- 二、事务绑定事件介绍
- 三、事务绑定事件原理
- 四、结语
一、背景
实际工作中碰到一个场景,现存系统有10w张卡需要进行换卡,简单来说就是为用户生成一张新卡,批量换卡申请需要进行审核,审核通过后异步进行处理。
为什么要异步呢?首先是每批次的处理量很大,还有就是换卡的过程需要多次外调第三方,整个事务非常耗时,流程图如下:

这里有个问题,就是审核通过后修改申请记录的状态是一个事务,事务可能没提交,申请记录状态未变更为审核通过,MQ消费端就已经处理了,由于没查询到审核通过的记录,导致操作失败。
那么这个问题怎么解决呢?有同学会说审核通过后延迟几秒发送MQ,但事务提交的时间取决于变更的记录,网络耗时等等,不可控的因素比较多。
最好的方式就是等事务提交后再发送MQ进行异步处理,在Spring中有两种方式进行处理:
- 编程式事务,借用
PlatformTransactionManager平台事务管理器手动提交事务后发送MQ。 - 声明式事务,通过
TransactionalEventListener监听事务是否提交,然后再进行处理,这也是我们今天要介绍的事务绑定事件监听机制。
当然,通过事件发布和订阅的方式,也利于业务代码之间的解耦。
二、事务绑定事件介绍
从Spring 4.2版本开始,事件监听器可以绑定到事务的某个阶段,最典型的应用就是当事务完成时再处理事件。注册一个常规的监听器我们可以通过@EventListener来实现。
如果我们需要将事件和事务绑定可以使用 @TransactionalEventListener注解,默认情况下,监听器会绑定到事务的提交阶段。
举个例子:
事务事件发布者:
@Component
public class TransactionalEventPublisher implements ApplicationEventPublisherAware {private ApplicationEventPublisher applicationEventPublisher;public void publishCreationEvent(CreationEvent creationEvent) {applicationEventPublisher.publishEvent(creationEvent);}@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {this.applicationEventPublisher = applicationEventPublisher;}public static class CreationEvent extends ApplicationEvent {public CreationEvent(Object source) {super(source);}}
}
事务事件监听器:
@Component
public class TransactionalEventListener {@TransactionalEventListener
public void handleOrderCreatedEvent(CreationEvent creationEvent) {}
}
TransactionalEventListener注解暴露了一个phase的属性,通过该属性可以指定监听器绑定到事务的哪个阶段,该属性的值有:
- BEFORE_COMMIT:事务提交前。
- AFTER_COMMIT:事务提交后。
- AFTER_ROLLBACK:事务回滚后。
- AFTER_COMPLETION:事务完成后(提交或者回滚)。
如果事件发布时没有事务运行,事务监听器不会被调用,但可以通过设置fallbackExecution为true来指定即使事件发布时没有事务运行,监听器也会被调用。
三、事务绑定事件原理
正常的ApplicationEvent会在事件发布时同步调用事件监听器进行处理,但是当事件在事务环境中运行发布,且被TransacntionApplicationListener监听时不会直接调用监听器的处理方法,而是会通过回调的方式根据事务所处的阶段进行回调。
那么这种方式到底是怎么实现的呢?
想看实现,先得看监听器的实现,处理事务事件的监听器为TransactionalApplicationListener,它的核心实现为TransactionalApplicationListenerMethodAdapter,从名字就能看出来是里面包含了事务事件处理的处理逻辑。
类图如下:

主要看onApplicationEvent这个方法,可以看到逻辑也很简单:
- 如果当前线程有事务绑定,那么会通过
TransactionSynchronizationManager事务同步管理器注册一个事务事件监听的同步器。 - 如果当前线程没有事务绑定,且
fallbackExecution属性为true,那么会直接调用父类ApplicationListenerMethodAdapter的processEvent方法,说白了就是调用事件监听方法。
备注:事务事件监听器也只有在由
PlatformTransactionManage平台事务管理器管理的线程绑定的事务中才生效。
@Override
public void onApplicationEvent(ApplicationEvent event) {if (TransactionSynchronizationManager.isSynchronizationActive() &&TransactionSynchronizationManager.isActualTransactionActive()) {TransactionSynchronizationManager.registerSynchronization(new TransactionalApplicationListenerSynchronization<>(event, this, this.callbacks));}else if (this.fallbackExecution) {if (getTransactionPhase() == TransactionPhase.AFTER_ROLLBACK && logger.isWarnEnabled()) {logger.warn("Processing " + event + " as a fallback execution on AFTER_ROLLBACK phase");}processEvent(event);}else {// No transactional event execution at allif (logger.isDebugEnabled()) {logger.debug("No transaction is active - skipping " + event);}}
}
至于TransactionalApplicationListenerSynchronization是啥呢?
其实它就是一个事务同步的回调接口,主要由AbstractPlatformTransactionManager抽象平台事务管理器在事务提交的各个阶段进行调用。
说到平台事务管理器大家肯定都熟悉,它包含了Spring中事务处理基本流程,比如:
- 当前方法是否有事务运行。
- 应用指定的隔离级别。
- 挂起或者恢复事务。
- 检查提交时的
rollback-only标识。 - 触发注册的同步回调(TransactionSynchronization)。
public interface TransactionSynchronization extends Ordered, Flushable {/** Completion status in case of proper commit. */int STATUS_COMMITTED = 0;/** Completion status in case of proper rollback. */int STATUS_ROLLED_BACK = 1;/** Completion status in case of heuristic mixed completion or system errors. */int STATUS_UNKNOWN = 2;@Overridedefault int getOrder() {return Ordered.LOWEST_PRECEDENCE;}default void suspend() {}default void resume() {}@Overridedefault void flush() {}default void beforeCommit(boolean readOnly) {}default void beforeCompletion() {}default void afterCommit() {}default void afterCompletion(int status) {}}
从上面的方法可以看出,TransactionSynchronization包含了一些事务相关的回调方法,我们看看它的实现TransactionalApplicationListenerSynchronization到底做了什么?
class TransactionalApplicationListenerSynchronization<E extends ApplicationEvent>implements TransactionSynchronization {private final E event;private final TransactionalApplicationListener<E> listener;private final List<TransactionalApplicationListener.SynchronizationCallback> callbacks;public TransactionalApplicationListenerSynchronization(E event, TransactionalApplicationListener<E> listener,List<TransactionalApplicationListener.SynchronizationCallback> callbacks) {this.event = event;this.listener = listener;this.callbacks = callbacks;}@Overridepublic int getOrder() {return this.listener.getOrder();}@Overridepublic void beforeCommit(boolean readOnly) {if (this.listener.getTransactionPhase() == TransactionPhase.BEFORE_COMMIT) {processEventWithCallbacks();}}@Overridepublic void afterCompletion(int status) {TransactionPhase phase = this.listener.getTransactionPhase();if (phase == TransactionPhase.AFTER_COMMIT && status == STATUS_COMMITTED) {processEventWithCallbacks();}else if (phase == TransactionPhase.AFTER_ROLLBACK && status == STATUS_ROLLED_BACK) {processEventWithCallbacks();}else if (phase == TransactionPhase.AFTER_COMPLETION) {processEventWithCallbacks();}}private void processEventWithCallbacks() {this.callbacks.forEach(callback -> callback.preProcessEvent(this.event));try {this.listener.processEvent(this.event);}catch (RuntimeException | Error ex) {this.callbacks.forEach(callback -> callback.postProcessEvent(this.event, ex));throw ex;}this.callbacks.forEach(callback -> callback.postProcessEvent(this.event, null));}}
上面的代码也很简单,该类的beforeCommit和afterCompletion方法会判断当前事务的状态以及监听器里指定的事务阶段去调用监听器里的业务方法。
四、结语
至此,事务事件监听的实现还是不复杂的,平时工作中也会有很多场景会用到,非常实用,下面是一个简单的流程图:


相关文章:
解决事务提交延迟问题:Spring中的事务绑定事件监听机制解析
目录 一、背景二、事务绑定事件介绍三、事务绑定事件原理四、结语 一、背景 实际工作中碰到一个场景,现存系统有10w张卡需要进行换卡,简单来说就是为用户生成一张新卡,批量换卡申请需要进行审核,审核通过后异步进行处理。 为什么…...
Python 异步编程的秘密武器:Asyncio
python编程中,异步编程是一个重要概念。它允许我们在等待某些操作(如网络请求或文件读写)时,不阻塞程序的其他部分运行。 在 Python 中,asyncio 是实现异步编程的强大工具。今天,我们将一同探索 asyncio 的…...
10年计算机考研408-计算机网络
【题33】下列选项中,不属于网络体系结构所描述的内容是() A.网络的层次 B.每一层使用的协议 C.协议的内部实现细节 D.每一层必须完成的功能 解析: 本题考查的是网络体系结构相关的概念。 图1描述了网络的7层架构以及每一层所要完成…...
深信服校招面试总结
许久没有更新博客,这两个月里发生的事情有些多。最近稍微稳定下来了,应该可以重新开始吧。 背景 首先感觉自己的笔试做的还行,除了第三个编程题没做出来,其他的应该都做出来了。当时忘记并查集的路径压缩怎么写了,加上…...
【LeetCode热题100】模拟
这篇博客记录了模拟相关的题目,也就是按照题目的描述写代码,很锻炼代码实现能力,包括了替换所有的问号、Z字形变换、外观数列、数青蛙4道题。 class Solution { public:string modifyString(string s) {int n s.size();for(int i 0 ; i <…...
如何在Chrome最新浏览器中调用ActiveX控件?
小编最近登陆工商银行网上银行,发现工商银行的个人网银网页,由于使用了ActiveX安全控件,导致不能用高版本Chrome浏览器打开,目前只有使用IE或基于IE内核的浏览器才能正常登录网上银行,而IE已经彻底停止更新了ÿ…...
一款好用的远程连接工具:MobaXterm
在日常工作中,作为开发者或运维人员,你是否经常需要远程连接服务器进行调试和管理?传统的SSH工具常常不够灵活,操作繁琐,无法满足日益复杂的工作需求。而MobaXterm的出现,带来了远程连接工具的全新体验。它…...
Spring Boot使用配置方式整合MyBatis
文章目录 一、实战目标二、步骤概览1. 创建部门映射器接口2. 创建映射器配置文件3. 配置全局映射器4. 测试映射器接口 三、详细步骤1、创建部门映射器接口2、创建映射器配置文件3、配置全局映射器4、测试映射器接口 四、结语 一、实战目标 在本实战课程中,我们将学…...
HarmonyOS第一课-应用程序框架基础习题答案
声明:本题库为最新的HarmonyOS第一课的学习题库,仅供参考学习! 一、判断题 1. 在基于Stage模型开发的应用项目中都存在一个app.json5配置文件、以及一个或多个module.json5配置文件。(正确) 正确(True) 错误(False) -…...
滚雪球学SpringCloud[10.2讲]:微服务项目的性能优化与调优
全文目录: 前言性能优化与调优概述性能优化的核心目标常见的性能瓶颈来源 性能瓶颈分析与调优策略1. 服务间通信优化优化策略: 2. 数据库优化优化策略: 3. 线程池优化优化策略: 4. 缓存优化优化策略: 常见问题的排查与解决1. 慢查…...
EasyExcel将数据库里面的数据生成excel文件
EasyExcel官方文档 1.在model模块导入依赖 <!-- 生成报表--> <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>4.0.3</version> </dependency> 2.修饰实体类 package…...
【YOLO学习】YOLOv1详解
文章目录 1. 概述2. 算法流程3. 网络结构4. 损失函数 1. 概述 1. YOLO 的全称是 You Only Look Once: Unified, Real-Time Object Detection。YOLOv1 的核心思想就是利用整张图作为网络的输入,直接在输出层回归 bounding box 的位置和 bounding box 所属的类别。简单…...
HarmonyOS应用开发(组件库)--组件模块化开发、工具包、设计模式(持续更新)
致力于,UI开发拿来即用,提高开发效率 常量格式枚举enum格式正则表达式...手机号校验...邮箱校验 文件判断文件是否存在 网络下载下载图片从沙箱中图片转为Base64格式从资源文件中读取图片转Base64 组件输入框...矩形输入框...输入框堆叠效果(…...
python测试开发---前后端交互Axios
Axios 是一个基于 Promise 的 HTTP 客户端,常用于浏览器和 Node.js 中发送 HTTP 请求。它封装了 XMLHttpRequest 和 Node.js 的 http 模块,使得处理网络请求更加简单和直观,尤其适合处理异步请求。以下是 Axios 的基础概念和使用方法…...
删除视频最后几帧 剪切视频
删除视频最后几帧 剪切视频 remove_last.py import subprocess def remove_last_frame(input_file, output_file, frame_rate):command_duration [ffprobe,-v, error,-show_entries, formatduration,-of, defaultnoprint_wrappers1:nokey1,input_file]try:total_duration fl…...
SSM框架学习(四、SpringMVC实战:构建高效表述层框架)
目录 一、SpringMVC简介和体验 1.介绍 2.主要作用 3.核心组件和调用流程理解 4.快速体验 二、SpringMVC接收数据 1.访问路径设置 (1)精准路径匹配 (2)模糊路径匹配 (3)类和方法上添加 RequestMapp…...
戴尔笔记本电脑——重装系统
说明:我的电脑是戴尔G3笔记本电脑。 第一步:按照正常的装系统步骤,配置并进入U盘的PE系统 如果进入PE系统,一部分的硬盘找不到,解决办法:U盘PE系统——出现部分硬盘找不到的解决办法 第二步:磁…...
领夹麦克风哪个品牌音质最好,主播一般用什么麦克风
在这个信息爆炸的时代,清晰的声音传达显得尤为重要。无论是激情澎湃的演讲,还是温馨动人的访谈,一款优质的无线领夹麦克风都能让声音清晰的传播。但市场上产品繁多,如何挑选出性价比高、性能卓越的无线领夹麦克风呢?本…...
华为静态路由(route-static)
静态路由的组成 在华为路由器中,使用ip route-static命令配置静态路由。 一条静态路由主要包含以下要素: 目的地址:数据包要到达的目标IP地址 子网掩码:用于指定目的地址的网络部分和主机部分 下一跳地址(可选&#…...
Focalboard开源项目管理系统本地Windows部署与远程访问协同办公
文章目录 前言1. 使用Docker本地部署Focalboard1.1 在Windows中安装 Docker1.2 使用Docker部署Focalboard 2. 安装Cpolar内网穿透工具3. 实现公网访问Focalboard4. 固定Focalboard公网地址 💡 推荐 前些天发现了一个巨牛的人工智能学习网站,通俗易懂&am…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...
如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
基于SpringBoot在线拍卖系统的设计和实现
摘 要 随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统,主要的模块包括管理员;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...
GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...
