【使用Spring事件监听机制实现业务解耦+相关拓展】

使用Spring事件监听机制实现业务解耦

  • 一.业务场景
  • 二.Spring事件监听机制是什么?
    • 1.相关概念
    • 2.解耦
  • 三.使用步骤
    • 1.定义事件
    • 2.发布事件
    • 3.侦听事件的对象
    • 4.测试结果
    • 5.有没有小伙伴们发现上面这种发布订阅有什么问题?
      • 代码改造:
        • 1. 自定义线程池:
        • 2. 侦听器改造
        • 3. ok,改造完毕
        • 4.核心源码分析
  • 四.观察者模式介绍
    • 1.基本概念:
    • 2.结合生活场景加深理解
  • 五.总结及拓展
    • 1.初始化操作、缓存填充
    • 2.结合策略模式使用

一.业务场景

假如咱们项目里面有这样一个场景,处理业务逻辑完需要发送一个邮件。
我们平常做法可以通过线程池提交一个异步任务或者通过CompletableFuture开启一个异步任务调用发送邮件方法,这里简单介绍一个新的方式来了解与使用Spring的事件发布与监听,了解这种机制有诸多方便!

二.Spring事件监听机制是什么?

1.相关概念

事件监听机制也是设计模式中观察者模式、发布-订阅模式的一种实现。
Spring的事件机制是A调用一个发布事件的方法,就能自动调用监听这个事件的方法B,这个过程有3个对象:

  • 事件本身,其实就是一个对象,这个对象对应的类要继承Spring的ApplicationEvent,这样才能被Spring识别。
  • 发布事件的对象,一般都是使用ApplicationContext的publishEvent方法来发布;
  • 接收(或者叫监听)事件的对象,实现ApplicationListener 接口或者使用@EventListener注解的方法;

2.解耦

通常我们利用消息队列来实现不同系统之间的解耦,如用户注册完成后,可以向消息队列发布一条消息,然后订阅了此topic的子系统(如邮件服务,积分服务)收到发布的消息之后,就会做相应的处理。这样做的好处是避免了在注册服务里耦合其他服务的代码,并且,执行子系统的业务将会异步执行,互不影响, 使用事件发布与监听机制也可以做业务解耦。

三.使用步骤

1.定义事件

定义邮件事件本身,只管邮件发送

public class MailEvent extends ApplicationEvent {public MailEvent(Object source) {super(source);}public void sendMail() {log.info("邮件发送中...");}
}

2.发布事件

模拟处理业务逻辑,然后通过我们的Spring容器发布这个邮件事件,其内部就是通过Spring的事件发布器发布的事件。

    @Autowiredprivate ApplicationContext applicationContext;@GetMapping("/pub-event")public void pub() {log.info("业务逻辑处理中....");applicationContext.publishEvent(new MailEvent(this));}

3.侦听事件的对象

  • 了解vue的小伙伴们应该知道watch侦听函数,侦听到对应的变量改变则触发侦听函数执行侦听器逻辑,一样的道理,这里是侦听器侦听到对应的事件则执行侦听器的逻辑。
  • 通过实现ApplicationListener接口或者使用@EventListener注解来定义侦听器,这里定义了4个侦听器,一个使用实现ApplicationListener
    接口的方式,另外3个使用@EventListener注解来定义侦听器的方式。
  • 用@EventListener注解定义的侦听器可以使用 @Order来指定侦听器的执行顺序,Order的值越小,优先级越高
  • 注意:‌实现ApplicationListener接口的侦听器不能使用@Order注解指定侦听器顺序。‌在Spring框架中,虽然@Order注解常用于指定Bean的优先级或排序,但在事件监听器的上下文中,@Order注解并不适用于ApplicationListener接口的实现类。这是因为ApplicationListener接口本身并不支持通过@Order注解来指定侦听器的顺序
@Component
@Slf4j
public class MailEventListener implements ApplicationListener<MailEvent> {// @Order(2) 不能这样使用,不生效的@Overridepublic void onApplicationEvent(MailEvent mailEvent) {log.info("侦听器2侦听到到邮件事件啦...");mailEvent.sendMail();}@Order(1)@EventListenerpublic void listener1(MailEvent mailEvent) {log.info("侦听器1侦听到邮件事件啦...");mailEvent.sendMail();}@Order(3)@EventListenerpublic void listener3(MailEvent mailEvent) {log.info("侦听器3侦听到邮件事件啦...");mailEvent.sendMail();}@Order(2)@EventListenerpublic void listener2(MailEvent mailEvent) {log.info("侦听器2侦听到邮件事件啦...当前执行线程:{}",Thread.currentThread().getName());mailEvent.sendMail();}
}

