SpringBoot - 事件机制使用详解(ApplicationEvent、ApplicationListener)

SpringBoot - 事件机制使用详解(ApplicationEvent、ApplicationListener)

Spring 事件机制使用观察者模式来传递事件和消息。我们可以使用 ApplicationEvent 类来发布事件,然后使用 ApplicationListener 接口来监听事件。当事件发生时,所有注册的 ApplicationListener 都会得到通知。事件用于在松散耦合的组件之间交换信息。由于发布者和订阅者之间没有直接耦合,因此可以在不影响发布者的情况下修改订阅者,反之亦然。下面通过样例样式事件机制的使用。

1,基本用法

(1)首先我们创建一个自定义事件类 MyEvent,该类继承自 ApplicationEvent 类。

// 自定义事件类
public class MyEvent extends ApplicationEvent {private String message;public MyEvent(Object source, String message) {super(source);this.message = message;}public String getMessage() {return message;}
}

(2)接着定义一个事件监听器类 MyEventListener,该类实现 ApplicationListener 接口,并注册为 Spring 的组件。只要监听器对象在 Spring 应用程序上下文中注册,它就会接收事件。当 Spring 路由一个事件时,它使用监听器的签名来确定它是否与事件匹配。

// 事件监听器
@Component
public class MyEventListener implements ApplicationListener<MyEvent> {// 事件发生时执行@Overridepublic void onApplicationEvent(MyEvent event) {System.out.println("接收到事件: " + event.getMessage());// 模拟事件处理try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}}
}

(3)最后我们可以使用 ApplicationContext 的 publishEvent 方法来发布事件。

@RestController
public class HelloController {@Autowiredprivate ApplicationContext context;@GetMapping("/hello")public void hello() {System.out.println("准备发送事件");context.publishEvent(new MyEvent(this, "welcome to hangge.com"));System.out.println("事件发送完毕");}
}

(4)启动项目测试一下,我们访问 /hello 接口时,控制台输出如下,说明事件机制运行成功。

  • 注意:spring 事件是同步的,这意味着发布者线程将阻塞,直到所有监听都完成对事件的处理为止。

在这里插入图片描述

2,使用 @EventListener 监听事件

(1)上面样例我们通过实现 ApplicationListener 接口来定义监听器。从 Spring 4.1 开始,可以使用 @EventListener 注解的方法,以自动注册与该方法签名匹配的 ApplicationListener(监听器类同样需要注册为 Spring 的组件)。下面代码的效果同上面是一样的:

// 事件监听器
@Component
public class MyEventListener{// 使用注解实现事件监听@EventListenerpublic void onApplicationEvent(MyEvent event) {System.out.println("接收到事件: " + event.getMessage());// 模拟事件处理try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}}
}

(2)我们可以使用 @EventListener 注解的 value 属性来指定我们要监听的事件类型。比如下面代码来监听 MyEvent 类型的事件:
如果需要同时指定多个事件类型可以这么写:@EventListener({MyEvent.class,AnotherEvent.class})。

// 事件监听器
@Component
public class MyEventListener{// 使用注解实现事件监听@EventListener(MyEvent.class)public void onApplicationEvent(MyEvent event) {System.out.println("接收到事件: " + event.getMessage());}
}

(3)我们可以使用 @EventListener 注解的 condition 属性来指定事件监听器的执行条件。在下面的代码中,#event.message == ‘hello’ 是一个 SpEL 表达式,表示当事件的 message 属性值为 hello 时,事件监听器才会被执行。
(1)SpEL (Spring Expression Language) 是一种强大的表达式语言,用于在运行时执行各种表达式。我们可以使用 SpEL 表达式来访问对象的属性、调用对象的方法、执行运算等。
(2)SpEL 表达式语法如下:
属性访问:使用 . 操作符访问对象的属性,例如,object.property 表示访问对象 object 的属性 property。
方法调用:使用 () 操作符调用对象的方法,例如,object.method() 表示调用对象 object 的方法 method。
运算符:SpEL 支持常用的运算符,包括算术运算符、关系运算符、逻辑运算符等。
(3)SpEL 表达式还支持一些特殊的操作符和函数,如下所示:
? 操作符:三目运算符,形如 (condition ? then : else),表示当 condition 为真时返回 then,否则返回 else。
instanceof 操作符:用于判断对象是否为某个类型,形如 object instanceof T,表示对象 object 是否为类型 T。
t() 函数:将对象转换为给定的类型,形如 t(T),表示将对象转换为类型 T。
elvis 操作符:用于判断对象是否为空,形如 object ?: defaultValue,表示如果对象不为空则返回对象,否则返回默认值。

