当前位置: 首页 > news >正文

Spring事件机制

文章目录

  • 一、Spring事件
  • 二、实现Spring事件
    • 1、自定义事件
    • 2、事件监听器
      • 2.1 实现ApplicationListener接口
      • 2.2 @EventListener
      • 2.3 @TransactionalEventListener
    • 3、事件发布
    • 4、异步使用
  • 三、EventBus
    • 1、事件模式
    • 2、EventBus三要素
    • 3、同步事件
      • 3.1 定义事件类
      • 3.2 定义事件监听
      • 3.3 测试
    • 4、异步事件
      • 4.1 定义事件
      • 4.2 定义事件监听
      • 4.3 测试
  • 四、EventBus和Spring Event区别

一、Spring事件

Spring Event(Application Event)其实就是一个观察者设计模式,一个 Bean 处理完成任务后希望通知其它 Bean 或者说一个 Bean 想观察监听另一个Bean 的行为。

Spring的事件(Application Event)为Bean和Bean之间的消息同步提供了支持。当一个Bean处理完成一个任务之后,希望另外一个Bean知道并能做相应的处理,这时我们就需要让另外一个Bean监听当前Bean所发生的事件


Spring的事件需要遵循如下流程:

  1. 自定义事件,继承ApplicationEvent
  2. 定义事件监听器
  3. 使用容器发布事件

二、实现Spring事件

1、自定义事件

自定义一个事件类,继承ApplicationEvent。该事件可以被ApplicationContext通过publishEvent方法进行发送

public class DemoEvent extends ApplicationEvent {private String msg;public DemoEvent(Object source, String msg) {super(source);this.msg = msg;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}
}

2、事件监听器

监听器有三种实现方式

  • 实现ApplicationListener接口
  • 使用@EventListener注解
  • 使用@TransactionalEventListener注解

2.1 实现ApplicationListener接口

新建一个类实现 ApplicationListener 接口,并且重写 onApplicationEvent 方法,然后交给Spring管理

@Component
public class DemoListener implements ApplicationListener<DemoEvent> {//实现ApplicationListener接口,并指定监听的事件类型 @Overridepublic void onApplicationEvent(DemoEvent event) { //使用onApplicationEvent方法对消息进行接受处理 String msg = event.getMsg();System.out.println("DemoListener获取到了监听消息:" + msg);}
}

2.2 @EventListener

将处理事件的方法使用 @EventListener 注解标记,此时 Spring将创建一个ApplicationListener的bean对象,使用给定的方法处理事件,参数可以指定处理的事件类型,以及处理条件。

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EventListener {@AliasFor("classes") Class<?>[] value() default {};@AliasFor("value") Class<?>[] classes() default {};String condition() default "";
}
@Component
public class DemoListener {@EventListener(DemoEvent.class)public void sendMsg(DemoEvent event) {String msg = event.getMsg();System.out.println("DemoListener获取到了监听消息:" + msg);}
}

2.3 @TransactionalEventListener

@EventListener@TransactionalEventListener 都是 Spring提供的注解,用于处理事件。主要区别在于处理事件的时间和事务的关联性。

  • @EventListener:可以应用于任何方法,使得该方法成为一个事件监听器。当一个事件被发布时,所有标记为 @EventListener 的方法都会被调用,无论当前是否存在一个活动的事务。 使用@EventListener 注解的方法可能在事务提交之前或之后被调用。
  • @TransactionalEventListener:该注解允许更精细地控制事件监听器在事务处理过程中的执行时机。@TransactionalEventListener 默认在当前事务提交后才处理事件(TransactionPhase.AFTER_COMMIT),这可以确保事件处理器只在事务成功提交后才被调用。也可以通过 phase 属性来改变事件处理的时机,例如在事务开始前、事务提交前、事务提交后或者事务回滚