4.测试结果

在这里插入图片描述

5.有没有小伙伴们发现上面这种发布订阅有什么问题?

没错,咱们上面的发布订阅是同步机制,也就是前面一个事件出错了,后面的事件不会执行了,程序异常中断了。
在这里插入图片描述
正常咱们的发布订阅,例如消息队列这些,生产端发布了消息,消费端失败了关我生产者什么事?像我们举例的业务场景处理完业务再发邮件,发送邮件失败了肯定不能够影响主业务进行的。所以发邮件这种肯定得异步处理,ok,咱们引入侦听器端异步处理。

代码改造:

1. 自定义线程池:

关于核心线程数、最大线程数,任务队列(阻塞队列)选择,后续有时间可以出一期文章,设置tomcat最大线程数结合压测来设置最合适的核心线程数、最大线程数,这里先随便设置一下。

@Configuration
@EnableAsync
public class ThreadPoolConfiguration {@Bean("commonPool")public ExecutorService commonThreadPoolExecutor(){return new TulingMallThreadPoolExecutor("测试公共线程池",16,50).getLhrmsThreadPoolExecutor();}
}
2. 侦听器改造

使用异步,都使用异步的方式了,就让执行各个侦听器的线程竞争好了,@Order注解完全没意义了,无非是画蛇添足,在同步的情况下,限定侦听器执行顺序才有意义,个人理解~

我看百度AI说@Async 和 @Order注解一起使用,@Order注解还能生效,我测了其实是不行的,也没见人有这样一起使用的。

@Component
@Slf4j
public class MailEventListener {@Autowiredprivate ThreadPoolTaskExecutor commonPool;// commonPool是我自定义线程池命名的bean名称@Async("commonPool")@EventListenerpublic void listener1(MailEvent mailEvent) {log.info("侦听器1侦听到邮件事件啦...当前执行线程:{}",Thread.currentThread().getName());mailEvent.sendMail();throw new RuntimeException("侦听器1异常啦....");}@Async("commonPool")@EventListenerpublic void listener3(MailEvent mailEvent) {log.info("侦听器3侦听到邮件事件啦...当前执行线程:{}",Thread.currentThread().getName());mailEvent.sendMail();}@Async("commonPool")@EventListenerpublic void listener2(MailEvent mailEvent) {log.info("侦听器2侦听到邮件事件啦...当前执行线程:{}",Thread.currentThread().getName());mailEvent.sendMail();}
}
3. ok,改造完毕

再一次测试,可以看到线程名称都是自定义线程池里面的,然后咱们listener1抛出异常,不影响程序继续运行,其他侦听器照样发邮件,这时候咱们在业务层只需要考虑下消费失败场景如何处理等等,这种就是咱们业务上需要考虑的补偿方案了。
在这里插入图片描述

4.核心源码分析

EventListenerMethodProcessor类的processBean方法
在这里插入图片描述
侦听器集合是在AbstractApplicationContext中定义的:private final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
其实不就是咱们学习设计模式时候的观察者模式嘛! 看了以上思路,咱们就算脱离Spring实现一套事件侦听机制是不是也有思路了呢?

四.观察者模式介绍

1.基本概念:

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。在这种模式中,一个目标对象(被观察者)管理所有依赖于它的观察者对象,并在其状态改变时主动通知这些观察者, 观察者模式又叫发布-订阅(Publish/Subscribe)模式。

2.结合生活场景加深理解

万事不离其宗,设计来源于生活,正如生活场景:老师有电话号码,学生需要知道老师的电话号码以便于在需要的时候拨打,在这样的组合中,老师就是一个被观察者(Subject),学生就是需要知道信息的观察者,老师需要管理所有的学生对象,当老师的电话号码发生改变时,学生得到通知,并更新相应的电话记录。 是不是跟咱们的Spring事件侦听机制如出一辙呢?

五.总结及拓展

