SpringBoot ApplicationEvent:事件发布与监听机制

news2025/5/18 15:03:53

在这里插入图片描述

文章目录

    • 引言
    • 一、事件机制的基本概念
    • 二、创建自定义事件
      • 2.1 定义事件类
      • 2.2 发布事件
      • 2.3 简化的事件发布
    • 三、创建事件监听器
      • 3.1 使用@EventListener注解
      • 3.2 实现ApplicationListener接口
      • 3.3 监听非ApplicationEvent类型的事件
    • 四、事件监听的高级特性
      • 4.1 条件事件监听
      • 4.2 异步事件监听
      • 4.3 事件监听的顺序控制
      • 4.4 事务事件监听
    • 五、Spring内置事件
      • 5.1 常见内置事件
      • 5.2 监听内置事件示例
    • 总结

引言

在现代企业级应用开发中,各个组件间的解耦和灵活通信变得越来越重要。Spring Framework提供了强大的事件机制,允许应用中的各个组件以松耦合的方式进行交互。SpringBoot继承并增强了这一机制,通过ApplicationEvent及其相关组件,为开发者提供了一套优雅的事件发布与监听框架。本文将深入探讨SpringBoot的事件机制,介绍其核心概念、实现方法及最佳实践,帮助开发者构建更加灵活、可维护的应用架构。

一、事件机制的基本概念

Spring的事件机制基于观察者设计模式,主要由三个核心组件构成:事件(Event)、事件发布者(Publisher)和事件监听器(Listener)。其工作流程是:事件发布者发布特定类型的事件,系统将事件传递给所有对该类型事件感兴趣的监听器,监听器随后执行相应的处理逻辑。

这种机制的最大优势在于实现了事件发布者与事件处理者之间的解耦,使得系统更加模块化,便于扩展和维护。在SpringBoot中,这一机制通过ApplicationEvent类及相关接口得以实现。

二、创建自定义事件

2.1 定义事件类

自定义事件需要继承SpringBoot的ApplicationEvent类,该类定义了事件的基本属性和行为:

package com.example.demo.event;

import org.springframework.context.ApplicationEvent;

/**
 * 用户注册事件
 * 在用户注册成功后发布,用于执行后续操作
 */
public class UserRegisteredEvent extends ApplicationEvent {
    
    // 用户ID
    private final Long userId;
    // 用户名
    private final String username;
    
    /**
     * 构造函数
     * @param source 事件源(发布事件的对象)
     * @param userId 注册用户ID
     * @param username 注册用户名
     */
    public UserRegisteredEvent(Object source, Long userId, String username) {
        super(source);
        this.userId = userId;
        this.username = username;
    }
    
    public Long getUserId() {
        return userId;
    }
    
    public String getUsername() {
        return username;
    }
}

这个例子定义了一个用户注册事件,它在用户注册成功后被发布,携带了用户ID和用户名信息,可以被其他组件监听并作出响应。

2.2 发布事件

在SpringBoot中,发布事件主要通过ApplicationEventPublisher接口完成,该接口通常通过依赖注入获取:

package com.example.demo.service;

import com.example.demo.event.UserRegisteredEvent;
import com.example.demo.model.User;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;

/**
 * 用户服务
 * 负责用户相关业务逻辑,并在适当时机发布事件
 */
@Service
public class UserService implements ApplicationEventPublisherAware {
    
    private ApplicationEventPublisher eventPublisher;
    
    /**
     * 实现ApplicationEventPublisherAware接口的方法
     * Spring会自动注入ApplicationEventPublisher
     */
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }
    
    /**
     * 用户注册方法
     * @param username 用户名
     * @param password 密码
     * @return 注册成功的用户对象
     */
    public User registerUser(String username, String password) {
        // 执行用户注册逻辑
        User newUser = saveUser(username, password);
        
        // 注册成功后,发布用户注册事件
        eventPublisher.publishEvent(new UserRegisteredEvent(this, newUser.getId(), newUser.getUsername()));
        
        return newUser;
    }
    
    /**
     * 保存用户信息(模拟方法)
     */
    private User saveUser(String username, String password) {
        // 实际项目中会将用户信息保存到数据库
        User user = new User();
        user.setId(1L); // 模拟ID
        user.setUsername(username);
        // 省略密码加密等安全处理
        return user;
    }
}

除了实现ApplicationEventPublisherAware接口外,还可以直接注入ApplicationEventPublisher:

