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

Spring Boot 中的事件发布与监听:深入理解 ApplicationEventPublisher(附Demo)

目录

  • 前言
  • 1. 基本知识
  • 2. Demo
  • 3. 实战代码

前言

🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF

基本的Java知识推荐阅读:

  1. java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全)
  2. 【Java项目】实战CRUD的功能整理(持续更新)

1. 基本知识

ApplicationEventPublisher 是 Spring 框架中一个功能接口(@FunctionalInterface),用于发布事件

是 Spring 的事件驱动模型的核心部分,开发者可以通过实现这个接口或通过 Spring 提供的现成实现来发布和管理事件

基本知识如下:

  1. 事件驱动模型
    Spring 提供了一个内置的事件模型,通过事件发布者(ApplicationEventPublisher)和事件监听器(@EventListenerApplicationListener
    事件可以是框架提供的(例如:ContextRefreshedEvent),也可以是用户自定义的事件

  2. @FunctionalInterface 注解
    声明此接口是函数式接口,只有一个抽象方法:publishEvent(Object event)
    可以使用 lambda 表达式或方法引用来实现

  3. 事件类型
    支持两种事件对象:
    ApplicationEvent 类型
    非 ApplicationEvent 类型(会被包装成 PayloadApplicationEvent)

  4. 事件传播特点
    异步/同步:事件的传播方式取决于事件监听器的实现,发布者本身不决定事件的执行方式
    高效性建议:事件监听器应尽量快速完成任务,对于耗时操作建议使用异步处理

主要方法解析

  1. publishEvent(ApplicationEvent event)
    接收 ApplicationEvent 类型事件
    实际是将事件转换为 Object 类型后调用 publishEvent(Object event) 方法
  2. 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" |
+-----------------------------+
  1. 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 {

    @Autowired
    private EventPublisherService eventPublisherService;

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Override
    public void run(String... args) {
        // 发布自定义事件
        eventPublisherService.publishEvent("Hello, ApplicationEventPublisher!");
    }
}
  1. 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;
    }
}
  1. CustomEventListener (事件监听器)
    使用 @EventListener 注解监听特定事件
    方法参数即为监听的事件类型
package com.example.demo;

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class CustomEventListener {

    @EventListener
    public void handleCustomEvent(CustomEvent event) {
        System.out.println("Received custom event: " + event.getMessage());
    }
}
  1. 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 {

    @Autowired
    private 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!

截图如下:

在这里插入图片描述

扩展功能

  1. 异步事件监听:在监听方法上加 @Async,并在主类中启用异步:
@SpringBootApplication
@EnableAsync
public class DemoApplication { ... }
  1. 多监听器:可以定义多个监听器监听相同事件,Spring 会自动分发到每个监听器

  2. 自定义事件的继承:可以继承 CustomEvent 定义不同类型的事件,以实现事件的多态性

3. 实战代码

比如发送邮件,如果单纯跟接口进行绑定,代码后续扩展优化会非常冗余!

使用 事件机制(applicationContext.publishEvent 和监听器)

有如下好处:

  1. 解耦业务逻辑
    通过事件机制,发送邮件的逻辑和业务逻辑分离:
    业务代码只需要关心触发“发送邮件”这一行为(发布事件)
    实际的邮件发送逻辑由监听器单独处理

这种解耦方式的优势:

  • 更清晰的代码职责:业务代码不会夹杂具体的邮件发送逻辑
  • 方便扩展:如果未来需要增加更多处理逻辑(如记录日志、重试机制等),可以直接扩展监听器,而不用修改业务代码

而且最主要的是:

  1. 支持异步操作
    在监听器上添加了 @Async 注解,可以让事件的处理逻辑异步执行:
@Async
@EventListener
public void onMessage(MailSendMessage message) {
    log.info("[onMessage][消息内容({})]", message);
    mailSendService.doSendMail(message);
}

这样,主线程可以迅速完成主要业务逻辑(如创建发送日志)并返回,而不用等待邮件发送完成

对于高并发场景,这种异步机制非常有用

如果直接在业务方法中调用发送函数,就无法方便地实现异步处理,可能会导致:

  • 性能问题:主线程被邮件发送操作阻塞
  • 用户体验问题:如果邮件发送需要较长时间,业务响应时间会变长
  1. 灵活性和可扩展性
    使用事件机制后,邮件发送的逻辑变成了“事件订阅者”:
    可以轻松增加或移除其他监听器,而不会影响现有的业务代码
    例如,除了发送邮件外,还可以添加监听器发送短信、推送通知、记录操作日志等
    可以根据不同的事件类型(不同的事件类)触发不同的逻辑

上述功能比较抽象,以实际代码为例:

如果做一个接口,发送邮件,信息量很大的时候,需要等这个邮件信息,待结果返回,才可以给客户!

  1. 发送邮件的代码和业务代码紧密耦合,修改或扩展会很麻烦
  2. 如果要实现异步发送,还需要自己额外管理线程池或异步任务,增加复杂度
  3. 如果未来需要在发送邮件时附加其他逻辑(如发送通知),业务代码会变得越来越复杂
public Long sendSingleMail(...) {
    ...
    // 直接发送邮件
    mailSendService.doSendMail(...);
    return sendLogId;
}

以下是结合ApplicationEventPublisher,下述代码以ruoyi-vue-pro代码为例子进行讲解

整体目录如下:

在这里插入图片描述

使用 @EventListener 注解监听特定事件

方法参数即为监听的事件类型
方法参数即为监听的事件类型
方法参数即为监听的事件类型

/**
 * 针对 {@link MailSendMessage} 的消费者
 */
@Component
@Slf4j
public class MailSendConsumer {

    @Resource
    private 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 {

    @Resource
    private 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);
    }

}

实际主体代码是直接使用发送消息,不用对接接收消息,接收消息是直接监听就好!

业务逻辑代码:

在这里插入图片描述


http://www.kler.cn/a/521031.html

相关文章:

  • TikTok 推出了一款 IDE,用于快速构建 AI 应用
  • 深入理解 SQL 中的子查询
  • SimpleFOC STM32教程10|基于STM32F103+CubeMX,速度闭环控制(有电流环)
  • 【Python】第五弹---深入理解函数:从基础到进阶的全面解析
  • 自动驾驶中的多传感器时间同步
  • MacOS安装Docker battery-historian
  • 用Devc++与easyx一步一步做游戏[启动界面部分]-解决hover闪烁问题及优化
  • Arduino大师练成手册 -- 控制 PN532 NFC 模块
  • 第 25 场 蓝桥月赛
  • 什么是AI Agent?
  • Vue.js 什么是 Vuex?
  • 基于新年视角下的城市人流数据分析
  • Baklib赋能下的内容中台智能化推荐系统解析与展望
  • Mac cursor设置jdk、Maven版本
  • Qt中QVariant的使用
  • 【橘子ES】使用docker搭建ELK环境
  • 2025美赛数学建模C题:奥运奖牌榜模型——思路+代码+模型
  • 二维数组一
  • Linux系统之ifconfig命令的基本使用
  • 2274. 不含特殊楼层的最大连续楼层数
  • 嵌入式C语言:结构体对齐
  • 前部分知识复习01
  • SpringBoot使用 easy-captcha 实现验证码登录功能
  • spring spring-boot spring-cloud发布以及适配
  • SAP MM 记录一次SAP外协采购收货提示 这种物料的特殊库存 O 0100003359 14019002不存在的问题
  • PTMD2.0-疾病相关的翻译后修饰数据库