使用Spring事件发布及事件侦听,可以很好的做业务解耦等等,还有很多好用的场景:

1.初始化操作、缓存填充

例如可以利用Spring的容器刷新事件(ContextRefreshedEvent)是在容器完全刷新后发布的。这个事件可以被侦听器侦听,以便执行一些操作,如数据库的初始化、缓存的填充等

2.结合策略模式使用

  • 我们可以侦听这个事件,拿到里面的我们自定义注解bean,结合我们的策略再调用具体的实现等等,我在我当前公司项目里面也使用了Spring的事件侦听机制结合策略 +模板方法的设计模式来实现需求,对于拓展上诸多便利,同事都夸牛逼,只能说优雅~ 之后有时间我会出一篇文章供大家参考设计思路。

好啦,相信小伙伴们对Spring的事件侦听机制有简单了解了,晚安。

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

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

相关文章

[极客大挑战 2019]EasySQL1

前言&#xff1a; 记录一下web方面的题(第一次接触。。。&#xff09; 学校课程要学web…… - - 行吧&#xff0c;尝试一下&#xff0c;至少学过MySQL。。。 不过&#xff0c;实际上&#xff0c;现实现在SQL漏洞少得可怜&#xff0c;但学习不会有错。 参考&#xff1a;&…

《 C++ 修炼全景指南:十二 》用红黑树加速你的代码!C++ Set 和 Map 容器从入门到精通

摘要 本文详细介绍了基于红黑树实现的 Set 和 Map 容器&#xff0c;包括其底层设计原理、插入和删除操作的实现细节、性能分析与优化策略&#xff0c;以及实际应用场景和未来发展方向。通过采用红黑树的数据结构&#xff0c;Set 和 Map 容器能够高效地处理有序数据&#xff0c…

【笔记】自动驾驶预测与决策规划_Part4_时空联合规划

文章目录 0. 前言1. 时空联合规划的基本概念1.1 时空分离方法1.2 时空联合方法 2.基于搜索的时空联合规划 &#xff08;Hybrid A* &#xff09;2.1 基于Hybrid A* 的时空联合规划建模2.2 构建三维时空联合地图2.3 基于Hybrid A*的时空节点扩展2.4 Hybrid A* &#xff1a;时空节…

多颜色绘制语义分割/变化检测结果图

在论文绘图时&#xff0c;传统的二元语义分割结果图颜色单一&#xff08;下图左&#xff09;&#xff0c;所以论文中常根据混淆矩阵类别使用多颜色进行绘制&#xff08;下图右&#xff09;&#xff0c;可以看到&#xff0c;结果的可视化效果更好。 以下是绘制代码&#xff1a; …

**CentOS7安装配置mysql**

CentOS7安装配置mysql 首先先将mysql57-community-release-el7.rpm解压出来 rpm -ivh mysql57-community-release-el7.rpmls /etc/yum.repos.d/ -l // 检查是否解压成功安装mysql yum install -y mysql-community-server可能会出现 GPG 密钥过期 rpm --import https://r…

OpenHarmony(鸿蒙南向)——平台驱动开发【MIPI DSI】

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 鸿蒙&#xff08;OpenHarmony&#xff09;南向开发保姆级知识点汇总~ 持续更新中…… 概述 功能简介 DSI&#xff08;Display Serial Interface&#x…

2024年9月23日---关于MyBatis框架(2)

