SpringEvent扩展性利器

news2024/11/25 14:40:03

使用Spring Event机制可以保证高扩展性:

在这里插入图片描述

使用Spring Event来发布应用内部领域事件,对于事件监听器可通过注解或类的方式来扩展,Spring
Event内部使用观察者模式,但api使用层面可以完全解耦事件发布和事件监听:

在这里插入图片描述

常用方式:

@Component
@Slf4j
public class ClazzHourEventListener {
    
    // 默认同步调用该方法,@Order注解编排顺序
    @EventListener
    @Order
    public void listener1(ClazzHourDepletedMemEvent event) {
        // ...
    }
    
    
    // @Async注解实现异步调用
    @Async
    @EventListener
    public void listener2(ClazzHourDepletedMemEvent event) {
        // ...
    }
    
    // 事务监听,默认在事务提交后同步执行该方法
    @TransactionalEventListener
    public void listener3(ClazzHourDepletedMemEvent event) {
        // ...
    }
}

注解实现事件监听需要考虑一下三个方面的内容:

  • 异步:如何实现异步
  • 事务:调用者的事务和监听器事务关系,包括异步情况下
  • 异常处理:异常需要捕获吗?对事务有什么影响

测试如下:

@Service
public class BizService {

    @Autowired
    StudentMapper mapper;

    @Autowired
    ApplicationEventPublisher publisher;

    @Transactional
    public void bizAction(){
        Student student = new Student();
        student.setId(120L);
        student.setName("test1");
        mapper.insert(student);

        System.out.println("bizAction +"+Thread.currentThread().getName());
        // 发布时间
        publisher.publishEvent(new TestEvent());
    }

}

@Component
public class TestTxListener {

    @Autowired
    StudentMapper studentMapper;

    // 该步抛异常会导致后续listener无法运行
    // 该步事务和BizService中是一个,抛异常会同时回滚
    // @Order(2) 
    @EventListener
    public void listener1(TestEvent event){
        System.out.println("TestTxListener 1 +"+Thread.currentThread().getName());

        Student student = new Student();
        student.setId(121L);
        student.setName("同步调用测试tx");
        studentMapper.insert(student);
        // throw new RuntimeException();

    }


    // 异步 情况下 该步事务和BizService中不是一个,这里抛异常不影响其他listenr执行,也不影响BizService事务
    // 如果order优先级高的同步listener抛异常,这里也会执行不到
    // @Order(1)
    @Async("testTPE")
    @EventListener
    public void listener2(TestEvent event){
        System.out.println("TestTxListener 2 +"+Thread.currentThread().getName());

        Student student = new Student();
        student.setId(122L);
        student.setName("异步调用测试tx");
        studentMapper.insert(student);
        
        // throw new RuntimeException();
    }
}


@Component
public class TestTxListener {

    @Autowired
    StudentMapper studentMapper;

    // 默认同步调用,这里面跑出异常不影响其他TransactionalEventListener执行
    // TransactionalEventListener 使用 TransactionSynchronization实现
    // 这里的事务需要指定REQUIRES_NEW,或者使用编程式事务。否则无法提交,详见TransactionSynchronization    
    // The transaction will have been committed already, but the 
    // transactional resources might still be active and accessible. 
    // As a consequence, any data access code triggered at this point 
    // will still "participate" in the original transaction, allowing 
    // to perform some cleanup (with no commit following anymore!), 
    // unless it explicitly declares that it needs to run in a 
    // separate transaction. Hence: Use PROPAGATION_REQUIRES_NEW for 
    // any transactional operation that is called from here.
    // 但是,如果使用@Async,就没有这个问题,因为事务是绑定线程的,多线程propogation无作用
    @Transactional(propogation=REQUIRES_NEW)
    @Order(1)
    @TransactionalEventListener
    public void listener1(TestEvent event){
        // 同步调用
        System.out.println("TestTxListener 1 +"+Thread.currentThread().getName());


        Student student = new Student();
        student.setId(161L);
        student.setName("同步调用测试tx");
        studentMapper.insert(student);
        // 不影响其他listener执行
        throw new RuntimeException();

    }


