Spring 事件实战及进阶特性
文章目录
- 一、核心概念与核心组件
- 1. 核心组件说明
- 2. 核心设计思想
- 二、基本使用步骤(Spring Boot 环境)
- 1. 步骤 1:定义事件(可选,Spring 4.2)
- 2. 步骤 2:发布事件
- 3. 步骤 3:监听事件(注解式,推荐)
- 三、进阶特性(实际开发必备)
- 1. 同步 vs 异步事件(核心进阶)
- 异步事件配置步骤:
- 2. 事件监听顺序
- 3. 事务绑定事件(@TransactionalEventListener)
- 4. 条件监听(仅满足条件时执行)
- 5. 自定义事件广播器
- 四、实战案例:电商订单事件流
- 业务场景:
- 完整代码:
- 核心优势:
- 五、常见问题与注意事项
Spring 事件是基于观察者设计模式的轻量级解耦机制,核心作用是分离事件发布者与订阅者,实现模块间无直接依赖的通信(如订单创建后同步发送通知、更新库存等场景)。以下从核心概念、使用步骤、进阶特性、实战案例等维度展开详细介绍:
一、核心概念与核心组件
Spring 事件体系的核心是 4 个组件,遵循 “发布 - 订阅” 模型:
1. 核心组件说明
| 组件名称 | 作用描述 | 核心接口 / 类 |
|---|---|---|
| 事件(Event) | 传递的消息载体,包含事件触发的上下文信息(如订单 ID、操作人等) | ApplicationEvent(抽象基类)、PayloadApplicationEvent(Spring 4.2 + 内置,无需自定义事件类) |
| 事件发布者(Publisher) | 触发并发布事件的组件(如订单服务、用户服务) | ApplicationEventPublisher(接口)、ApplicationContext(间接实现该接口,可直接注入使用) |
| 事件监听器(Listener) | 订阅并处理事件的组件(如通知服务、日志服务) | ApplicationListener(接口)、@EventListener(注解,Spring 4.2 + 推荐)、@TransactionalEventListener(事务绑定监听) |
| 事件广播器(Multicaster) | 负责将事件分发到所有订阅的监听器,是事件机制的 “中转站” | ApplicationEventMulticaster(核心接口),默认实现SimpleApplicationEventMulticaster |
2. 核心设计思想
-
解耦:发布者无需知道订阅者的存在,订阅者也无需依赖发布者,通过事件间接通信;
-
可扩展:新增业务逻辑(如订单创建后新增 “积分发放”)时,只需新增监听器,无需修改发布者代码;
-
同步默认:默认事件是同步执行(发布者阻塞直到所有监听器执行完毕),支持异步配置。
二、基本使用步骤(Spring Boot 环境)
Spring 4.2 + 后简化了事件使用,无需实现接口,注解式监听成为主流,以下是最常用的 “注解式事件” 完整流程:
1. 步骤 1:定义事件(可选,Spring 4.2)
-
自定义事件需继承
ApplicationEvent,封装事件上下文; -
若无需自定义事件类,可直接使用 Spring 内置的
PayloadApplicationEvent(传递任意类型的 “负载” 数据)。
// 自定义事件(场景:订单创建事件)public class OrderCreatedEvent extends ApplicationEvent {// 事件携带的上下文信息(如订单ID、用户ID、订单金额)private final Long orderId;private final Long userId;private final BigDecimal amount;// 构造方法:参数1为事件源(通常是发布者本身,可传null),后续为自定义上下文public OrderCreatedEvent(Object source, Long orderId, Long userId, BigDecimal amount) {super(source);this.orderId = orderId;this.userId = userId;this.amount = amount;}// getter方法(监听器需要获取上下文)public Long getOrderId() { return orderId; }public Long getUserId() { return userId; }public BigDecimal getAmount() { return amount; }}
2. 步骤 2:发布事件
通过注入ApplicationEventPublisher(或ApplicationContext,因为其实现了该接口)发布事件:
@Servicepublic class OrderService {// 注入事件发布器(Spring自动注入,无需手动配置)@Autowiredprivate ApplicationEventPublisher eventPublisher;// 订单创建业务:创建订单后发布事件@Transactionalpublic void createOrder(OrderDTO orderDTO) {// 1. 核心业务:保存订单到数据库Long orderId = saveOrderToDB(orderDTO); // 模拟保存逻辑// 2. 发布事件:通知其他模块(如通知服务、积分服务)eventPublisher.publishEvent(new OrderCreatedEvent(this, orderId, orderDTO.getUserId(), orderDTO.getAmount()));// (可选)使用内置PayloadApplicationEvent,无需自定义事件类// eventPublisher.publishEvent(// new PayloadApplicationEvent<>(this, orderDTO) // 直接传递订单DTO作为负载// );}private Long saveOrderToDB(OrderDTO orderDTO) {// 模拟数据库保存逻辑,返回订单IDreturn System.currentTimeMillis();}}
3. 步骤 3:监听事件(注解式,推荐)
使用@EventListener注解标记监听方法,Spring 自动扫描并注册为监听器,无需实现接口:
// 通知服务监听器:订单创建后发送短信@Servicepublic class NotificationListener {@Autowiredprivate SmsService smsService;// 监听OrderCreatedEvent事件@EventListenerpublic void handleOrderCreatedEvent(OrderCreatedEvent event) {// 获取事件上下文Long userId = event.getUserId();Long orderId = event.getOrderId();// 发送短信通知smsService.sendSms(userId, "您的订单" + orderId + "已创建成功,请注意查收!");}// (可选)监听PayloadApplicationEvent(无需自定义事件类)// @EventListener// public void handlePayloadEvent(PayloadApplicationEvent\<OrderDTO> event) {// OrderDTO orderDTO = event.getPayload();// smsService.sendSms(orderDTO.getUserId(), "订单创建通知...");// }}// 积分服务监听器:订单创建后增加积分@Servicepublic class PointListener {@Autowiredprivate PointService pointService;// 监听OrderCreatedEvent事件@EventListenerpublic void addPoint(OrderCreatedEvent event) {Long userId = event.getUserId();BigDecimal amount = event.getAmount();// 按订单金额1%增加积分int point = amount.multiply(new BigDecimal("0.01")).intValue();pointService.addPoint(userId, point);}}
三、进阶特性(实际开发必备)
1. 同步 vs 异步事件(核心进阶)
-
默认同步:发布者发布事件后,会阻塞直到所有监听器执行完毕(适用于需要监听器执行结果的场景);
-
异步事件:发布者无需等待监听器执行,直接返回,提高性能(适用于通知、日志等非核心业务)。
异步事件配置步骤:
-
启动类添加
@EnableAsync开启异步支持; -
监听器方法添加
@Async注解; -
(可选)自定义线程池(避免使用默认简单线程池)。
// 1. 启动类开启异步@SpringBootApplication@EnableAsync // 关键:开启Spring异步支持public class Application {public static void main(String\[] args) {SpringApplication.run(Application.class, args);}// 2. 自定义线程池(推荐,避免默认线程池的性能问题)@Beanpublic Executor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(5); // 核心线程数executor.setMaxPoolSize(10); // 最大线程数executor.setQueueCapacity(20); // 队列容量executor.setThreadNamePrefix("SpringEvent-"); // 线程名前缀executor.initialize();return executor;}}// 3. 监听器方法添加@Async,变为异步执行@Servicepublic class NotificationListener {@Async // 关键:标记为异步监听器@EventListenerpublic void handleOrderCreatedEvent(OrderCreatedEvent event) {// 异步执行:发布者无需等待,直接返回System.out.println("异步发送短信,当前线程:" + Thread.currentThread().getName());smsService.sendSms(event.getUserId(), "订单创建通知...");}}
2. 事件监听顺序
当多个监听器监听同一个事件时,可通过@Order注解指定执行顺序(值越小,优先级越高):
@Servicepublic class LogListener {// 顺序1:最先执行(记录订单创建开始日志)@Order(1)@EventListenerpublic void logStart(OrderCreatedEvent event) {System.out.println("订单" + event.getOrderId() + "创建事件开始处理");}}@Servicepublic class NotificationListener {// 顺序2:中间执行(发送短信)@Order(2)@Async@EventListenerpublic void handleOrderCreatedEvent(OrderCreatedEvent event) {smsService.sendSms(event.getUserId(), "订单创建通知...");}}@Servicepublic class PointListener {// 顺序3:最后执行(增加积分)@Order(3)@EventListenerpublic void addPoint(OrderCreatedEvent event) {pointService.addPoint(event.getUserId(), ...);}}
3. 事务绑定事件(@TransactionalEventListener)
场景:订单创建的事务提交后,才执行监听器(避免事务回滚导致的通知 / 积分错误)。
核心:@TransactionalEventListener可指定事件触发时机(事务提交后、回滚后、完成后等)。
@Servicepublic class NotificationListener {// 事务提交后才执行该监听器@TransactionalEventListener(phase = TransactionPhase.AFTER\_COMMIT)public void handleOrderCreatedEvent(OrderCreatedEvent event) {// 只有当createOrder方法的事务提交成功后,才发送短信smsService.sendSms(event.getUserId(), "订单创建通知...");}}
TransactionPhase枚举值说明:
-
AFTER_COMMIT:事务提交后(默认,最常用); -
AFTER_ROLLBACK:事务回滚后; -
AFTER_COMPLETION:事务完成后(无论提交还是回滚); -
BEFORE_COMMIT:事务提交前。
4. 条件监听(仅满足条件时执行)
通过@ConditionalOnExpression或方法参数条件,实现 “特定场景才触发监听器”:
@Servicepublic class PointListener {// 条件:订单金额>=100元才增加积分@EventListener@ConditionalOnExpression("#event.amount.compareTo(new BigDecimal('100')) >= 0")public void addPoint(OrderCreatedEvent event) {Long userId = event.getUserId();int point = event.getAmount().multiply(new BigDecimal("0.01")).intValue();pointService.addPoint(userId, point);}}
5. 自定义事件广播器
默认广播器是SimpleApplicationEventMulticaster,可自定义广播器实现特殊逻辑(如事件过滤、自定义线程池):
@Configurationpublic class EventConfig {// 自定义事件广播器@Beanpublic ApplicationEventMulticaster applicationEventMulticaster() {SimpleApplicationEventMulticaster multicaster = new SimpleApplicationEventMulticaster();// 绑定自定义线程池(异步事件使用)multicaster.setTaskExecutor(taskExecutor());// (可选)设置事件异常处理器(默认监听器抛出异常会中断后续执行)multicaster.setErrorHandler(e -> {System.err.println("监听器执行失败:" + e.getMessage());});return multicaster;}// 自定义线程池@Beanpublic Executor taskExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(5);executor.setMaxPoolSize(10);executor.setQueueCapacity(20);executor.initialize();return executor;}}
四、实战案例:电商订单事件流
以电商订单创建为例,展示 Spring 事件如何解耦多个模块:
业务场景:
订单创建后需执行 3 个操作:1. 发送短信通知;2. 增加用户积分;3. 记录操作日志。通过事件机制解耦,每个操作作为独立监听器。
完整代码:
-
订单事件类(OrderCreatedEvent):同 “基本使用步骤 1”;
-
订单服务(OrderService):同 “基本使用步骤 2”;
-
3 个监听器(通知、积分、日志):
// 1. 短信通知监听器(异步+事务提交后执行)@Servicepublic class SmsListener {@Autowiredprivate SmsService smsService;@Async@TransactionalEventListener(phase = TransactionPhase.AFTER\_COMMIT)public void sendOrderSms(OrderCreatedEvent event) {smsService.sendSms(event.getUserId(), "订单" + event.getOrderId() + "创建成功!");}}// 2. 积分监听器(条件触发:金额>=100元)@Servicepublic class PointListener {@Autowiredprivate PointService pointService;@TransactionalEventListener(phase = TransactionPhase.AFTER\_COMMIT)@ConditionalOnExpression("#event.amount.compareTo(new BigDecimal('100')) >= 0")public void addOrderPoint(OrderCreatedEvent event) {int point = event.getAmount().multiply(new BigDecimal("0.01")).intValue();pointService.addPoint(event.getUserId(), point);}}// 3. 日志监听器(同步执行,优先记录)@Servicepublic class LogListener {@Autowiredprivate OperationLogService logService;@Order(1)@TransactionalEventListener(phase = TransactionPhase.AFTER\_COMMIT)public void recordOrderLog(OrderCreatedEvent event) {OperationLog log = new OperationLog();log.setUserId(event.getUserId());log.setBusinessType("订单创建");log.setBusinessId(event.getOrderId().toString());log.setCreateTime(new Date());logService.save(log);}}
核心优势:
-
解耦:订单服务无需依赖短信、积分、日志服务,后续新增 “订单推送” 功能,只需新增监听器;
-
可维护:每个业务逻辑独立,修改短信发送逻辑不影响订单核心业务;
-
可扩展:异步事件提高订单创建接口的响应速度,避免因短信发送超时阻塞订单流程。
五、常见问题与注意事项
-
异步事件的事务问题:异步监听器无法共享发布者的事务,若需事务一致性,需使用
@TransactionalEventListener绑定事务阶段; -
监听器线程安全:默认监听器是单例,若监听器有成员变量,需保证线程安全(如使用
ThreadLocal或无状态设计); -
事件泛滥:避免定义过多细粒度事件,可按业务域合并(如 “订单事件” 包含创建、支付、取消等子类型);
-
异常处理:同步事件中,某个监听器抛出异常会中断后续监听器执行,可通过自定义
ErrorHandler处理; -
Spring 版本差异:
@EventListener是 Spring 4.2 + 新增,若使用旧版本需实现ApplicationListener接口。
