Spring Boot 实现事件监听(监听器+自定义事件)完整指南

Spring Boot 实现事件监听(监听器+自定义事件)完整指南

Spring Boot 的事件监听机制基于 观察者模式,核心是「自定义事件 + 事件发布者 + 事件监听器」,可实现业务解耦(如订单创建后触发库存扣减、日志记录、消息推送等)。以下是从「基础用法→进阶场景→实战示例」的完整实操,覆盖 Spring Boot 2.x/3.x 版本。

一、核心概念(先理解再上手)

组件 作用
事件(Event) 承载业务数据的载体(如订单创建事件包含订单ID、金额等),需继承 ApplicationEvent
发布者(Publisher) 发布事件的组件,通过 ApplicationEventPublisherApplicationContext 发布
监听器(Listener) 监听指定事件,事件发布后自动执行处理逻辑(支持同步/异步)

二、快速实现:基础事件监听(同步)

步骤1:定义自定义事件(承载业务数据)

创建继承 ApplicationEvent 的事件类,封装需要传递的业务数据:

java 复制代码
import org.springframework.context.ApplicationEvent;

/**
 * 自定义订单创建事件(示例:订单创建后触发的事件)
 */
public class OrderCreateEvent extends ApplicationEvent {
    // 事件携带的业务数据
    private Long orderId;
    private String orderNo;
    private Double amount;

    // 构造方法(必须调用父类构造器)
    public OrderCreateEvent(Object source, Long orderId, String orderNo, Double amount) {
        super(source); // source:事件源(通常是发布事件的对象)
        this.orderId = orderId;
        this.orderNo = orderNo;
        this.amount = amount;
    }

    // getter/setter
    public Long getOrderId() { return orderId; }
    public String getOrderNo() { return orderNo; }
    public Double getAmount() { return amount; }
}

步骤2:实现事件监听器(3种方式,任选其一)

Spring Boot 支持 3 种监听器实现方式,推荐使用注解式(简洁)。

方式1:注解式监听器(@EventListener,推荐)

无需实现接口,直接在方法上标注 @EventListener,指定监听的事件类型:

java 复制代码
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

/**
 * 订单事件监听器(注解式)
 */
@Component // 必须交给Spring容器管理
public class OrderEventListener {

    /**
     * 监听订单创建事件(同步执行)
     */
    @EventListener(OrderCreateEvent.class) // 指定监听的事件类型
    public void handleOrderCreateEvent(OrderCreateEvent event) {
        // 事件处理逻辑(示例:打印订单信息、扣减库存、记录日志等)
        System.out.println("【同步监听器】接收到订单创建事件:");
        System.out.println("订单ID:" + event.getOrderId());
        System.out.println("订单编号:" + event.getOrderNo());
        System.out.println("订单金额:" + event.getAmount());
        
        // 实际业务逻辑:如扣减库存、发送短信通知、记录操作日志等
        // stockService.deductStock(event.getOrderId());
        // smsService.sendNotify(event.getOrderNo());
    }
}
方式2:实现接口式监听器(ApplicationListener)

实现 ApplicationListener 接口,泛型指定监听的事件类型:

java 复制代码
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

/**
 * 订单事件监听器(接口式)
 */
@Component
public class OrderCreateApplicationListener implements ApplicationListener<OrderCreateEvent> {

    @Override
    public void onApplicationEvent(OrderCreateEvent event) {
        System.out.println("【接口式监听器】接收到订单创建事件:订单编号=" + event.getOrderNo());
    }
}
方式3:手动注册监听器(编程式,适用于动态注册)

在配置类中通过 ApplicationEventMulticaster 手动注册监听器(较少用):

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.core.task.SimpleAsyncTaskExecutor;

@Configuration
public class ListenerConfig {

    @Bean
    public ApplicationEventMulticaster applicationEventMulticaster() {
        SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();
        // 注册监听器(可动态添加/移除)
        multicaster.addApplicationListener(new OrderCreateApplicationListener());
        return multicaster;
    }
}

步骤3:发布事件(触发监听器)

通过 ApplicationEventPublisher 发布事件(注入即可使用):

java 复制代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

/**
 * 测试控制器:发布订单创建事件
 */
@RestController
public class OrderController {

    // 注入事件发布器(Spring内置,直接注入)
    @Autowired
    private ApplicationEventPublisher eventPublisher;

