【Spring事务】Spring事务事件控制,解决业务异步操作

news/2024/2/21 3:39:27

使用背景

在业务中,经常会有这样的需求,在数据库事务提交之后,发送异步消息或者进行其他的事务操作。

例如当用户注册成功之后,发送激活码,如果用户注册后就执行发送激活码,但是在用户保存时出现提交事务异常,数据库进行回滚,用户实际没有注册成功,但是用户却收到了激活码,此时,正确的是应该在用户注册保存事务提交完成之后,然后发送激活码。

标题复制10行,并且每行大于10个字符【Spring事务】Spring事务事件控制,解决业务异步操作【Spring事务】Spring事务事件控制,解决业务异步操作,并且每行大于10个字符【Spring事务】Spring事务事件控制,解决业务异步操作

使用注解@TransactionalEventListener

demo展示

事务监听器

@Component
public class TransactionListener {@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)public void handler(TransactionEvent transactionEvent) {System.out.println(transactionEvent.getSource());}
}

业务代码

@Override
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
public void saveUser() {User user = new User();userMapper.insert(user);eventPublisher.publishEvent(newTransactionEvent("事务提交后发布事件1"));
}
标题复制10行,并且每行大于10个字符【Spring事务】Spring事务事件控制,解决业务异步操作【Spring事务】Spring事务事件控制,解决业务异步操作,并且每行大于10个字符【Spring事务】Spring事务事件控制,解决业务异步操作

源码解析

EventListenerMethodProcessor