    @Order(2)
    // @Async 可异步
    @TransactionalEventListener
    public void listener2(TestEvent event){
        System.out.println("TestTxListener 2 +"+Thread.currentThread().getName());

        Student student = new Student();
        student.setId(162L);
        student.setName("异步调用测试tx");
        studentMapper.insert(student);
    }
}

由此可见 EventListener
执行过程中遇到异常终止,则后续的同步&异步EventListener都不会执行(之前的会执行,使用@Order控制顺序),而TransactionalEventListener相互之间不受影响。所以使用EventListener要做好异常处理。此外TransactionalEventListener方法内使用事务(默认afterCommit)需要注明@Transactional(
propogation=REQUIRES_NEW)或使用编程式事务,但是如果@Async异步时,就不需要指定,因为事务是绑定线程的。

ps:

org.springframework.modulith.events.ApplicationModuleListener
 
@Async
@Transactional(propagation=REQUIRES_NEW)
@TransactionalEventListener
@Documented
@Target({METHOD,ANNOTATION_TYPE})
@Retention(RUNTIME)
public @interface ApplicationModuleListener{
    @AliasFor(annotation=org.springframework.context.event.EventListener.class,attribute="id")
    String id();
    @AliasFor(annotation=org.springframework.transaction.annotation.Transactional.class,attribute="readOnly")
    boolean readOnlyTransaction();
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

建筑驱鸟设备 | 建筑专用超声波驱鸟器

从半夜的鸣叫到频繁的鸟粪污染,鸟类活动有时会成为城市居民不得不面对的小小困扰。通过合理的驱鸟方法,我们可以有效地减少鸟类对建筑物的侵扰,保护建筑物的完好和安全,同时维护城市居民的生活质量。 建筑专用超声波驱鸟器&#x…

spl序表字段批量修改

如果没有条件,全量修改,使用run 如果没有条件,是可以直接使用run函数,对指定一个列的所有都操作,但是没有任何条件 如果使用了筛选条件,使用了select函数,会返回一个被筛选并且修改的序表(有条件…

ai assistant激活成功后,如何使用

ai assistant激活成功后,如图 ai assistant渠道:https://web.52shizhan.cn/activity/ai-assistant 在去年五月份的 Google I/O 2023 上,Google 为 Android Studio 推出了 Studio Bot 功能,使用了谷歌编码基础模型 Codey,Codey 是…

【贪心算法初级训练】在花坛上是否能种下n朵花、碰撞后剩余的行星

1、在花坛上是否能种下n多花 一个很长的花坛,一部分地已经种植了花,另一部分却没有,花不能种植在相邻的地块上否则它们会争夺水源,两者都会死去。给你一个整数数组表示花坛,由若干个0和1组成,0表示没种植花…

ThreadLocal的使用

ThreadLocal 一般不会单独使用,基本上都是 放在一个工具类中,然后在拦截器中去使用(至少存储 Token 的时候是这样的) 这里为了更加还原真实的线上环境,直接就用了 拦截器统一返回全局异常捕获ThreadLocal,…

生信算法8 - HGVS转换与氨基酸字母表

HGVS 概念 HGVS 人类基因组变异协会(Human Genome Variation Society)提出的转录本编号,cDNA 参考序列(以前缀“c.”表示)、氨基酸参考序列(以前缀“p.”表示)。cDNA 中一种碱基被另一种碱基取代,以“>”进行表示,如:c.2186A&…

软件测试之解构单元测试

软件单元测试是对软件中的最小可测试单元进行检查和验证的过程。这些单元可以是函数、方法、类实例,或者是任何具有明确功能、规格定义和接口定义的程序代码模块。单元测试是软件开发过程中的最低级别的测试活动,它确保软件的独立单元在与程序的其他部分…

Jar打包成Service在Window运行

直接启动 bat脚本直接启动jar包 Spring Boot 部署在Windows Service启动 环境 电脑环境需要安装Microsoft.NETFramework 4 Microsoft.NETFramework 4 打包 WinSW 下载exe和xml配置文件,将exe和xml放在同一个目录下,并且重命名为一样的名字&…

Ideogram-免费使用的 AI 工具,可以生成逼真的图像、海报、徽标

工具来源:Ideogram | AI工具箱 什么是Ideogram AI? Ideogram AI是一款高效的工具,旨在将文本与AI生成的图像结合在一起。该应用程序提供了用户友好的界面,使您能够轻松地制作出色的艺术作品、标志和设计。 与传统工具不同,Ideogram AI因其能够以无与伦比的简便和速度将…

Git 查看当前分支是基于哪个分支拉取(源头分支)

场景: 项目中使用 Git 管理代码仓库的时候,随着项目的持续迭代及项目的扩展,多版本并行开发是非常常见的事情,多版本并行开发就伴随着多分支,随着 Git 的分支越拉越多,这时候很容易造成分支的混乱&#xf…

webgis 之 地图投影

地图投影 什么是地图投影目的种类等角投影的分类墨卡托投影Web 墨卡托投影 参考小结 为了更好地展示地球上的数据,需要将地球投影到一个平面上。地图投影是一个数学问题,按照一定的几何关系,将地球上的经纬度坐标映射到一个平面上的坐标。地球…

由 Vault 支持的 KES 的 MinIO Operator

为了提供安全锁定和擦除的合规性功能,MinIO 使用服务器端加密 (SSE) 在存储层加密对象,以保护对象作为写入操作的一部分。MinIO 以极高的效率做到这一点——基准测试表明 MinIO 能够以接近线速进行加密/解密。 MinIO 使用的秘诀是…

【头歌】HBase扫描与过滤答案 解除复制粘贴限制

解除复制粘贴限制 当作者遇到这个限制的时候火气起来了三分,然后去网上搜索答案,然后发现了一位【碳烤小肥肠】居然不贴代码,XX链接,贴截图,瞬时火气冲顶,怒写此文 首先启动万能的控制台,然后C…

帕金森综合征的预防方法

帕金森综合征是一种慢性神经退行性疾病,目前尚无法彻底治愈。然而,通过采取一些预防措施,可以降低患病风险或延缓病情发展。以下是一些基于最新研究和医学建议的预防方法: 健康饮食:保持低盐、低脂饮食,多吃…

SQL新手蜕变:掌握这20条常用SQL语句,让你也能成为高手!

序言 在现代软件开发中,SQL(Structured Query Language,结构化查询语言)作为与数据库交互的标准编程语言,是每个开发者必学的基础技能。掌握SQL并在数据库管理与数据分析中应用自如,能显著提升开发效率和数…

语音相关算法学习整理

最近看了一下百度paddlespeech的一些公开课,把课程里的视频内容大体听了一下,现在整理一下笔记。教程链接见:飞桨AI Studio星河社区-人工智能学习与实训社区 语音识别的过程可以这样简单概括: 将声音信号经过预加重、加窗、fft等…

蓝牙数传芯片TD5325A,蓝牙5.1—拓达半导体

拓达TD5325A芯片是一款支持蓝牙BLE&SPP的纯数传芯片,蓝牙5.1版本。芯片的亮点在于性能强,支持APP端直接对芯片做设置与查询操作,包括修改蓝牙名、UUID、MAC地址,以及直接操作蓝牙芯片自身的IO与PWM口,还包括支持简…

开发产品要遵循这些「关键规则」

目录 简介 关键规则 第一点:了解产品的操作使用环境 第二点:尽可能计划将来的功能 第三点:静电 ESD 保护 第四点:尽早考虑 BOM 成本 第五点:开发文件管理(原理图、BOM、代码等) 产品资源…

如何使用Excel与Outlook实现邮件群发:详细教程

引言 在工作中,我们经常需要发送大量邮件。手动发送既费时又容易出错。本教程将教你如何使用Excel和Outlook,通过简单的VBA代码实现邮件的自动群发,提高工作效率。 准备工作 在开始之前,你需要确保以下工具已经安装在电脑上&am…

1969python房屋租赁管理系统mysql数据库Flask结构BootStrap布局计算机软件工程网页

一、源码特点 python Flask房屋租赁管理系统是一套完善的web设计系统mysql数据库 ,对理解python编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。 python flask 房屋租赁管理系统 开发环境pycharm mysql …