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

news2024/11/27 6:17:06

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> {
  // 事件发生时执行
  @Override
  public 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 {
  @Autowired
  private 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{
  // 使用注解实现事件监听
  @EventListener
  public 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{
  @EventListener
  public AnotherEvent listener1(MyEvent event) {
    // 处理事件
    System.out.println("事件监听器 1 接收到事件: " + event.getMessage());
    return new AnotherEvent(this, "转发" + event.getMessage());
  }
 
  @EventListener
  public void listener2(AnotherEvent event) {
    // 处理事件
    System.out.println("事件监听器 2 接收到事件: " + event.getMessage());
  }
}

3,使用 @Order 指定优先级

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

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

在这里插入图片描述

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

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

在这里插入图片描述

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

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

// 事件监听器
@Component
public class MyEventListener{
  // 使用注解实现事件监听
  @Async
  @EventListener
  public 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> {
  // 事件发生时执行
  @Override
  public 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.coloradmin.cn/o/460484.html

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

相关文章

移动端网页特效

文章目录 一、触屏事件&#xff08;一&#xff09;触屏事件概述&#xff08;二&#xff09; 触摸事件对象&#xff08;TouchEvent&#xff09;&#xff08;三&#xff09; 移动端拖动元素 二、移动端常见特效&#xff08;一&#xff09;案例&#xff1a;移动端轮播图&#xff0…

Windows安装Docker

目录 一.启用Hyper-V和容器特性 1.右键Windows点击应用和功能 2.点击程序和功能​编辑 3.启用或关闭Windows功能​编辑 4.开启 Hyper-V 和容器特性 二.下载安装Docker 1.下载Docker &#xff08;Download Docker Desktop | Docker&#xff09; 2.点击安装 3.把第一个选…

《商用密码应用与安全性评估》第三章商用密码标准与产品应用3.3商用密码产品检测

商用密码产品检测框架 GM/T 0028-2014《密码模块安全技术要求》将密码模块安全分为从一级到四级安全性逐次增强的4个等级GM/T 0008-2012《安全芯片密码检测准则》将安全芯片安全分为从一级到三级安全性逐次增强的3个等级。 对于不同安全等级密码产品的选用&#xff0c;应考虑以…

Docker --- 简介、安装

一、什么是Docker 微服务虽然具备各种各样的优势&#xff0c;但服务的拆分通用给部署带来了很大的麻烦。 分布式系统中&#xff0c;依赖的组件非常多&#xff0c;不同组件之间部署时往往会产生一些冲突。 在数百上千台服务中重复部署&#xff0c;环境不一定一致&#xff0c;会…

【故障定位】基于粒子群优化算法的故障定位及故障区段研究【IEEE33节点】(Matlab代码实现)

&#x1f4a5; &#x1f4a5; &#x1f49e; &#x1f49e; 欢迎来到本博客 ❤️ ❤️ &#x1f4a5; &#x1f4a5; &#x1f3c6; 博主优势&#xff1a; &#x1f31e; &#x1f31e; &#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 …

VS2017配置Ipopt-基于Windows环境

文章目录 1、 背景2、 配置流程3、测试THE END 1、 背景 \qquad 本科研狗最近手头有个非线性规划模型需要求解&#xff0c;因为Ipopt是一款开源的NLP求解器&#xff0c;所以想要使用一下下。于是直接搜Ipopt官网&#xff0c;果然令人惊喜地列出了安装教程&#xff0c;但对于Win…

2023年五月份图形化一级打卡试题

活动时间 从2023年5月1日至5月21日&#xff0c;每天一道编程题。 本次打卡的规则如下&#xff1a; 小朋友每天利用10~15分钟做一道编程题&#xff0c;遇到问题就来群内讨论&#xff0c;我来给大家答疑。 小朋友做完题目后&#xff0c;截图到朋友圈打卡并把打卡的截图发到活动群…

混合办公现在还“吃香”吗?未来发展趋势如何?

在疫情期间&#xff0c;时不时就听到的“居家办公”新闻也在疫情放开后鲜少看到了。在疫情期间&#xff0c;呼声极高的混合办公、远程办公似乎也随着疫情的放开而“销声匿迹”了。 难道是&#xff0c;因疫情而生的混合办公&#xff0c;也最终因疫情放开而“死”了&#xff1f;在…

力扣刷题——搜索插入位置

目录 1、题目描述 2、题目分析 3、答案解析 1、题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1:…

webAPI学习笔记2(DOM事件高级)

1. 注册事件&#xff08;绑定事件&#xff09; 1.1 注册事件概述 给元素添加事件&#xff0c;称为注册事件或者绑定事件。 注册事件有两种方式&#xff1a;传统方式和方法监听注册方式 传统注册方式 利用 on 开头的事件 onclick <button οnclick“alert(hi~)”><…

【文献篇】国家法律法规数据库提供免费的文献下载功能

【文献篇】国家法律法规数据库提供免费的文献下载功能 不用登录、不用注册、点击即可免费下载word、PDF等版本&#xff01;&#xff01;&#xff01; 比网上随便找、复制粘贴、还需要格式更改、担心完整性、准确性等问题省心N倍&#xff01;&#xff01;&#xff01;—【蘇小…

中国国际金融展开幕 蚂蚁集团数字化三件套产品升级更易用

4月25日&#xff0c;2023中国国际金融展(以下简称“金融展”)在北京开幕。本次展会以“荟萃金融科技成果&#xff0c;展现数字金融力量”为主题&#xff0c;突出科技创新为金融带来变革。上百家参展商展示了行业最新科技成果。 记者看到&#xff0c;在蚂蚁集团展区就展示了十几…

QT Graphics View 绘图架构之场景、视图与图形项简介

1、场景、视图与图形项 采用QPainter 绘图时需要在绘图设备的 paintEvent()事件里编写绘图的程序&#xff0c;实现整个绘图过程。这种方法如同使用 Windows 的画图软件在绘图&#xff0c;绘制的图形是位图&#xff0c;这种方法适合于绘制复杂性不高的固定图形&#xff0c;不能…

供应商竞争情报分析工具 —— 全国招投标查询API

引言 招投标是一项非常重要的商业活动&#xff0c;涉及政府采购、建筑工程、物资采购等众多领域。招投标活动的开展需要广泛的信息支持&#xff0c;包括招标公告、中标结果、项目动态等各种信息。然而&#xff0c;由于信息分散、更新速度慢等原因&#xff0c;用户往往难以及时…

实景区剧本杀系统开发

实景区剧本杀系统开发需要考虑以下几个方面&#xff1a; 场地选取&#xff1a;选择合适的场地&#xff0c;足够容纳游戏人数和游戏内容&#xff0c;同时需要考虑安全性和便利性。 剧情设定&#xff1a;根据场地和游戏类型设计剧情&#xff0c;包括人物角色、任务目标、…

优思学院|精益管理的理念是什么?

作为一个企业&#xff0c;我们都希望拥有高效率和优异的竞争力。但是&#xff0c;如何才能在竞争激烈的市场中脱颖而出&#xff1f;这时&#xff0c;精益管理理念的出现可以帮助我们。 精益管理的基本概念是什么&#xff1f; 精益管理的核心理念是通过消除浪费来实现生产效率…

【WCH】CH32F203基于内部RTC时钟+I2C SSD1306 OLED显示

【WCH】CH32F203基于内部RTC时钟SSD1306 OLED显示 &#x1f4cc;相关篇《STM32F103基于标准库I2C SSD1306仿数码管RTC时钟显示》 ✨CH32F203的内部时钟频率&#xff1a;40KHz &#x1f516;基于Keil开发环境 &#x1f4dc;基于SDK中的RTC示例修改为内部时钟源&#xff0c;串…

详解C语言string.h中常用的14个库函数(三)

本篇博客继续讲解C语言string.h头文件中的库函数。本篇博客计划讲解3个函数&#xff0c;分别是&#xff1a;strstr, strtok, strerror。其中strstr函数我会用一种最简单的方式模拟实现。 strstr char * strstr ( const char * str1, const char * str2 );strstr可以在str1中查…

电脑怎么远程控制另一台电脑

要从一台电脑远程控制另一台电脑&#xff0c;您可以使用远程桌面软件。 以下是远程控制另一台电脑的步骤&#xff1a; 一、在两台电脑上安装远程桌面软件 有多种远程桌面软件可用&#xff0c;例如 Splashtop、微软远程桌面。 在远程电脑和本地电脑上分别安装软件。访问各自软…

MQ集群(rabbitMQ)

普通集群: 创建过程 我们先在之前启动的mq容器中获取一个cookie值&#xff0c;作为集群的cookie。执行下面的命令&#xff1a; docker exec -it mq cat /var/lib/rabbitmq/.erlang.cookie 可以看到cookie值如下&#xff1a; EFLYOBKNYFETSAXOUEYI 接下来&#xff0c;停止…