    /**
     * 模拟创建订单,发布事件
     */
    @GetMapping("/createOrder/{orderId}")
    public String createOrder(@PathVariable Long orderId) {
        // 1. 模拟创建订单(业务逻辑)
        String orderNo = "ORDER_" + System.currentTimeMillis();
        Double amount = 99.9;
        System.out.println("订单创建成功:" + orderNo);

        // 2. 发布订单创建事件(触发监听器)
        OrderCreateEvent event = new OrderCreateEvent(this, orderId, orderNo, amount);
        eventPublisher.publishEvent(event); // 发布事件

        return "订单创建成功,事件已发布:" + orderNo;
    }
}

步骤4:测试验证

启动 Spring Boot 项目,访问 http://localhost:8080/createOrder/1001,控制台输出:

复制代码
订单创建成功:ORDER_1710888888888
【同步监听器】接收到订单创建事件:
订单ID:1001
订单编号:ORDER_1710888888888
订单金额:99.9
【接口式监听器】接收到订单创建事件:订单编号=ORDER_1710888888888

三、进阶:异步事件监听(避免阻塞主线程)

默认监听器是同步执行 (发布事件的线程会等待监听器执行完成),若监听器处理耗时逻辑(如调用第三方接口、发送邮件),会阻塞主线程。可通过 @Async 实现异步监听。

步骤1:开启异步支持(主类加注解)

在 Spring Boot 主类上添加 @EnableAsync,开启异步功能:

java 复制代码
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync // 开启异步支持
public class EventListenerDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(EventListenerDemoApplication.class, args);
    }
}

步骤2:监听器方法添加 @Async 注解

在监听方法上标注 @Async,即可异步执行:

java 复制代码
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
public class OrderAsyncEventListener {