@Service
public class AlternativeUserService {
    
    private final ApplicationEventPublisher eventPublisher;
    
    @Autowired
    public AlternativeUserService(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }
    
    public User registerUser(String username, String password) {
        // 注册逻辑
        User newUser = saveUser(username, password);
        
        // 发布事件
        eventPublisher.publishEvent(new UserRegisteredEvent(this, newUser.getId(), newUser.getUsername()));
        
        return newUser;
    }
    
    // 其他方法...
}

2.3 简化的事件发布

从Spring 4.2开始,还可以直接发布任意对象作为事件,Spring会自动将其包装为PayloadApplicationEvent:

@Service
public class SimpleUserService {
    
    private final ApplicationEventPublisher eventPublisher;
    
    @Autowired
    public SimpleUserService(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }
    
    public User registerUser(String username, String password) {
        // 注册逻辑
        User newUser = saveUser(username, password);
        
        // 直接发布对象作为事件
        eventPublisher.publishEvent(newUser);
        
        return newUser;
    }
    
    // 其他方法...
}

三、创建事件监听器

SpringBoot提供了多种方式来创建事件监听器,以下是几种常见方法:

3.1 使用@EventListener注解

最简洁的方式是使用@EventListener注解:

package com.example.demo.listener;

import com.example.demo.event.UserRegisteredEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

/**
 * 用户注册事件监听器
 * 负责处理用户注册后的操作
 */
@Component
public class UserRegistrationListener {
    
    private static final Logger logger = LoggerFactory.getLogger(UserRegistrationListener.class);
    
    /**
     * 处理用户注册事件
     * @param event 用户注册事件
     */
    @EventListener
    public void handleUserRegistration(UserRegisteredEvent event) {
        logger.info("新用户注册: ID={}, 用户名={}", event.getUserId(), event.getUsername());
        
        // 执行用户注册后的操作,例如:
        // 1. 发送欢迎邮件
        sendWelcomeEmail(event.getUsername());
        
        // 2. 创建默认用户设置
        createDefaultUserSettings(event.getUserId());
        
        // 3. 记录用户注册统计
        updateRegistrationStatistics();
    }
    
    private void sendWelcomeEmail(String username) {
        logger.info("发送欢迎邮件给: {}", username);
        // 邮件发送逻辑
    }
    
    private void createDefaultUserSettings(Long userId) {
        logger.info("为用户 {} 创建默认设置", userId);
        // 创建默认设置逻辑
    }
    
    private void updateRegistrationStatistics() {
        logger.info("更新用户注册统计数据");
        // 更新统计逻辑
    }
}

3.2 实现ApplicationListener接口

另一种方式是实现ApplicationListener接口:

package com.example.demo.listener;

import com.example.demo.event.UserRegisteredEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
 * 使用ApplicationListener接口的事件监听器
 */
@Component
public class EmailNotificationListener implements ApplicationListener<UserRegisteredEvent> {
    
    private static final Logger logger = LoggerFactory.getLogger(EmailNotificationListener.class);
    
    @Override
    public void onApplicationEvent(UserRegisteredEvent event) {
        logger.info("ApplicationListener: 处理用户注册事件");
        
        // 执行事件处理逻辑
        String emailContent = String.format(
            "尊敬的%s,欢迎注册我们的服务!您的账号已创建成功。",
            event.getUsername()
        );
        
        logger.info("准备发送的邮件内容: {}", emailContent);
        // 实际发送邮件的代码...
    }
}

3.3 监听非ApplicationEvent类型的事件

如前所述,从Spring 4.2开始,可以监听任意类型的对象:

@Component
public class GenericEventListener {
    
    private static final Logger logger = LoggerFactory.getLogger(GenericEventListener.class);
    
    @EventListener
    public void handleUserEvent(User user) {
        logger.info("接收到User对象事件: {}", user.getUsername());
        // 处理逻辑
    }
    
    // 可以添加多个监听方法,每个方法处理不同类型的事件
    @EventListener
    public void handleOrderEvent(Order order) {
        logger.info("接收到Order对象事件: ID={}", order.getId());
        // 处理逻辑
    }
}

四、事件监听的高级特性

4.1 条件事件监听

可以使用SpEL表达式指定事件监听的条件:

@Component
public class ConditionalEventListener {
    
    private static final Logger logger = LoggerFactory.getLogger(ConditionalEventListener.class);
    