// 事件监听器
@Component
public class MyEventListener{// 使用注解实现事件监听@EventListener(condition = "#event.message == 'hello'")public void onApplicationEvent(MyEvent event) {System.out.println("接收到事件: " + event.getMessage());}
}

(4)对于使用 @EventListener 注解并定义为具有返回类型的方法,Spring 会将结果作为新事件发布。在下面的示例中,第一个方法返回的 AnotherEvent 将被发布,然后由第二个方法处理。

// 事件监听器
@Component
public class MyEventListener{@EventListenerpublic AnotherEvent listener1(MyEvent event) {// 处理事件System.out.println("事件监听器 1 接收到事件: " + event.getMessage());return new AnotherEvent(this, "转发" + event.getMessage());}@EventListenerpublic void listener2(AnotherEvent event) {// 处理事件System.out.println("事件监听器 2 接收到事件: " + event.getMessage());}
}

3,使用 @Order 指定优先级

(1)当 Spring 发布一个事件时,会调用所有能处理这个事件的事件监听器方法。如果你有多个事件监听器方法,那么 Spring 会依次调用这些方法。比如下面样例,当发布一个 MyEvent 事件时,Spring 会依次调用这两个方法。

// 事件监听器
@Component
public class MyEventListener{@EventListenerpublic void listener1(MyEvent event) {// 处理事件System.out.println("事件监听器 1 接收到事件: " + event.getMessage());}@EventListenerpublic void listener2(MyEvent event) {// 处理事件System.out.println("事件监听器 2 接收到事件: " + event.getMessage());}
}

在这里插入图片描述

(2)我们可以使用 @Order 注解来指定事件监听器方法的优先级。@Order 注解可以标注在类上或方法上,表示这个类或方法的优先级。数值越小,优先级越高。

// 事件监听器
@Component
public class MyEventListener{@Order(2)@EventListenerpublic void listener1(MyEvent event) {// 处理事件System.out.println("事件监听器 1 接收到事件: " + event.getMessage());}@Order(1)@EventListenerpublic void listener2(MyEvent event) {// 处理事件System.out.println("事件监听器 2 接收到事件: " + event.getMessage());}
}

在这里插入图片描述

4,使用 @Async 实现异步事件监听

(1)从第一个样例运行结果可以看出默认 spring 事件是同步的,这意味着发布者线程将阻塞,直到所有侦听器都完成对事件的处理为止。我们可以使用 @Async 注解来标注一个事件监听器方法,表示这个方法是一个异步方法,应该在独立的线程中执行。

// 事件监听器
@Component
public class MyEventListener{// 使用注解实现事件监听@Async@EventListenerpublic void onApplicationEvent(MyEvent event) {System.out.println("接收到事件: " + event.getMessage());// 模拟事件处理try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}}
}

(2)同时我们还需要在配置类(@Configuration 类之一或 @SpringBootApplication 类)中启用异步处理,才能使用 @Async 注解。

@Configuration
@EnableAsync
public class MyConfig {// 配置类
}

(3)最后测试一下,可看到实现了异步事件监听:
在这里插入图片描述

附:Spring Boot 内置的 Application Event

(1)Spring Boot 中包含了一些与 SpringApplication 生命周期相关的内置 Application Event,包括:

  • ApplicationStartingEvent:在 Spring Boot 应用程序启动之前发布。
  • ApplicationEnvironmentPreparedEvent:在 Spring Boot 应用程序的环境已经准备好,但正在创建 Application Context 上下文之前发布。
  • ApplicationContextInitializedEvent:当 Spring Boot 应用程序 Application Context 上下文准备就绪并且调用
  • ApplicationContextInitializers,但尚未加载 bean 定义时发布。
  • ApplicationPreparedEvent:在 Spring Boot 应用程序的 Application Context 上下文已经创建,但尚未刷新之前发布。
  • ApplicationStartedEvent:在 Spring Boot 应用程序的 Application Context 上下文已经刷新,但尚未启动之前发布。
  • ApplicationReadyEvent:在 Spring Boot 应用程序已经启动并准备接受请求之后发布。
  • ApplicationFailedEvent:在 Spring Boot 应用程序启动失败时发布。

(2)假设我们需要监听 ApplicationStartingEvent,则首先定义一个监听器类:

// 事件监听器
public class MyEventListener implements ApplicationListener<ApplicationStartingEvent> {// 事件发生时执行@Overridepublic void onApplicationEvent(ApplicationStartingEvent event) {// 处理事件System.out.println("应用程序将要启动");}
}

(3)由于该事件实际上是在 Application Context 创建前触发的,这时的 Bean 是不能被加载的。所以我们不能在这个监听器类上中使用 @Component 注册监听器,只能通过 SpringApplication 注册监听器。我们对项目启动类做如下修改:
注意:对于ApplicationStartedEvent、ApplicationReadyEvent、ApplicationFailedEvent 这些在 Application Context上下文已经创建完毕之后,所以可以直接使用 @Component 注册,不需要下面这个步骤。

@SpringBootApplication
public class RmiserverApplication {public static void main(String[] args) {SpringApplication app = new SpringApplication(RmiserverApplication.class);app.addListeners(new MyEventListener()); //注册监听器app.run(args);}
}

或者也可以使用使用 SpringApplicationBuilder 注册监听器:

@SpringBootApplication
public class RmiserverApplication {public static void main(String[] args) {new SpringApplicationBuilder().sources(RmiserverApplication.class).listeners(new MyEventListener()) //注册监听器.run(args);}
}

(4)启动项目,可以看到控制台输出如下:
在这里插入图片描述

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

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

相关文章

程序员月经焦虑 :如何成为高级工程师

高级工程师意味着什么&#xff1f; ChatGPT的回复&#xff1a;高级工程师对编程语言、软件设计原则和开发方法有深刻的理解。 开发方法 开发方法学是旨在使团队有效的组织方法。这些对我们来说可能很无聊&#xff0c;但我们希望你在这方面有专业知识。 我已经对非敏捷开发方法…

SAHI强化YOLOv5在小目标上的表现

文章目录 环境前言安装sahiyolov5检测sahi添加新的检测模型 环境 ubuntu 18.04 64bitsahi 0.8.4yolov5 5.0pytorch 1.7.1cu101 前言 目标检测和实例分割是迄今为止计算机视觉中最重要的应用领域&#xff0c;各种目标检测网络层出不穷&#xff0c;然而&#xff0c;小目标的检…

速卖通(AliExpress)店铺流量怎么转化?自养号测评策略

随着全球电商的蓬勃发展&#xff0c;速卖通&#xff08;AliExpress&#xff09;作为中国领先的跨境电商平台&#xff0c;为卖家提供了一个广阔的销售舞台。然而&#xff0c;对于卖家来说&#xff0c;如何让速卖通店铺实现转化&#xff0c;吸引更多的买家成为关键。 一、速卖通…

javaweb `jdbc.properties`文件编写

问题&#xff1a;查询数据库查不到&#xff0c;大概率是两者编码对应不上&#xff1f; 问题描述&#xff1a; 从数据库查询这一句&#xff0c;但数据库是有这个值的。 解决办法&#xff1a; 这是jdbc.properties里面写的内容 drivercom.mysql.jdbc.Driverurljdbc:mysql://12…

jmeter简单压测kafka

前言 这也是一个笔记&#xff0c;就是计划用jmeter做性能测试&#xff0c;但是这里是只要将数据放到kafka的topic里&#xff0c;后面查看下游业务处理能力。 一、方案 因为只要实现数据放到kafka&#xff0c;参考了下博友的方案&#xff0c;可行。 二、方案验证 详细过程就不…

iptables详解

1、介绍 iptables 是一个在 Linux 系统上用于配置和管理防火墙规则的工具。它允许系统管理员定义数据包的过滤规则、网络地址转换&#xff08;NAT&#xff09;规则和数据包的网络地址和端口的转发规则。iptables 提供了非常灵活和强大的功能&#xff0c;可以用于保护网络安全、…

微服务实战系列之MQ

前言 从今天起&#xff0c;席卷北国的雪&#xff0c;持续了一整天&#xff0c;北京也不例外。这场意外的寒潮&#xff0c;把整个冬天渲染的格外cool。当然你可以在外面打雪仗、堆雪人、拉雪橇&#xff0c;也可以静坐屋内&#xff0c;来一场围炉煮茶的party。此刻&#xff0c;冬…

KUKA机器人如何隐藏程序或程序段?

KUKA机器人如何隐藏程序或程序段? 如下图所示,新建一个示例程序进行说明, 如下图所示,如果红框中的动作指令不想让别人看到,想隐藏起来,如何做到? 如下图所示,在想要隐藏的程序或程序段的前后,分别添加 ;fold 和 endfold指令(这里要注意是英文状态下的输入法), 如…

安卓跳转页面闪屏,方法里需要传View 参数

/*** 跳转到首页的公共方法* */public void ToIndexpage(View v){//设置跳转的页面Intent intent new Intent(this, MainActivity.class);//实行跳转startActivity(intent);}

JS基础之闭包

JS基础之闭包 闭包闭包的目的闭包的优缺点 闭包 官方定义&#xff1a; 闭包是能够访问到自由变量的函数。 自由变量&#xff1a;能够在函数中使用&#xff0c;但是不是函数的参数&#xff0c;也不是内部的局部变量。 示例&#xff1a; var a 1; function foo(){console.log(a…

【Jmeter】Jmeter基础5-Jmeter元件介绍之线程(用户)

2.5.1、线程组 一个线程组即一个虚拟用户组&#xff0c;线程组中的每个线程即为1个虚拟用户&#xff0c;每个线程互相隔离&#xff0c;互不影响参数说明&#xff1a; 在取样器错误后要执行的动作 继续&#xff1a;忽略错误&#xff0c;继续执行启动下一进程循环&#xff1a; 终…

解决:ModuleNotFoundError: No module named ‘docx‘

解决&#xff1a;ModuleNotFoundError: No module named ‘docx’ 文章目录 解决&#xff1a;ModuleNotFoundError: No module named docx背景报错问题报错翻译报错位置代码报错原因解决方法今天的分享就到此结束了 背景 在使用之前的代码时&#xff0c;报错&#xff1a; Trace…

LainChain 原理解析:结合 RAG 技术提升大型语言模型能力

摘要&#xff1a;本文将详细介绍 LainChain 的工作原理&#xff0c;以及如何通过结合 RAG&#xff08;Retrieval-Aggregated Generation&#xff09;技术来增强大型语言模型&#xff08;如 GPT 和 ChatGPT 等&#xff09;的性能。我们将探讨 COT、TOT、RAG 以及 LangChain 的概…

_try_finally原理探究后续

入口程序的最后一道防线 这里调用mainCRTStartup()&#xff0c;然后调用入口程序 相当于这里才是一个进程开始执行的地方 这里有一个call调用&#xff0c;跟进去看看 发现有修改fs:[0]的操作&#xff0c;这里就相当于编译器为我们注册了一个异常处理函数 这里到kernel32.dll里面…

jsp 学科竞赛成绩管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 学科竞赛成绩管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysq…

定制软件开发的 5 个挑战

对于大公司来说&#xff0c;定制软件开发就像是眼中钉。无论您是要创建内部使用的工具、自动化手动流程还是推出新产品&#xff0c;从头开始构建它历来都是昂贵且危险的。花钱购买领先的现成解决方案之一&#xff0c;却不得不花费更多的时间和金钱对其进行定制来完成工作&#…

Java数据结构篇——实现顺序表的增删查改

文章目录 1.线性表2. 顺序表2.1 顺序表结构2.2 实现顺序表接口2.3 打印顺序表2.2 实现新增元素2.3 实现查找元素2.3 获取pos位置的值2.4 删除元素2.5 获取顺序表的长度2.6 清空顺序表 3.代码在这 1.线性表 定义&#xff1a;线性表是 n 个具有相同特性的数据元素的有序序列。线…

大数据存储技术(1)—— Hadoop简介及安装配置

目录 一、Hadoop简介 &#xff08;一&#xff09;概念 &#xff08;二&#xff09;Hadoop发展历史 &#xff08;三&#xff09;Hadoop三大发行版本 &#xff08;四&#xff09;Hadoop的优势 二、Hadoop的组成 &#xff08;一&#xff09;Hadoop1.x和Hadoop2.x的区别​…

SSL通配符详解

通配符证书是一种特殊的SSL/TLS证书&#xff0c;它允许一个域名及其所有子域名使用相同的证书。这意味着&#xff0c;如果您有一个通配符证书&#xff0c;您可以为该证书中的任何子域名提供SSL/TLS加密&#xff0c;而无需为每个子域名单独购买和配置证书。 通配符证书使用通配…

苹果电脑双开

1.第一步&#xff1a;在应用程序中找到微信 复制一个副本出来 2.第二步:打开复制的《微信副本》 右键打开 – 显示包内容 3.第三步:Contents - info.plist 后右键 打开方式 选择 文本编辑 4.第四步&#xff1a;找到查找和替换 这一段com.tencent.xinWeChat 后面是修改 com.tenc…