Spring Boot 中的事件发布与监听:深入理解 ApplicationEventPublisher(附Demo)
目录
- 前言
- 1. 基本知识
- 2. Demo
- 3. 实战代码
前言
🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF
基本的Java知识推荐阅读:
- java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全)
- 【Java项目】实战CRUD的功能整理(持续更新)
1. 基本知识
ApplicationEventPublisher 是 Spring 框架中一个功能接口(@FunctionalInterface),用于发布事件
是 Spring 的事件驱动模型的核心部分,开发者可以通过实现这个接口或通过 Spring 提供的现成实现来发布和管理事件
基本知识如下:
-
事件驱动模型:
Spring 提供了一个内置的事件模型,通过事件发布者(ApplicationEventPublisher)和事件监听器(@EventListener或ApplicationListener)
事件可以是框架提供的(例如:ContextRefreshedEvent),也可以是用户自定义的事件 -
@FunctionalInterface 注解:
声明此接口是函数式接口,只有一个抽象方法:publishEvent(Object event)
可以使用 lambda 表达式或方法引用来实现 -
事件类型:
支持两种事件对象:
ApplicationEvent 类型
非 ApplicationEvent 类型(会被包装成 PayloadApplicationEvent) -
事件传播特点:
异步/同步:事件的传播方式取决于事件监听器的实现,发布者本身不决定事件的执行方式
高效性建议:事件监听器应尽量快速完成任务,对于耗时操作建议使用异步处理
主要方法解析
publishEvent(ApplicationEvent event)
接收 ApplicationEvent 类型事件
实际是将事件转换为 Object 类型后调用 publishEvent(Object event) 方法publishEvent(Object event)
接收任何对象类型的事件
如果事件不是 ApplicationEvent 类型,会封装为 PayloadApplicationEvent
2. Demo
完整的可执行 Spring Boot 示例,展示了如何使用 ApplicationEventPublisher 实现事件发布和监听功能
这是一个基于 Spring Boot 核心功能的示例,无需 Spring Cloud
项目结构如下:
src/main/java/com/example/demo├── DemoApplication.java├── CustomEvent.java├── CustomEventListener.java├── EventPublisherService.java
截图如下:

主体流程如下:
+-----------------------+
| DemoApplication | <--- 运行时触发事件发布
+-----------------------+|v
+----------------------------+ 1. 通过依赖注入调用服务类
| EventPublisherService |
+----------------------------+|v
+-----------------------+ 2. 使用 ApplicationEventPublisher 发布事件
| ApplicationEventPublisher | -------> 发布 CustomEvent
+-----------------------+|v
+----------------------+
| CustomEvent | <--- 发布事件包含消息
+----------------------+|v
+-----------------------+
| CustomEventListener |
+-----------------------+|v
+-----------------------------+
| 打印 "Received custom event" |
+-----------------------------+
DemoApplication(主类)
package com.example.demo;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class DemoApplication implements CommandLineRunner {@Autowiredprivate EventPublisherService eventPublisherService;public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}@Overridepublic void run(String... args) {// 发布自定义事件eventPublisherService.publishEvent("Hello, ApplicationEventPublisher!");}
}
CustomEvent(事件类)
继承自 ApplicationEvent
可以封装任何自定义属性,例如 message
package com.example.demo;import org.springframework.context.ApplicationEvent;public class CustomEvent extends ApplicationEvent {private final String message;public CustomEvent(Object source, String message) {super(source);this.message = message;}public String getMessage() {return message;}
}
CustomEventListener(事件监听器)
使用@EventListener注解监听特定事件
方法参数即为监听的事件类型
package com.example.demo;import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;@Component
public class CustomEventListener {@EventListenerpublic void handleCustomEvent(CustomEvent event) {System.out.println("Received custom event: " + event.getMessage());}
}
EventPublisherService(事件发布服务)
用于在应用上下文中发布事件
Spring 框架会自动分发事件到匹配的监听器
package com.example.demo;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;@Service
public class EventPublisherService {@Autowiredprivate ApplicationEventPublisher eventPublisher;public void publishEvent(String message) {// 构造并发布事件CustomEvent event = new CustomEvent(this, message);eventPublisher.publishEvent(event);System.out.println("Event published: " + message);}
}
运行 DemoApplication 后,控制台输出如下:
Event published: Hello, ApplicationEventPublisher!
Received custom event: Hello, ApplicationEventPublisher!
截图如下:

扩展功能
- 异步事件监听:在监听方法上加 @Async,并在主类中启用异步:
@SpringBootApplication
@EnableAsync
public class DemoApplication { ... }
-
多监听器:可以定义多个监听器监听相同事件,Spring 会自动分发到每个监听器
-
自定义事件的继承:可以继承 CustomEvent 定义不同类型的事件,以实现事件的多态性
3. 实战代码
比如发送邮件,如果单纯跟接口进行绑定,代码后续扩展优化会非常冗余!
使用 事件机制(applicationContext.publishEvent 和监听器)
有如下好处:
- 解耦业务逻辑
通过事件机制,发送邮件的逻辑和业务逻辑分离:
业务代码只需要关心触发“发送邮件”这一行为(发布事件)
实际的邮件发送逻辑由监听器单独处理
这种解耦方式的优势:
- 更清晰的代码职责:业务代码不会夹杂具体的邮件发送逻辑
- 方便扩展:如果未来需要增加更多处理逻辑(如记录日志、重试机制等),可以直接扩展监听器,而不用修改业务代码
而且最主要的是:
- 支持异步操作
在监听器上添加了@Async注解,可以让事件的处理逻辑异步执行:
@Async
@EventListener
public void onMessage(MailSendMessage message) {log.info("[onMessage][消息内容({})]", message);mailSendService.doSendMail(message);
}
这样,主线程可以迅速完成主要业务逻辑(如创建发送日志)并返回,而不用等待邮件发送完成
对于高并发场景,这种异步机制非常有用
如果直接在业务方法中调用发送函数,就无法方便地实现异步处理,可能会导致:
- 性能问题:主线程被邮件发送操作阻塞
- 用户体验问题:如果邮件发送需要较长时间,业务响应时间会变长
- 灵活性和可扩展性
使用事件机制后,邮件发送的逻辑变成了“事件订阅者”:
可以轻松增加或移除其他监听器,而不会影响现有的业务代码
例如,除了发送邮件外,还可以添加监听器发送短信、推送通知、记录操作日志等
可以根据不同的事件类型(不同的事件类)触发不同的逻辑
上述功能比较抽象,以实际代码为例:
如果做一个接口,发送邮件,信息量很大的时候,需要等这个邮件信息,待结果返回,才可以给客户!
- 发送邮件的代码和业务代码紧密耦合,修改或扩展会很麻烦
- 如果要实现异步发送,还需要自己额外管理线程池或异步任务,增加复杂度
- 如果未来需要在发送邮件时附加其他逻辑(如发送通知),业务代码会变得越来越复杂
public Long sendSingleMail(...) {...// 直接发送邮件mailSendService.doSendMail(...);return sendLogId;
}
以下是结合ApplicationEventPublisher,下述代码以ruoyi-vue-pro代码为例子进行讲解
整体目录如下:

使用 @EventListener 注解监听特定事件
方法参数即为监听的事件类型
方法参数即为监听的事件类型
方法参数即为监听的事件类型
/*** 针对 {@link MailSendMessage} 的消费者*/
@Component
@Slf4j
public class MailSendConsumer {@Resourceprivate MailSendService mailSendService;@EventListener@Async // Spring Event 默认在 Producer 发送的线程,通过 @Async 实现异步public void onMessage(MailSendMessage message) {log.info("[onMessage][消息内容({})]", message);mailSendService.doSendMail(message);}}
对应的实体类:
@Data
public class MailSendMessage {/*** 邮件日志编号*/@NotNull(message = "邮件日志编号不能为空")private Long logId;/*** 接收邮件地址*/@NotNull(message = "接收邮件地址不能为空")private String mail;/*** 邮件账号编号*/@NotNull(message = "邮件账号编号不能为空")private Long accountId;/*** 邮件发件人*/private String nickname;/*** 邮件标题*/@NotEmpty(message = "邮件标题不能为空")private String title;/*** 邮件内容*/@NotEmpty(message = "邮件内容不能为空")private String content;// private File files;}
对应发送消息:
@Slf4j
@Component
public class MailProducer {@Resourceprivate ApplicationContext applicationContext;/*** 发送 {@link MailSendMessage} 消息** @param sendLogId 发送日志编码* @param mail 接收邮件地址* @param accountId 邮件账号编号* @param nickname 邮件发件人* @param title 邮件标题* @param content 邮件内容*/public void sendMailSendMessage(Long sendLogId, String mail, Long accountId,String nickname, String title, String content) {MailSendMessage message = new MailSendMessage().setLogId(sendLogId).setMail(mail).setAccountId(accountId).setNickname(nickname).setTitle(title).setContent(content);applicationContext.publishEvent(message);}}
实际主体代码是直接使用发送消息,不用对接接收消息,接收消息是直接监听就好!
业务逻辑代码:

相关文章:
Spring Boot 中的事件发布与监听:深入理解 ApplicationEventPublisher(附Demo)
目录 前言1. 基本知识2. Demo3. 实战代码 前言 🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF 基本的Java知识推荐阅读: java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全&am…...
java.math 包 中的 BigDecimal 类(详细案例拆解)
前言: 小编打算近期更俩三期类的专栏,一些常用的专集类,给大家分好类别总结和详细的代码举例解释。 今天是第五个 java.lang.Math 包中的 BigDecimal 类 我们一直都是以这样的形式,让新手小白轻松理解复杂晦涩的概念,…...
【记录】日常|从零散记录到博客之星Top300的成长之路
文章目录 shandianchengzi 2024 年度盘点概述写作风格简介2024年的创作内容总结 shandianchengzi 2024 年度盘点 概述 2024年及2025年至今我创作了786即84篇文章,加上这篇就是85篇。 很荣幸这次居然能够入选博客之星Top300,这个排名在我之前的所有年份…...
定时器按键tim_key模版
低优先级放在高优先级内势必是程序卡死 把高优先级放到低优先级内,会使程序卡死 可修改 Debuger调试方法 Pwm rcc #include "my_main.h" uint8_t led_sta0x10; char text[30]; void LED_Disp(uint8_t dsLED) {HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPI…...
Swing使用MVC模型架构
什么是MVC模式? MVC是一组英文的缩写,其全名是Model-View-Controller,也就是“模型-视图-控制器”这三个部分组成。这三个部分任意一个部分发生变化都会引起另外两个发生变化。三者之间的关系示意图如下所示: MVC分为三个部分,所以在MVC模型中将按照此三部分分成三…...
ui-automator定位官网文档下载及使用
一、ui-automator定位官网文档简介及下载 AndroidUiAutomator:移动端特有的定位方式,uiautomator是java实现的,定位类型必须写成java类型 官方地址:https://developer.android.com/training/testing/ui-automator.html#ui-autom…...
gitee——报错修改本地密码
有时候当我们向远端push本地的仓库时会有一些报错的行为。 如下: 这是因为我们在gitee修改了密码时,本地还没有更新提交,总是报错 解决修改密码报错 如下: 1.在本地点击搜索栏找到控制面板 步骤如下...
C++/CLI(Common Language Runtime)关键点详解
C++/CLI(Common Language Runtime)是 Microsoft Visual C++ 的一个扩展,允许使用 .NET Framework 的功能,同时保留对本机 C++ 代码的访问。当您需要在 C++ 和 C# 之间进行互操作时,C++/CLI 是一种常见的选择,因为它可以作为桥梁,将托管代码(如 C#)与非托管代码(如 C+…...
小盒科技携手体验家,优化智能教育服务体验,打造在线教育新高度
北京小盒科技有限公司(简称“小盒科技”,由“作业盒子”更名而来)是一家专注于教育科技的公司,致力于利用人工智能、大数据等先进技术,为中小学教育提供创新的解决方案和产品。 近日,「小盒科技」携手体…...
Docker 在Linux 系统中的使用说明
目录 一:Docker 容器介绍1、容器技术的发展2、容器的关键技术3、Docker 发展历程4、容器的运行效率 二:Docker 安装方式1、在线安装 Docker2、离线安装 Docker 二:Docker 数据目录1、数据存储路径2、子目录的作用 三:Docker 配置文…...
Docker Hub 全面解析及应对策略
在现代 DevOps 和容器化应用开发中,Docker Hub 是一个不可或缺的工具。然而,一些地区或企业对 Docker Hub 的访问受到限制,甚至全面禁止。这种现象引发了开发者和运维人员的广泛关注。那么,为什么 Docker Hub 会被禁用?…...
关于pygame窗口输入法状态异常切换现象的分析报告
一、问题描述 1.1 需求说明 我们准备使用Pygame开发一个键盘输入测试程序,需要确保输入时窗口始终处于英文输入模式,也就是禁止中文输入; 1.2 现象描述 控制台种显示,程序在初始化时,会有两次IMM状态切换操作&…...
基于新年视角下的城市人流数据分析
2025年新年~~~ 旅游消费似乎又成为城市活力的动力话题。 透过话题看数据,透过数据看结果,无非是从--人流量--到--人留量,能不能留下人,能否因人而产生消费。 基于这个角度,地方政府经营城市的商业模式本质则是为城市…...
分布式理解
分布式 如何理解分布式 狭义的分布是指,指多台PC在地理位置上分布在不同的地方。 分布式系统 分布式系**统:**多个能独立运行的计算机(称为结点)组成。各个结点利用计算机网络进行信息传递,从而实现共同的“目标或者任…...
macOS使用LLVM官方发布的tar.xz来安装Clang编译器
之前笔者写过一篇博文ubuntu使用LLVM官方发布的tar.xz来安装Clang编译器介绍了Ubuntu下使用官方发布的tar.xz包来安装Clang编译。官方发布的版本中也有MacOS版本的tar.xz,那MacOS应该也是可以安装的。 笔者2015款MBP笔记本,CPU是intel的,出厂…...
ppp综合实验
IP地址 r1 r2 r3 r4 hdlc封装 pap认证 r2 r3 chap认证 r2 r4 MGRE 主认证 [r1]int Tunnel 0/0/0 [r1-Tunnel0/0/0]ip add 192.168.4.1 24 [r1-Tunnel0/0/0]tunnel-protocol gre p2mp [r1-Tunnel0/0/0]source 12.1.1.1 [r1-Tunnel0/0/0]nhrp entry multicast dynamic [r1-Tu…...
C#实现SQL Server数据血缘关系生成程序
要在现有的C#程序中添加功能,输出SQL Server数据血缘关系的三张表到Excel文件,我们需要进行以下几个步骤: 分析存储过程、视图和函数中的引用关系,构建数据血缘关系。 按依赖性从小到大排序表的顺序。 找出对应生成表的数据的存储…...
HBuilderX构建Vue项目
版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhl HBuilderX概述 HBuilderX是一款专为开发者设计的高效开发工具,致力于提升开发者的编码效率和体验。HBuilderX既适合追求极致效率的极客,也适合希望简…...
基于微信小程序的英语学习交流平台设计与实现(LW+源码+讲解)
专注于大学生项目实战开发,讲解,毕业答疑辅导,欢迎高校老师/同行前辈交流合作✌。 技术范围:SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:…...
使用 Confluent Cloud 的 Elasticsearch Connector 部署 Elastic Agent
作者:来自 Elastic Nima Rezainia Confluent Cloud 用户现在可以使用更新后的 Elasticsearch Sink Connector 与 Elastic Agent 和 Elastic Integrations 来实现完全托管且高度可扩展的数据提取架构。 Elastic 和 Confluent 是关键的技术合作伙伴,我们很…...
JDK17 HashMap
HashMap ArrayList用动态数组存放元素,而HashMap用动态数组(桶)存储键值对。 如果两个键值对映射到桶同一个索引,则称为散列冲突。HashMap采用拉链法解决冲突,即桶中每个索引指向一个链表或者红黑树,多个键…...
算法随笔_23: 通过删除字母匹配到字典里最长单词
上一篇:算法随笔_22:数组中的k-diff对-CSDN博客 题目描述如下: 给你一个字符串 s 和一个字符串数组 dictionary ,找出并返回 dictionary 中最长的字符串,该字符串可以通过删除 s 中的某些字符得到。 如果答案不止一个,返回长度最长且字母序…...
ansible自动化运维实战--通过role远程部署nginx并配置(8)
文章目录 1、准备工作2、创建角色结构3、编写任务4、准备配置文件(金甲模板)5、编写变量6、编写处理程序7、编写剧本8、执行剧本Playbook9、验证-游览器访问每台主机的nginx页面 在 Ansible 中,使用角色(Role)来远程部…...
Python网络自动化运维---用户交互模块
文章目录 目录 文章目录 前言 实验环境准备 一.input函数 代码分段解析 二.getpass模块 前言 在前面的SSH模块章节中,我们都是将提供SSH服务的设备的账户/密码直接写入到python代码中,这样很容易导致账户/密码泄露,而使用Python中的用户交…...
计算机的错误计算(二百二十二)
摘要 利用大模型化简计算 实验表明,虽然结果正确,但是,大模型既绕了弯路,又有数值计算错误。 与前面相同,再利用同一个算式看看另外一个大模型的化简与计算能力。 例1. 化简计算摘要中算式。 下面是与一个大模型的…...
音频 PCM 格式 - raw data
文章目录 raw 音频格式:PCM其他音频格式:mp31. 无损压缩音频(类比 PNG 图像)2. 有损压缩音频(类比 JPEG 图像) 试了一下科大讯飞的音频识别云 api,踩了点坑 与本文无关:讯飞的 api 使…...
mybatis(78/134)
前天学了很多,关于java的反射机制,其实跳过了new对象,然后底层生成了字节码,创建了对应的编码。手搓了一遍源码,还是比较复杂的。 <?xml version"1.0" encoding"UTF-8" ?> <!DOCTYPE …...
连接 OpenAI 模型:基础操作
在这一部分中,我们将介绍如何连接 OpenAI 模型,设置 API 密钥,并使用 Spring AI 的 ChatClient 与 OpenAI 模型进行简单的对话。Spring AI 为集成 OpenAI 模型提供了方便的工具,使得开发者能够更轻松地与 GPT 系列模型进行交互。 …...
数据分箱 baggingboosting onehot独热编码 woe编码 sklearn的ensemble(集成学习)
目录 数据分箱就是将连续变量离散化。 bagging&boosting onehot独热编码 独热编码的结果如下: woe编码 WOE编码的基本原理 步骤一:计算WOE 步骤二:应用WOE WOE编码的优点 示例 数据示例 步骤一:计算每个类别的违约…...
企业微信开发010_使用WxJava企业微信开发框架_封装第三方应用企业微信开发003_并且实现多企业授权访问---企业微信开发012
继续来看吧,上一节,已经把config部分,代码都拿过来了: 并且把企业微信第三方应用开发部分,对应的config的配置,mutiltp 代码拿过来了,并且把yml中的配置也给出了. 然后,这里说一下config中的内容,到时候自己看也可以看懂 其实就是封装了,当系统启动,加载企微模块,这个时候,会…...