    /**
     * 条件事件监听 - 只处理VIP用户的注册事件
     */
    @EventListener(condition = "#event.userType == 'VIP'")
    public void handleVipUserRegistration(UserRegisteredEvent event) {
        logger.info("处理VIP用户注册: {}", event.getUsername());
        // VIP用户特殊处理逻辑
    }
}

4.2 异步事件监听

默认情况下,事件处理是同步的,可能会阻塞发布者。通过添加@Async注解,可以实现异步事件处理:

package com.example.demo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;

@Configuration
@EnableAsync  // 启用异步支持
public class AsyncConfig {
    // 异步配置...
}
package com.example.demo.listener;

import com.example.demo.event.UserRegisteredEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

/**
 * 异步事件监听器
 */
@Component
public class AsyncEventListener {
    
    private static final Logger logger = LoggerFactory.getLogger(AsyncEventListener.class);
    
    /**
     * 异步处理用户注册事件
     * 适用于耗时操作,避免阻塞主线程
     */
    @EventListener
    @Async
    public void handleUserRegistrationAsync(UserRegisteredEvent event) {
        logger.info("异步处理用户注册事件,线程: {}", Thread.currentThread().getName());
        
        try {
            // 模拟耗时操作
            Thread.sleep(2000);
            logger.info("完成用户 {} 的异步处理", event.getUsername());
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            logger.error("异步处理被中断", e);
        }
    }
}

4.3 事件监听的顺序控制

当多个监听器处理同一个事件时,可以使用@Order注解控制执行顺序:

@Component
public class OrderedEventListeners {
    
    private static final Logger logger = LoggerFactory.getLogger(OrderedEventListeners.class);
    
    @EventListener
    @Order(1)  // 最高优先级,最先执行
    public void handleUserRegistrationFirst(UserRegisteredEvent event) {
        logger.info("第一步处理: {}", event.getUsername());
        // 处理逻辑
    }
    
    @EventListener
    @Order(2)  // 第二执行
    public void handleUserRegistrationSecond(UserRegisteredEvent event) {
        logger.info("第二步处理: {}", event.getUsername());
        // 处理逻辑
    }
    
    @EventListener
    @Order(Integer.MAX_VALUE)  // 最低优先级,最后执行
    public void handleUserRegistrationLast(UserRegisteredEvent event) {
        logger.info("最后步骤处理: {}", event.getUsername());
        // 处理逻辑
    }
}

4.4 事务事件监听

在事务环境下,可能需要在事务成功提交后才执行某些操作。Spring提供了@TransactionalEventListener注解:

package com.example.demo.listener;

import com.example.demo.event.UserRegisteredEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;

/**
 * 事务事件监听器
 */
@Component
public class TransactionalListener {
    
    private static final Logger logger = LoggerFactory.getLogger(TransactionalListener.class);
    
    /**
     * 事务提交后处理事件
     * 确保只有在事务成功提交后才执行后续操作
     */
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleUserRegistrationAfterCommit(UserRegisteredEvent event) {
        logger.info("事务提交后处理用户注册: {}", event.getUsername());
        // 例如:发送消息到外部系统,此操作只应在事务成功后执行
    }
    
    /**
     * 事务回滚后处理事件
     */
    @TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
    public void handleUserRegistrationAfterRollback(UserRegisteredEvent event) {
        logger.info("事务回滚后处理: {}", event.getUsername());
        // 例如:记录失败原因,发送警报等
    }
}

五、Spring内置事件

Spring提供了多种内置事件,这些事件在特定时刻自动发布:

5.1 常见内置事件

  • ContextRefreshedEvent: 当ApplicationContext初始化或刷新时发布
  • ContextStartedEvent: 当ApplicationContext启动时发布
  • ContextStoppedEvent: 当ApplicationContext停止时发布
  • ContextClosedEvent: 当ApplicationContext关闭时发布

5.2 监听内置事件示例

package com.example.demo.listener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

/**
 * Spring内置事件监听器
 */
@Component
public class SystemEventListener {
    
    private static final Logger logger = LoggerFactory.getLogger(SystemEventListener.class);
    
    /**
     * 监听应用上下文刷新事件
     * 适合执行应用启动后的初始化工作
     */
    @EventListener
    public void handleContextRefresh(ContextRefreshedEvent event) {
        logger.info("应用上下文已刷新,应用ID: {}", event.getApplicationContext().getId());
        
        // 执行系统初始化逻辑
        // 例如:预加载缓存,初始化资源等
        initializeSystemResources();
    }
    