4.7 不同返回值类型的查询 4.7.1 返回基本数据类型 /**查询student表中的记录个数 */ int selectCount(); <select id"selectCount" resultType"_int">select count(*) from student; </select> 4.7.2 返回引用类型(实体类) /**返回值为实…

智能泡茶设备控制系统设计-设计说明书

设计摘要&#xff1a; 智能泡茶设备控制系统设计旨在实现对泡茶过程的自动化控制&#xff0c;提升泡茶的便利性和稳定性。本系统采用嵌入式技术与传感器相结合&#xff0c;实现对泡茶设备的全方位监控和控制。关键功能包括水温控制、浸泡时间控制、浸泡压力控制、茶水浓度控制…

电信、移动、联调等运营商都有那些国产化自研软件

国产化自研软件方面有着积极的探索和实践&#xff0c;包括操作系统、数据库和中间件等&#xff0c;电信运营商在国产化软件方面取得了显著进展&#xff1a; 操作系统&#xff1a; 中国电信推出了基于华为欧拉openEuler开源系统的天翼云操作系统CTyunOS&#xff0c;已上线部署5万…

基于JAVA+SpringBoot+Vue的医院后台管理系统

基于JAVASpringBootVue的医院后台管理系统 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末附源码下载链接&#x1f345; 哈…

DBeaver启动报错 Faild to load the JNI shared library

DBeaver启动报错 Faild to load the JNI shared library 问题现象 安装完成后&#xff0c;启动dbeaver报错 查看版本为64位版本&#xff0c;JAVA也为64为版本 dbeaver版本 java版本 解决 在dberver.ini添加指定配置&#xff0c;即可启动成功

HarmonyOS NEXT:解密从概念到实践的技术创新与应用前景

HarmonyOS是目前华为手机所搭载的鸿蒙系统&#xff0c;它在Open Harmony的基础上兼容了安卓的AOSP&#xff0c;所以可以使用安卓APK应用&#xff0c;HarmonyOS属于华为在当前阶段过渡使用的系统&#xff0c;原生鸿蒙的应用生态尚未发展起来&#xff0c;兼容安卓应用可以让用户有…

Spring源码-ConfigurationClassPostProcessor类解析spring相关注解

ConfigurationClassPostProcessor类的作用 此类是一个后置处理器的类&#xff0c;主要功能是参与BeanFactory的建造&#xff0c;主要功能如下 1、解析加了Configuration的配置类 2、解析ComponentScan扫描的包 3、解析ComponentScans扫描的包 4、解析Import注解 该类在springbo…

【web开发】Spring Boot 快速搭建Web项目(三)

Date: 2024.08.31 18:01:20 author: lijianzhan 简述&#xff1a;根据上篇原文Spring Boot 快速搭建Web项目&#xff08;二&#xff09;&#xff0c;由于已经搭建好项目初始的框架&#xff0c;以及自动创建了一个启动类文件&#xff08;TestWebApplication.java&#xff09; …

带你一文了解CISP-PTE的用处

CISP-PTE认证是由中国信息安全测评中心颁发的国家级专业证书&#xff0c;专注于培养和考核网络安全渗透测试方面的高级应用安全人才。CISP-PTE认证的目的是提升个人在信息安全领域的技术水平&#xff0c;特别是在渗透测试方面。 一、CISP-PTE的重要性 1.提升职业竞争力 CISP-PT…

华为云发布全栈可观测平台AOM,以AI赋能应用运维可观测

9月19日&#xff0c;华为全联接大会2024举办期间&#xff0c;在“AI赋能应用现代化&#xff0c;加速软件生产力跃升”为主题的论坛上&#xff0c;华为云发布全栈可观测平台AOM&#xff0c;以AI赋能应用运维可观测&#xff0c;提升企业应用可用性与稳定性。 该平台发布标志着华…

MacOS上安装MiniConda的详细步骤

前言 MiniConda是一种环境配置工具。在不同的开发项目中&#xff0c;我们会使用到不同版本的Python和第三方库&#xff08;例如Numpy、Pandas)。如果不使用环境配置工具&#xff0c;每次开发都需要清除电脑里上一次开发的环境和配置文件。为了在同一台机器上同时开发多个项目&…

Redis面试笔记

1.什么是Redis 简单来说 redis 就是一个数据库&#xff0c;不过与传统数据库不同的是 redis 的数据是存在内存中的&#xff0c;所以读写速度非常快&#xff0c;因此 redis 被广泛应用于缓存方向。另外&#xff0c;redis 也经常用来做分布式锁。redis 提供了多种数据类型来支持…

Leetcode尊享面试100题-252.会议室

给定一个会议时间安排的数组 intervals &#xff0c;每个会议时间都会包括开始和结束的时间 intervals[i] [starti, endi] &#xff0c;请你判断一个人是否能够参加这里面的全部会议。 示例 1&#xff1a; 输入&#xff1a;intervals [[0,30],[5,10],[15,20]] 输出&#xff…

R包:VennDiagram韦恩图

加载R包 library(VennDiagram)数据 # Prepare character vectors v1 <- c("DKK1", "NPC1", "NAPG", "ERG", "VHL", "BTD", "MALL", "HAUS1") v2 <- c("SMAD4", "DKK1…