@Component
public class DemoListener {@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, value = DemoEvent.class)public void messageListener(DemoEvent event) {String msg = event.getMsg();System.out.println("DemoListener获取到了监听消息:" + msg);}
}

3、事件发布

通过spring上下文对象ApplicationContextpublishEvent方法即可发布事件

@Component
public class DemoPublisher {@Autowiredprivate ApplicationContext applicationContext; //注入ApplicationContext用来发布事件 public void publish(String msg) {applicationContext.publishEvent(new DemoEvent(this, msg));  // 使用ApplicationContext对象的publishEvent发布事件}
}

4、异步使用

如果要开启异步,需要在启动类增加 @EnableAsync 注解,Listener 类需要开启异步的方法增加 @Async 注解

@Async的原理是通过 Spring AOP 动态代理 的方式来实现的。Spring 容器启动初始化bean时,判断类中是否使用了@Async注解,如果使用了则为其创建切入点和切入点处理器,根据切入点创建代理,在线程调用@Async注解标注的方法时,会调用代理,执行切入点处理器invoke方法,将方法的执行提交给线程池中的另外一个线程来处理,从而实现了异步执行。



三、EventBus

  1. EventBus是一个轻量级的发布/订阅模式的应用模式,相比于各种 MQ 中间件更加简洁、轻量,它可以在单体非分布式的小型应用模块内部使用。
  2. 我们也可以把它和 MQ 中间件结合起来使用,使用 EventBus 作为当前应用程序接收 MQ 消息的统一入口,然后应用内部基于 EventBus 进行分发订阅,以达到高内聚低耦合的目的(当应用内部需要消费多种不同 MQ 中间件消息时,不需要在当前应用的好多不同代码位置都编写 MQ 消费代码)

1、事件模式

EventBus 默认为同步调用,同一个 EventBus 中注册的多个订阅处理,再事件下发后是被总线串行逐个调用的,如果其中一个方法占用事件较长,则同一个 EventBus 中的其他事件处于等待状态,且发送消息事件的代码调用处也是同步调用等待的状态。同一个 EventBus 对象,不仅仅在同一个 post 调用中串行执行,在多次并发 post 调用时,多个 post 调用之间也是串行等待执行的关系

在这里插入图片描述

  • 同步事件模式:同步事件模式下,事件的触发和事件的处理在同一个线程中同步处理
  • 异步事件模式:异步事件模式下,事件的触发和事件的处理在不同的线程中,事件的处理在一个线程池中

2、EventBus三要素

  • Event 事件,它可以是任意类型
  • Subscriber 事件订阅者,需要加上注解@Subscribe(),并且指定线程模型,默认是POSTING
  • Publisher 事件的发布者。我们可以在任意线程里发布事件,调用post(Object)方法即可

3、同步事件

3.1 定义事件类

public class MessageEvent {private String message;public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}@Overridepublic String toString() {return "MessageEvent{" + "message='" + message + '\'' + '}';}
}

3.2 定义事件监听

import com.google.common.eventbus.Subscribe;public class MessageListener {@Subscribepublic void listen(MessageEvent event) {System.out.println(Thread.currentThread().getName() + " : " + event);}
}

3.3 测试

import com.google.common.eventbus.EventBus;public class SyncEventTest {public static void main(String[] args) {EventBus eventBus = new EventBus();eventBus.register(new MessageListener());MessageEvent messageEvent = new MessageEvent();messageEvent.setMessage("你好!");for (int i = 0; i < 100; i++) {eventBus.post(messageEvent);}}
}

测试结果
从结果可以看出,事件的触发和事件的处理都是在同一个线程中。

main : MessageEvent{message='你好!'} 
main : MessageEvent{message='你好!'} 
main : MessageEvent{message='你好!'} 
main : MessageEvent{message='你好!'} 
main : MessageEvent{message='你好!'} 
main : MessageEvent{message='你好!'} 
main : MessageEvent{message='你好!'} 
main : MessageEvent{message='你好!'}......