    private void initializeSystemResources() {
        logger.info("初始化系统资源...");
        // 初始化代码
    }
}

总结

Spring Boot的事件机制为应用提供了强大的组件间通信能力,使开发者能够构建松耦合、高内聚的系统。通过事件发布与监听,业务逻辑可以被合理分割,主流程专注于核心操作,而次要或辅助操作则通过事件异步处理,从而提高系统的可维护性和扩展性。在实际应用中,事件机制尤其适用于以下场景:用户注册后发送欢迎邮件、订单创建后进行库存检查、数据变更后更新缓存、业务操作完成后发送通知等。这些操作与主流程关联但又相对独立,通过事件机制处理可以使代码结构更加清晰。随着微服务架构的普及,Spring Boot的事件机制也可以与消息队列等技术结合,实现跨服务的事件驱动架构。开发者可以在服务内部使用ApplicationEvent处理本地事件,而对于需要跨服务通信的场景,则可以将事件转发到消息队列,实现更大范围的解耦。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2337901.html

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

相关文章

广东2024信息安全管理与评估一阶段答案截图

2023-2024 学年广东省职业院校技能大赛 高等职业教育组 信息安全管理与评估 赛题一 模块一 网络平台搭建与设备安全防护 一、 比赛时间 本阶段比赛时间为 180 分钟。 二、 赛项信息 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 第一…

安卓手机如何改ip地址教程

对于安卓手机用户而言&#xff0c;ip修改用在电商、跨境电商、游戏搬砖、社交软件这些需要开多个账号的项目。因为多个设备或账号又不能在同一ip网络下&#xff0c;所以修改手机的IP地址防检测成为一个必要的操作。以下是在安卓手机上更改IP地址的多种方法及详细步骤&#xff0…

(51单片机)LCD显示数据存储(DS1302时钟模块教学)(LCD1602教程)(独立按键教程)(延时函数教程)(I2C总线认识)(AT24C02认识)

目录 演示视频&#xff1a; 源代码 main.c LCD1602.c LCD1602.h AT24C02.c AT24C02.h Key.c Key.h I2C.c I2C.h Delay.c Delay.h 代码解析与教程&#xff1a; Dealy模块 LCD1602模块 Key模块 I2C总线模块 AT24C02模块 /E2PROM模块 main模块 演示视频&#xff1a; &…

STL简介 + string【上】

一 . STL简介 1.1 什么是STL STL&#xff08;standard template libaray - 标准模板库) : 是C标准库的重要组成部分 &#xff0c; 不仅是一个可复用的组件库 &#xff0c; 而且是一个包罗 数据结构 与 算法 的软件框架 。 注意 &#xff1a; 是标准库的一部分 &#xff…

【Bluedroid】A2DP Sink播放流程源码分析(二)

接上一篇继续分析:【Bluedroid】A2DP Sink播放流程源码分析(一)_安卓a2dp sink播放流程-CSDN博客 AVDTP接收端(Sink)流事件处理 bta_av_sink_data_cback 是 Bluedroid 中 A2DP Sink 角色的 AVDTP 数据回调函数,负责处理接收端的音频数据事件,将底层接收到的音频数据传递…

redis利用备忘录

fofa: icon_hash"864611937" 防护&#xff1a; redis的安全设置&#xff1a;设置完毕&#xff0c;需要重加载配置文件启动redis 1.绑定内网ip地址进行访问 2. requirepass设置redis密码 3.保护模式开启protected-mode开启&#xff08;默认开启&#xff09; 4.最好把…

SAP系统中MD01与MD02区别

知识点普及&#xff0d;MD01与MD02区别 1、从日常业务中&#xff0c;我们都容易知道MD01是运行全部物料&#xff0c;MD02是运行单个物料 2、在做配置测试中&#xff0c;也出现过MD02可以跑出物料&#xff0c;但是MD01跑不出的情况。 3、MD01与MD02的差异: 3.1、只要在物料主数…

ubuntu学习day3

3 编译与调试 3.1 gcc/g编译器 当我们进行编译的时候&#xff0c;要使用一系列的工具&#xff0c;我们称之为工具链。SDK就是编译工具链的简写&#xff0c;我们所使用的是gcc系列编译工具链。使用-v参数来查看gcc的版本&#xff0c;从而确定某些语法特性是否可用&#xff0c;…

6.8.最小生成树

