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…...
【Spring】Spring启示录
目录 前言 一、示例程序 二、OCP开闭原则 三、依赖倒置原则DIP 四、控制反转IOC 总结 前言 在软件开发的世界里,随着项目的增长和需求的变化,如何保持代码的灵活性、可维护性和扩展性成为了每个开发者必须面对的问题。传统的面向过程或基于类的设计…...
ospf动态路由配置,cost路径调整,ospf认证实验
一、实验拓扑如图: 接口ip配置网络 :10.17.12.* 10.17.13.* ,10.17.23.* 回环接口配置分别为 10.0.1.1 ,10.0.1.2,10.0.1.3对应三台路由器 ar1配置接口ip interface GigabitEthernet0/0/0 ip address 10.17.12.1…...
在Rust应用中访问.ini格式的配置文件
在Rust应用中访问.ini格式的配置文件,你可以使用第三方库,比如 ini 或 config. 下面是一个使用 ini 库的示例,该库允许你读取和解析.ini文件。 使用 ini 库 添加依赖 首先,你需要在你的 Cargo.toml 文件中添加 ini 库的依赖&am…...
批量处理多个模型的预测任务
#!/bin/bash# 检查是否传入必要的参数,若未传入参数则打印用法并退出 if [ "$#" -lt 1 ]; thenecho "用法: $0 <file_path>"echo "示例: $0 /home/aistudio/work/PaddleSeg/city/cityscapes_urls_extracted.txt"exit 1 fi# 读取…...
Java 编程初体验
Java学习资料 Java学习资料 Java学习资料 一、引言 在当今数字化的时代,编程已然成为一项极具价值的技能。而 Java 作为一门广泛应用于企业级开发、移动应用、大数据等众多领域的编程语言,吸引着无数初学者投身其中。当我们初次踏入 Java 编程的世界&…...
element-plus 的table section如何实现单选
如果是单选那么全新的按钮应该隐藏或者不可编辑的状态。但是我没找到改变成不可编辑的方法,只能采取隐藏 <template><!-- 注意要包一层div根元素,否则css样式可能会不生效,原因不详 --><div><el-table ref"proTab…...
【JavaEE进阶】图书管理系统 - 壹
目录 🌲序言 🌴前端代码的引入 🎋约定前后端交互接口 🚩接口定义 🍃后端服务器代码实现 🚩登录接口 🚩图书列表接口 🎄前端代码实现 🚩登录页面 🚩…...
牛客周赛 Round 77 题解
文章目录 A-时间表B-数独数组D-隐匿社交网络E-1or0 A-时间表 签到题 #include <bits/stdc.h> using namespace std;int main() {int a[6] {20250121,20250123,20250126,20250206,20250208,20250211};int n; cin >> n;cout << a[n - 1];return 0; }B-数独数…...
Mybatis配置文件详解
MyBatis通过XML或注解的方式将Java对象与数据库中的记录进行映射,极大地简化了数据访问层的开发。而在MyBatis的核心组成部分中,配置文件扮演着举足轻重的角色。它不仅定义了MyBatis的运行环境,还配置了数据源、事务管理、映射器等关键元素&a…...
《深度揭秘:TPU张量计算架构如何重塑深度学习运算》
在深度学习领域,计算性能始终是推动技术发展的关键因素。从传统CPU到GPU,再到如今大放异彩的TPU(张量处理单元),每一次硬件架构的革新都为深度学习带来了质的飞跃。今天,就让我们深入探讨TPU的张量计算架构…...
Java基础知识总结(二十二)--List接口
List本身是Collection接口的子接口,具备了Collection的所有方法。现在学习List体系特有的共性方法,查阅方法发现List的特有方法都有索引,这是该集合最大的特点。 List:有序(元素存入集合的顺序和取出的顺序一致),元素都…...
[STM32 - 野火] - - - 固件库学习笔记 - - -十二.基本定时器
一、定时器简介 STM32 中的定时器(TIM,Timer)是其最重要的外设之一,广泛用于时间管理、事件计数和控制等应用。 1.1 基本功能 定时功能:TIM定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中…...
算法随笔_27:最大宽度坡
上一篇:算法随笔_26: 按奇偶排序数组-CSDN博客 题目描述如下: 给定一个整数数组 nums,坡是元组 (i, j),其中 i < j 且 nums[i] < nums[j]。这样的坡的宽度为 j - i。 找出 nums 中的坡的最大宽度,如果不存在,返回 0 。 …...
无公网IP 外网访问本地部署 llamafile 大语言模型
llamafile 是一种AI大模型部署(或者说运行)的方案,它的特点就是可以将模型和运行环境打包成一个独立的可执行文件,这样就简化了部署流程。用户只需要下载并执行该文件,无需安装运行环境或依赖库,这大大提高…...
使用PC版本剪映制作照片MV
目录 制作MV模板时长调整拖动边缘缩短法分割删除法变速法整体调整法 制作MV 导入音乐 导入歌词 点击歌词 和片头可以修改字体: 还可以给字幕添加动画效果: 导入照片,自动创建照片轨: 修改片头字幕:增加两条字幕轨&…...
搭建 docxify 静态博客教程
首先,安装 node 环境安装 docxify ,参考官网:https://docsify.js.org/#/zh-cn/ npm i docsify-cli -g新建docs文件夹专门用来放文章,初始化命令 docsify init ./docs就会生成如下两个文件,index.html 入口文件&#…...
汽车OEMs一般出于什么目的来自定义Autosar CP一些内容
汽车OEMs在使用AUTOSAR CP(Classic Platform)协议时,可能会根据自身的特定需求对标准协议进行修改,形成自己的企业标准(企标)。这种修改通常是为了满足特定的硬件平台、功能需求、安全要求或优化性能。以下是一些常见的修改场景和例子: 1. 硬件平台适配 企业可能会根据…...
Vue.js Vuex 模块化管理
Vue.js Vuex 模块化管理 今天咱们来聊聊如何在 Vuex 中进行模块化管理。当你的 Vue.js 应用变得越来越庞大时,单一的状态管理可能会让人头疼。这时候,Vuex 的模块化功能就派上用场了。 为什么需要模块化? 想象一下,如果把所有的…...
分布式光纤应变监测是一种高精度、分布式的监测技术
一、土木工程领域 桥梁结构健康监测 主跨应变监测:在大跨度桥梁的主跨部分,如悬索桥的主缆、斜拉桥的斜拉索和主梁,分布式光纤应变传感器可以沿着这些关键结构部件进行铺设。通过实时监测应变情况,能够精确捕捉到车辆荷载、风荷…...
地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...