4、异步事件

4.1 定义事件

public class MessageEvent {private String message;public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}@Overridepublic String toString() {return "MessageEvent{" + "message='" + message + '\'' + '}';}
}

4.2 定义事件监听

import com.google.common.eventbus.AllowConcurrentEvents;
import com.google.common.eventbus.Subscribe;public class MessageListener {/*** 注解@AllowConcurrentEvents是用来标识当前订阅者是线程安全的 * Guava会对listener对象,遍历其带有@Subscribe注解的所有方法,然后对针对每一个listener对象和method方法,标识唯一一个订阅者* 找到唯一识别的观察者后,会对该观察者进行包装wrap,包装成一个EventSubscriber对象,* 对于没有@AllowConcurrentEvents注解的方法,会被包装成SynchronizedEventSubscriber,即同步订阅者对象。 * 同步订阅者对象在处理事件时是使用了synchronized,强同步锁! * 总结: * 如果当前观察者(method)是线程安全的thread-safe,建议增加注解@AllowConcurrentEvents,以减少同步开销。 * 对于使用的是非异步(AsyncEventBus),也建议增加@AllowConcurrentEvents,因为不需要进行同步。*/@AllowConcurrentEvents@Subscribepublic void listen(MessageEvent event) {System.out.println(Thread.currentThread().getName() + " : " + event);}
}

4.3 测试

import com.google.common.eventbus.AsyncEventBus;import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class AsyncEventTest {private static final int CORE_SIZE = Runtime.getRuntime().availableProcessors();private static final int ALIVE_TIME = 60;private static final int CACHE_SIZE = 1000;public static void main(String[] args) {AsyncEventBus eventBus = new AsyncEventBus(new ThreadPoolExecutor(CORE_SIZE, CACHE_SIZE << 1, ALIVE_TIME, TimeUnit.SECONDS, new ArrayBlockingQueue(CACHE_SIZE)));eventBus.register(new MessageListener());MessageEvent messageEvent = new MessageEvent();messageEvent.setMessage("你好!");for (int i = 0; i < 100; i++) {eventBus.post(messageEvent);}}
}

测试结果
从结果可以看出,异步事件模式下,事件的触发和处理是在不同的线程中的,事件的处理是在单独的线程池中进行处理

pool-1-thread-2 : MessageEvent{message='你好!'} 
pool-1-thread-3 : MessageEvent{message='你好!'} 
pool-1-thread-4 : MessageEvent{message='你好!'} 
pool-1-thread-5 : MessageEvent{message='你好!'} 
pool-1-thread-6 : MessageEvent{message='你好!'} 
pool-1-thread-7 : MessageEvent{message='你好!'}pool-1-thread-8 : MessageEvent{message='你好!'} 
pool-1-thread-9 : MessageEvent{message='你好!'} 
pool-1-thread-10 : MessageEvent{message='你好!'} 
.......


四、EventBus和Spring Event区别

项目事件发布者发布方法是否异步监听者注册方式
EventBus任意对象EventBusEventBus#post注解Subscribe的方法手动注册EventBus#register
Spring Event任意对象ApplicationEventPublisherApplicationEventPublisher#publishEvent支持同步异步注解EventListener的方法系统注册

相关文章:

Spring事件机制

文章目录 一、Spring事件二、实现Spring事件1、自定义事件2、事件监听器2.1 实现ApplicationListener接口2.2 EventListener2.3 TransactionalEventListener 3、事件发布4、异步使用 三、EventBus1、事件模式2、EventBus三要素3、同步事件3.1 定义事件类3.2 定义事件监听3.3 测…...

vue+canvas音频可视化

1.代码 <template><div class"subGuide"><canvas id"canvas"></canvas><br><audio id"audio" src"./audio.mp3" controls></audio></div> </template><script> export…...

俊昭stm32笔记