一.复习&#xff1a; 1.生成树&#xff1a; 对于一个连通的无向图&#xff0c;假设图中有n个顶点&#xff0c;如果能找到一个符合以下要求的子图&#xff1a; 子图中包含图中所有的顶点&#xff0c;同时各个顶点保持连通&#xff0c; 而且子图的边的数量只有n-1条&#xff0…

QT中栅格模式探索

1、Qt中选择了栅格模式&#xff0c;如下图所示&#xff1a; 2、在进行整个大的UI界面布局时&#xff0c;需了解每个控件所需要选择的属性sizePolicy。 sizePolicy包含如下几种选择&#xff1a; 3、举个例子&#xff1a;此时整个UI界面&#xff0c;我采用了栅格模式&#xf…

C++入门基础:命名空间,缺省参数,函数重载,输入输出

命名空间&#xff1a; C语言是基于C语言的&#xff0c;融入了面向对象编程思想&#xff0c;有了很多有用的库&#xff0c;所以接下来我们将学习C如何优化C语言的不足的。 在C/C语言实践中&#xff0c;在全局作用域中变量&#xff0c;函数&#xff0c;类会有很多&#xff0c;这…

tomcat 的安装与启动

文章目录 tomcat 服务器安装启动本地Tomcat服务器 tomcat 服务器安装 https://tomcat.apache.org/下载 Tomcat 10.0.X 启动本地Tomcat服务器 进入 Tomcat 的 bin

算法-堆+单调栈

堆 首先堆在我们的Java中我们的是一个优先队列类 PriorityQueue 然后我们要弄最大堆和最小堆 最大堆&#xff1a; PriorityQueue<Integer> pq new PriorityQueue<Integer>((a, b) -> b - a); 最小堆&#xff1a; PriorityQueue<Integer> pq new P…

物联网平台管理系统

物联网平台管理系统概述 物联网平台管理系统是物联网架构中的核心枢纽&#xff0c;承担着承上启下的关键作用。它向下连接各类物联网设备&#xff0c;实现设备的接入、管理与控制&#xff1b;向上为应用开发提供统一的数据接口和共性模块工具&#xff0c;支撑起各种丰富多彩的…

STM32CubeMX-H7-15-SPI通信协议读写W25Q64

前言 SPI&#xff08;Serial Peripheral Interface&#xff09;通信协议是一种高速、全双工、同步的串行通信协议 本篇文章就使用W25Q64模块来学习SPI,包括软件SPI代码的编写&#xff0c;硬件SPI&#xff0c;中断SPI和DMASPI SPI的应用场景和模块 &#xff01;这里是抄AI的&a…

【软考】论devops在企业信息系统开发中的应用

摘要&#xff1a; 随着互联网的不断发展&#xff0c;各行各业都在建设自己的企业信息系统&#xff0c;而随着业务的不断升级和复杂化&#xff0c;系统的更新迭代速度越来越快&#xff0c;系统也越来越复杂。对于信息系统开发者&#xff0c;架构师&#xff0c;管理者&#xff0c…

生物化学笔记:医学免疫学原理22 肿瘤及肿瘤治疗

肿瘤及肿瘤治疗 免疫疗法 CAR-T细胞介绍

JVM考古现场(二十二):降维打击·用二向箔优化内存模型

"警报&#xff01;三维堆内存正在经历二维化坍缩&#xff01;" 我腰间的玄铁令突然震动&#xff0c;在蜀山剑派的量子剑阵中投射出诡异的曼德博分形——这是三体文明发动降维打击的铁证&#xff01; 楔子&#xff1a;二向箔奇点降临 昆仑镜监控日志&#xff1a; // …

操作系统-PV

&#x1f9e0; 背景&#xff1a;为什么会有 PV&#xff1f; 类比&#xff1a;内存&#xff08;生产者&#xff09; 和 CPU&#xff08;消费者&#xff09; 内存 / IO / 磁盘 / 网络下载 → 不断“生产数据” 例如&#xff1a;读取文件、下载视频、从数据库加载信息 CPU → 负…

医院数据中心智能化数据上报与调数机制设计

针对医院数据中心的智能化数据上报与调数机制设计,需兼顾数据安全性、效率性、合规性及智能化能力。以下为系统性设计方案,分为核心模块、技术架构和关键流程三部分: 一、核心模块设计 1. 数据上报模块 子模块功能描述多源接入层对接HIS/LIS/PACS/EMR等异构系统,支持API/E…