Spring Boot 实现事件监听(监听器+自定义事件)完整指南
Spring Boot 的事件监听机制基于 观察者模式,核心是「自定义事件 + 事件发布者 + 事件监听器」,可实现业务解耦(如订单创建后触发库存扣减、日志记录、消息推送等)。以下是从「基础用法→进阶场景→实战示例」的完整实操,覆盖 Spring Boot 2.x/3.x 版本。
一、核心概念(先理解再上手)
| 组件 | 作用 |
|---|---|
| 事件(Event) | 承载业务数据的载体(如订单创建事件包含订单ID、金额等),需继承 ApplicationEvent |
| 发布者(Publisher) | 发布事件的组件,通过 ApplicationEventPublisher 或 ApplicationContext 发布 |
| 监听器(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. 事件过滤(只处理符合条件的事件)
通过 @EventListener 的 condition 属性,使用 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:方法返回值/参数不符合异步要求(异步方法建议返回
void或Future)。
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) {
// 业务逻辑
}
总结
- Spring Boot 事件监听核心:自定义事件(ApplicationEvent) + 发布器(ApplicationEventPublisher) + 监听器(@EventListener/ApplicationListener);
- 同步监听器:默认方式,阻塞主线程,适合核心业务(如库存扣减);
- 异步监听器:需加
@EnableAsync + @Async,适合耗时操作(如短信、邮件); - 进阶优化:通过
@Order指定执行顺序、condition过滤事件、自定义异步线程池提升性能; - 核心场景:业务解耦(如订单创建、用户注册后的多业务处理)。