EventListenerMethodProcessor用来解析带有带有@EventListener注解的方法。遍历类上的方法,判断工厂是否支持,用对应的工厂生成监听器。

	private void processBean(final String beanName, final Class<?> targetType) {if (!this.nonAnnotatedClasses.contains(targetType) &&AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&!isSpringContainerClass(targetType)) {//...if (CollectionUtils.isEmpty(annotatedMethods)) {//...}else {// Non-empty set of methodsConfigurableApplicationContext context = this.applicationContext;Assert.state(context != null, "No ApplicationContext set");List<EventListenerFactory> factories = this.eventListenerFactories;Assert.state(factories != null, "EventListenerFactory List not initialized");for (Method method : annotatedMethods.keySet()) {for (EventListenerFactory factory : factories) {if (factory.supportsMethod(method)) {Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));ApplicationListener<?> applicationListener =factory.createApplicationListener(beanName, targetType, methodToUse);if (applicationListener instanceof ApplicationListenerMethodAdapter) {((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);}context.addApplicationListener(applicationListener);break;}}}if (logger.isDebugEnabled()) {logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +beanName + "': " + annotatedMethods);}}}}

TransactionalEventListenerFactory仅支持TransactionalEventListener注解,生成ApplicationListenerMethodTransactionalAdapter的对象。

public class TransactionalEventListenerFactory implements EventListenerFactory, Ordered {private int order = 50;public TransactionalEventListenerFactory() {}public void setOrder(int order) {this.order = order;}public int getOrder() {return this.order;}public boolean supportsMethod(Method method) {return AnnotatedElementUtils.hasAnnotation(method, TransactionalEventListener.class);}public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {return new ApplicationListenerMethodTransactionalAdapter(beanName, type, method);}
}

AbstractTransactionManagementConfiguration会引入TransactionalEventListenerFactory

    @Bean(name = {"org.springframework.transaction.config.internalTransactionalEventListenerFactory"})@Role(2)public static TransactionalEventListenerFactory transactionalEventListenerFactory() {return new TransactionalEventListenerFactory();}
标题复制10行,并且每行大于10个字符【Spring事务】Spring事务事件控制,解决业务异步操作【Spring事务】Spring事务事件控制,解决业务异步操作,并且每行大于10个字符【Spring事务】Spring事务事件控制,解决业务异步操作

ApplicationListenerMethodTransactionalAdapter

发布事件,主要是创建了TransactionSynchronization,注册到了TransactionSynchronizationManager

    public void onApplicationEvent(ApplicationEvent event) {if (TransactionSynchronizationManager.isSynchronizationActive() && TransactionSynchronizationManager.isActualTransactionActive()) {TransactionSynchronization transactionSynchronization = this.createTransactionSynchronization(event);TransactionSynchronizationManager.registerSynchronization(transactionSynchronization);} else if (this.annotation.fallbackExecution()) {if (this.annotation.phase() == TransactionPhase.AFTER_ROLLBACK && this.logger.isWarnEnabled()) {this.logger.warn("Processing " + event + " as a fallback execution on AFTER_ROLLBACK phase");}this.processEvent(event);} else if (this.logger.isDebugEnabled()) {this.logger.debug("No transaction is active - skipping " + event);}}
标题复制10行,并且每行大于10个字符【Spring事务】Spring事务事件控制,解决业务异步操作【Spring事务】Spring事务事件控制,解决业务异步操作,并且每行大于10个字符【Spring事务】Spring事务事件控制,解决业务异步操作

事务提交

TransactionSynchronizationUtils#invokeAfterCompletion,事务提交会遍历TransactionSynchronization执行afterCompletion方法

    public static void invokeAfterCompletion(@Nullable List<TransactionSynchronization> synchronizations, int completionStatus) {if (synchronizations != null) {Iterator var2 = synchronizations.iterator();while(var2.hasNext()) {TransactionSynchronization synchronization = (TransactionSynchronization)var2.next();try {synchronization.afterCompletion(completionStatus);} catch (Throwable var5) {logger.error("TransactionSynchronization.afterCompletion threw exception", var5);}}}

ApplicationListenerMethodTransactionalAdapter.TransactionSynchronizationEventAdapter#afterCompletion,调用事件监听器的processEvent方法,会反射调用被@TransactionalEventListener修饰的方法。

        public void afterCompletion(int status) {if (this.phase == TransactionPhase.AFTER_COMMIT && status == 0) {this.processEvent();} else if (this.phase == TransactionPhase.AFTER_ROLLBACK && status == 1) {this.processEvent();} else if (this.phase == TransactionPhase.AFTER_COMPLETION) {this.processEvent();}}protected void processEvent() {this.listener.processEvent(this.event);}
标题复制10行,并且每行大于10个字符【Spring事务】Spring事务事件控制,解决业务异步操作【Spring事务】Spring事务事件控制,解决业务异步操作,并且每行大于10个字符【Spring事务】Spring事务事件控制,解决业务异步操作

使用TransactionSynchronizationManager TransactionSynchronizationAdapter

demo展示

@Autowired
private UserDao userDao;
@Autowired
private JmsProducer jmsProducer;public User saveUser(User user) {// 保存用户userDao.save(user);final int userId = user.getId();// 兼容无论是否有事务if(TransactionSynchronizationManager.isActualTransactionActive()) {TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {@Overridepublic void afterCommit() {jmsProducer.sendEmail(userId);}});} else {jmsProducer.sendEmail(userId);}
}
标题复制10行,并且每行大于10个字符【Spring事务】Spring事务事件控制,解决业务异步操作【Spring事务】Spring事务事件控制,解决业务异步操作,并且每行大于10个字符【Spring事务】Spring事务事件控制,解决业务异步操作

在这里插入图片描述

标题复制10行,并且每行大于10个字符【Spring事务】Spring事务事件控制,解决业务异步操作【Spring事务】Spring事务事件控制,解决业务异步操作,并且每行大于10个字符【Spring事务】Spring事务事件控制,解决业务异步操作


https://www.xjx100.cn/news/306131.html

相关文章

Ninject使用教程

.net framework 下可以使用 Ninject 作为 DI 容器. 关于构造函数和属性注入的对比 不推荐属性注入的主要原因是: 测试困难: 属性注入导致依赖硬编码在类中,不能通过构造函数正确初始化,允许测试。顺序依赖: 当一个类依赖于多个属性时,它们的初始化顺序可能存在依赖关系。但是属…

MySQL- 存储引擎

MySQL体系结构 连接层 最上层是一些客户端和链接服务&#xff0c;包含本地sock 通信和大多数基于客户端/服务端工具实现的类似于 TCP/IP的通信。主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程 池的概念&#xff0c;为通过认证安全接入的客户端…

更多的 bash shell 命令

章节目录&#xff1a; 一、监测进程1.1 探查进程1.2 实时监测进程1.3 结束进程 二、监测磁盘空间2.1 挂载存储设备2.2 使用 df 命令2.3 使用 du 命令 三、处理数据文件3.1 数据排序3.2 数据搜索3.3 数据压缩3.4 数据归档 四、结束语 本章内容&#xff1a; 进程管理获取磁盘统计…

Java如何判空

目录 前言 List的判空 String的判空 Optional 4.1 Optional对象的创建 4.2使用场景 前言 实际项目中我们会有很多地方需要判空校验&#xff0c;如果不做判空校验则可能产生NullPointerException异常。 针对异常的处理我们在上一篇有提及&#xff1a; 先来看一下实际项目…

把Kubernetes用于微服务管理的最佳实践

把Kubernetes用于微服务管理的最佳实践 一、 引言1 什么是 Kubernetes2 Kubernetes 的优势和应用场景3 为什么使用 Kubernetes 部署容器化应用程序 二、 准备工作1 安装 Kubernetes2 准备容器镜像3 准备 Kubernetes 配置文件 三、 部署应用程序1 创建 Kubernetes 命名空间2 创建…

Solidity中的函数和事件

Solidity是一种用于编写智能合约的编程语言&#xff0c;它是以太坊平台上最常用的语言之一。在Solidity中&#xff0c;函数和事件是合约的两个关键组成部分。函数用于定义合约中的行为和逻辑&#xff0c;而事件则用于实现合约与外部应用程序之间的通信和交互。本文将深入探讨So…

【mongoDB】mongodb权限验证 || mongodb重启 || mongodb常用命令

mongodb版本号 6.0 前言 mongoDB刚开始无需密码登录mongoDB有3默认数据库&#xff0c;分别为&#xff1a; admin 超级用户&#xff0c;能对所有数据库操作&#xff0c;执行管理员命令config 分片集群配置的数据库local 分片集群锁信息的集合test 这个数据库一般是隐式创建的&…

辉哥带你学hive第一讲 hive基本介绍以及环境搭建

文章目录 1.Hive介绍1.1 hive 基本情况1.2 Hive架构原理1.3 Hive 安装1.4 元数据配置1.5 hive 服务部署1.6Hive常用交互命令1.6.1 Hive参数配置方式1.Hive介绍 1.1 hive 基本情况 hive基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张表。 Hive是一个Hadoop客…