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. 核心场景:业务解耦(如订单创建、用户注册后的多业务处理)。
相关推荐
葫芦和十三27 分钟前
图解 MongoDB 21|选举与 failover:Primary 是怎么选出来的
后端·mongodb·agent
GetcharZp1 小时前
26k Star 开源内网穿透神器 NetBird,一分钟实现全球设备互联!
后端
考虑考虑2 小时前
Mybatis实现批量插入
java·后端·mybatis
咖啡八杯2 小时前
GoF设计模式——中介者模式
java·后端·spring·设计模式
lizhongxuan5 小时前
多Agent之间的区别
后端
青石路6 小时前
记一次多JDK版本问题的排查,一坑套一坑,差点没爬上来
java
杨充7 小时前
1.面向对象设计思想
后端
IT_陈寒7 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
systemPro7 小时前
2.6亿条设备数据,历史查询从超时到50ms,我做了什么
后端
要阿尔卑斯吗8 小时前
提示词优化启示:为什么“按顺序输出“比“关键度评分“更有效
后端