    /**
     * 异步监听订单创建事件(不阻塞主线程)
     */
    @Async // 异步执行
    @EventListener(OrderCreateEvent.class)
    public void handleOrderCreateEventAsync(OrderCreateEvent event) {
        try {
            // 模拟耗时操作(如调用第三方支付接口)
            Thread.sleep(3000);
            System.out.println("【异步监听器】接收到订单创建事件:订单编号=" + event.getOrderNo());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

步骤3:测试异步效果

访问 http://localhost:8080/createOrder/1001,控制台会先输出「订单创建成功」,3秒后再输出异步监听器的内容,说明主线程未阻塞。

四、高级用法:事件过滤 + 多监听器执行顺序

1. 事件过滤(只处理符合条件的事件)

通过 @EventListenercondition 属性,使用 SpEL 表达式过滤事件:

java 复制代码
/**
 * 只监听金额大于100元的订单事件
 */
@EventListener(condition = "#event.amount > 100")
public void handleHighAmountOrder(OrderCreateEvent event) {
    System.out.println("【金额过滤监听器】高金额订单:" + event.getOrderNo() + ",金额=" + event.getAmount());
}

2. 指定监听器执行顺序

通过 @Order 注解指定监听器执行优先级(数值越小,执行越靠前):

java 复制代码
// 优先级1(最先执行)
@Order(1)
@EventListener(OrderCreateEvent.class)
public void listener1(OrderCreateEvent event) {
    System.out.println("【监听器1】执行");
}

// 优先级2(后执行)
@Order(2)
@EventListener(OrderCreateEvent.class)
public void listener2(OrderCreateEvent event) {
    System.out.println("【监听器2】执行");
}

五、实战场景:订单创建后的多业务处理

需求

订单创建后,需完成 3 件事:扣减库存(同步)、发送短信通知(异步)、记录操作日志(同步)。

实现代码

java 复制代码
// 1. 库存监听器(同步,高优先级)
@Component
public class StockListener {
    @Order(1)
    @EventListener(OrderCreateEvent.class)
    public void deductStock(OrderCreateEvent event) {
        System.out.println("【库存监听器】扣减订单" + event.getOrderNo() + "的库存");
        // 库存扣减逻辑
    }
}

// 2. 短信监听器(异步)
@Component
public class SmsListener {
    @Async
    @EventListener(OrderCreateEvent.class)
    public void sendSms(OrderCreateEvent event) {
        System.out.println("【短信监听器】给订单" + event.getOrderNo() + "发送通知短信");
        // 短信发送逻辑
    }
}

// 3. 日志监听器(同步,低优先级)
@Component
public class LogListener {
    @Order(2)
    @EventListener(OrderCreateEvent.class)
    public void recordLog(OrderCreateEvent event) {
        System.out.println("【日志监听器】记录订单" + event.getOrderNo() + "创建日志");
        // 日志记录逻辑
    }
}

测试结果

复制代码
订单创建成功:ORDER_1710888888888
【库存监听器】扣减订单ORDER_1710888888888的库存
【日志监听器】记录订单ORDER_1710888888888创建日志
(3秒后)
【短信监听器】给订单ORDER_1710888888888发送通知短信

六、避坑要点

1. 监听器未触发

  • 原因1:监听器类未加 @Component(未交给 Spring 容器管理);
  • 原因2:事件发布时,Spring 容器尚未初始化完成(如在 @PostConstruct 中发布事件,需延迟);
  • 原因3:事件类型匹配错误(如监听器监听 OrderEvent,但发布的是 OrderCreateEvent)。

2. 异步监听器不执行

  • 原因1:未加 @EnableAsync 注解;
  • 原因2:监听器方法抛出未捕获的异常(异步线程异常不会阻塞主线程,需手动日志记录);
  • 原因3:方法返回值/参数不符合异步要求(异步方法建议返回 voidFuture)。

3. 同步监听器阻塞主线程

  • 解决:耗时操作(如调用第三方接口、IO 操作)必须用异步监听器(@Async);
  • 优化:配置异步线程池,避免默认线程池耗尽。

七、进阶优化:自定义异步线程池

默认异步线程池为 SimpleAsyncTaskExecutor(每次创建新线程),生产环境建议自定义线程池:

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
@EnableAsync
public class AsyncConfig {

    /**
     * 自定义异步线程池
     */
    @Bean("eventExecutor") // 指定线程池名称
    public Executor eventExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5); // 核心线程数
        executor.setMaxPoolSize(10); // 最大线程数
        executor.setQueueCapacity(20); // 队列容量
        executor.setKeepAliveSeconds(60); // 空闲线程存活时间
        executor.setThreadNamePrefix("event-"); // 线程名前缀
        // 拒绝策略:队列满后,由调用线程执行(避免任务丢失)
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

监听器指定自定义线程池

java 复制代码
@Async("eventExecutor") // 指定使用自定义线程池
@EventListener(OrderCreateEvent.class)
public void handleOrderCreateEventAsync(OrderCreateEvent event) {
    // 业务逻辑
}

总结

  1. Spring Boot 事件监听核心:自定义事件(ApplicationEvent) + 发布器(ApplicationEventPublisher) + 监听器(@EventListener/ApplicationListener)
  2. 同步监听器:默认方式,阻塞主线程,适合核心业务(如库存扣减);
  3. 异步监听器:需加 @EnableAsync + @Async,适合耗时操作(如短信、邮件);
  4. 进阶优化:通过 @Order 指定执行顺序、condition 过滤事件、自定义异步线程池提升性能;
  5. 核心场景:业务解耦(如订单创建、用户注册后的多业务处理)。
相关推荐
我真会写代码2 小时前
深入解析Java ArrayList与HashMap扩容机制:原理、差异与实战避坑
java·数据结构·算法·集合
Victor3562 小时前
MongoDB(48)什么是主从复制?
后端
代码探秘者2 小时前
【大模型应用】3.分块入门
java·后端·python·spring
Victor3562 小时前
MongoDB(49)什么是副本集?
后端
橙子家2 小时前
Serilog 日志库简单实践(五)数据库 Sinks(.net8)
后端
独断万古他化3 小时前
【抽奖系统开发实战】Spring Boot 抽奖模块全解析:MQ 异步处理、缓存信息、状态扭转与异常回滚
java·spring boot·redis·后端·缓存·rabbitmq·mvc
weisian1513 小时前
Java并发编程--12-读写锁与StampedLock:高并发读场景下的性能优化利器
java·开发语言·性能优化·读写锁·stampedlock
RDCJM3 小时前
Spring Boot + Vue 全栈开发实战指南
vue.js·spring boot·后端
卷福同学9 小时前
【养虾日记】Openclaw操作浏览器自动化发文
人工智能·后端·算法