stm32——中断优先级 stm32——创建基础工程模板stm32——创建基础工程模板-CSDN博客 stm32——MCU启动方式stm32——MCU启动方式_stm32调试时程序启动方式-CSDN博客 stm32——串口stm32——串口_stm32 串口-CSDN博客 stm32——lcd液晶显示stm32——lcd液晶显示-CSDN博客...

W30-python03-pytest+selenium+allure访问百度网站实例

此篇文章为总结性&#xff0c;将pystest、selenium、allure结合起来 功能如下&#xff0c;web自动化&#xff0c;输入baidu网站&#xff0c;搜索“雷军”、打开网页中第一条内容 pytestsel.py如下&#xff1a; import time import re import allure import pytest from tools…...

如何在 Debian 8 上安装和使用 PostgreSQL 9.4

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 简介 关系型数据库是满足多种需求的数据组织的基石。它们支持从在线购物到火箭发射等各种应用。PostgreSQL 是一种历史悠久但仍然活跃的…...

【微信小程序】微信小程序设置本地背景图片在真机无法显示的解决方案

微信小程序设置本地背景图片在真机无法显示的解决方案 在开发微信小程序时&#xff0c;很多开发者会遇到一个常见的问题&#xff1a;在调试环境中设置本地背景图片可以正常显示&#xff0c;但在真机上却无法显示。本文将详细探讨这一问题的原因&#xff0c;并提供三种解决方案…...

Arthas在线诊断案例实战整理

Arthas - Java 应用诊断利器 Arthas 是一款线上监控诊断产品&#xff0c;通过全局视角实时查看应用 load、内存、gc、线程的状态信息&#xff0c;并能在不修改应用代码的情况下&#xff0c;对业务问题进行诊断&#xff0c;包括查看方法调用的出入参、异常&#xff0c;监测方法…...

使用 XRDP 远程linux主机

一、简介 XRDP是一个开源的远程桌面协议&#xff08;Remote Desktop Protocol,RDP&#xff09;服务器&#xff0c;采用的是标准的RDP。 官网地址&#xff1a;https://www.xrdp.org/ github地址&#xff1a; https://github.com/neutrinolabs/xrdp/releases XRDP也是C/S架构&…...

学习小型gpt源码(自用)

数据集构建_哔哩哔哩_bilibili &#xff08;b站上有一系列课&#xff0c;从数据处理到模型构建和训练使用&#xff09; 什么是batch&#xff1f; 为什么一个batch内的句子要一样长&#xff1f; 不同batch的长度可以不一样&#xff0c;但是同一个batch内长度一样&#xff01;…...

@Transactional使用的注意事项

在项目中涉及到CRUD操作时&#xff0c;一般都会在方法上添加该注解&#xff0c;以为加上Transactional&#xff0c;Spring就可以自动帮我们进行事务的开启、提交 有一个很多人都会犯的误区&#xff1a; 将Spring事务与Transactional划上了等号&#xff0c;只要有数据库相关操作…...

快手可灵视频生成大模型全方位测评

快手视频生成大模型“可灵”&#xff08;Kling&#xff09;&#xff0c;是全球首个真正用户可用的视频生成大模型&#xff0c;自面世以来&#xff0c;凭借其无与伦比的视频生成效果&#xff0c;在全球范围内赢得了用户的热烈追捧与高度评价。截至目前&#xff0c;申请体验其内测…...

【JavaScript】`Map` 数据结构

文章目录 一、Map 的基本概念二、常见操作三、与对象的对比四、实际应用场景 在现代 JavaScript 中&#xff0c;Map 是一种非常重要且强大的数据结构。与传统的对象&#xff08;Object&#xff09;不同&#xff0c;Map 允许您使用各种类型的值作为键&#xff0c;不限于字符串或…...

Ubuntu22.04使用NVM安装多版本Node.js和版本切换

Fabric官方目前支持Node.js开发区块链应用&#xff0c;建议使用Node长期支持版本&#xff08;LTS&#xff09;。 建议使用NVM安装Node.js&#xff0c;NVM可以帮助我们方便的在Node的不同版本之间进行切换&#xff0c;这样我们就可以同时工作在不同的项目上。 下面是安装的脚本…...

基于C51和OLED12864实现Goole小恐龙

在数字娱乐领域&#xff0c;Google小恐龙&#xff08;T-Rex Runner&#xff09;以其简单而上瘾的游戏机制赢得了广泛的关注和喜爱。这款内置于Chrome浏览器的离线小游戏&#xff0c;不仅为用户带来了乐趣&#xff0c;也激发了开发者们对其进行各种创新和扩展的灵感。本文将介绍…...

【Docker】CentOS7环境下的安装

环境展示 安装 配置仓库 sudo yum install -y yum-utils # docker官方key文件下载 sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 建议使用阿里云key文件下载 sudo yum-config-manager --add-repo https://mirrors.aliyun.…...

浏览器的最大并发数(http1.1)

HTTP/1.1&#xff1a;每个资源请求通常需要单独的TCP连接&#xff0c;尽管支持Keep-Alive机制&#xff0c;允许在同一个TCP连接上连续发送多个请求。但通常浏览器限制并发TCP连接数&#xff08;例如&#xff0c;每个域名最多6个并发连接&#xff09;。 HTTP/2&#xff1a;引入…...

Android 开发中px、dpi 和 dp三个单位的介绍

Android 开发中px、dpi 和 dp三个单位的介绍 在 Android 开发中&#xff0c;px、dpi 和 dp 是用来描述屏幕尺寸和密度的单位&#xff0c;它们在设计和开发中有着不同的作用和用途。 1. px&#xff08;像素&#xff09; 定义&#xff1a; px 表示屏幕上的一个像素点&#xff0c…...

zookeeper开启SASL权限认证

目录 一、SASL介绍 二、使用 SASL 进行身份验证 2.1 服务器到服务器的身份验证 2.2 客户端到服务器身份验证 三、验证功能 一、SASL介绍 默认情况下&#xff0c;ZooKeeper 不使用任何形式的身份验证并允许匿名连接。但是&#xff0c;它支持 Java 身份验证与授权服务(JAAS)…...

mysql一个小问题引发的思考-mysql类型转换-查询缓存 及 MYSQL查询缓存以及自动选择不使用查询缓存的情况

一、mysql一个小问题引发的思考-mysql类型转换-查询缓存 最近在做的一个项目中有一个SQL语句发现点问题&#xff0c;大概如下&#xff1a; select * from table where cid0 or find_in_set(1, cid); 数据表中的字段cid是字符串类型&#xff0c;原来的后端同学未提过此字段还能是…...

css更改图片颜色

css更改图片颜色&#xff0c;比较时候颜色单一的图片&#xff0c;比如logo之类的 css中的 filter 属性定义元素&#xff08;通常是 <img>&#xff09;的视觉效果&#xff08;如模糊和饱和度&#xff09; img{ -webkit-filter: invert(51%) sepia(94%) saturate(6433%) h…...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

汽车生产虚拟实训中的技能提升与生产优化​

在制造业蓬勃发展的大背景下&#xff0c;虚拟教学实训宛如一颗璀璨的新星&#xff0c;正发挥着不可或缺且日益凸显的关键作用&#xff0c;源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例&#xff0c;汽车生产线上各类…...

基于Uniapp开发HarmonyOS 5.0旅游应用技术实践

一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架&#xff0c;支持"一次开发&#xff0c;多端部署"&#xff0c;可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务&#xff0c;为旅游应用带来&#xf…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力

引言&#xff1a; 在人工智能快速发展的浪潮中&#xff0c;快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型&#xff08;LLM&#xff09;。该模型代表着该领域的重大突破&#xff0c;通过独特方式融合思考与非思考…...

C++ 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...