Spring Event 学习笔记
从入门到进阶的系统化指南
📚 目录
- 背景与动机
- 核心概念与定义
- 基础使用:同步事件
- 进阶使用:异步事件
- 高级特性:事务事件监听
- [Spring Event vs 消息队列](#Spring Event vs 消息队列)
- 最佳实践
- 常见问题与解决方案
- 源码原理解析
- 实战案例
1. 背景与动机
1.1 为什么需要 Spring Event?
在传统的应用开发中,我们经常遇到以下场景:
场景 1:用户注册
java
// ❌ 紧耦合的代码
public void registerUser(User user) {
userRepository.save(user); // 保存用户
emailService.sendWelcomeEmail(user); // 发送邮件
pointsService.grantNewUserPoints(user); // 赠送积分
analyticsService.trackRegistration(user); // 数据统计
}
问题:
- ✗ 高耦合:主业务与辅助业务混在一起
- ✗ 难维护:新增业务需要修改核心代码
- ✗ 不灵活:无法动态添加/移除监听逻辑
- ✗ 性能问题:所有操作同步执行,影响响应时间
场景 2:订单支付成功后的后续处理
java
// ❌ 同样的问题
public void payOrder(Order order) {
order.setStatus(PAID);
orderRepository.save(order);
// 以下都是"支付成功后"的业务
inventoryService.reduceStock(order);
logisticsService.createShipment(order);
invoiceService.generateInvoice(order);
notificationService.notifyUser(order);
}
1.2 Spring Event 解决了什么问题?
Spring Event 提供了一种 发布-订阅(Pub-Sub)模式,实现:
✅ 解耦 :核心业务与辅助业务分离
✅ 扩展性 :新增监听器无需修改发布者代码
✅ 灵活性 :支持同步/异步、事务集成
✅ 可维护性:职责单一,代码清晰
使用 Spring Event 后:
java
// ✅ 解耦后的代码
public void registerUser(User user) {
userRepository.save(user);
// 发布事件,其他业务自行订阅
applicationEventPublisher.publishEvent(new UserRegisteredEvent(user));
}
1.3 Spring Event 在哪些场景下使用?
| 场景 | 说明 | 示例 |
|---|---|---|
| 业务解耦 | 主业务与辅助业务分离 | 用户注册后发邮件、赠积分 |
| 异步处理 | 耗时操作不阻塞主流程 | 订单支付后生成发票 |
| 事务同步 | 确保事件在事务提交后执行 | 支付成功后扣库存 |
| 领域事件 | DDD 中的领域事件 | 订单状态变更事件 |
| 系统通知 | 内部模块间通信 | 缓存刷新、日志记录 |
2. 核心概念与定义
2.1 Spring Event 的核心组件
Spring Event 机制由以下几个核心组件构成:
┌─────────────┐ 发布 ┌──────────────────┐
│ 发布者 │ ───────────────> │ ApplicationEvent │
│ Publisher │ │ (事件对象) │
└─────────────┘ └──────────────────┘
│
│ 传播
↓
┌─────────────────────┐
│ ApplicationContext │
│ (事件多播器) │
└─────────────────────┘
│
┌───────────────┼───────────────┐
↓ ↓ ↓
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 监听器 A │ │ 监听器 B │ │ 监听器 C │
│ Listener │ │ Listener │ │ Listener │
└─────────────┘ └─────────────┘ └─────────────┘
2.1.1 事件(Event)
事件是传递信息的载体,继承自 ApplicationEvent(或者在 Spring 4.2+ 可以是任意 POJO)。
java
// 方式 1:继承 ApplicationEvent(传统方式)
public class UserRegisteredEvent extends ApplicationEvent {
private User user;
public UserRegisteredEvent(Object source, User user) {
super(source);
this.user = user;
}
public User getUser() {
return user;
}
}
// 方式 2:POJO(Spring 4.2+)
public class OrderPaidEvent {
private Order order;
private LocalDateTime paidTime;
// 构造器、getter、setter
}
2.1.2 事件发布者(Publisher)
通过 ApplicationEventPublisher 发布事件。
java
@Service
public class UserService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void registerUser(User user) {
// 核心业务逻辑
userRepository.save(user);
// 发布事件
eventPublisher.publishEvent(new UserRegisteredEvent(this, user));
}
}
2.1.3 事件监听器(Listener)
监听并处理事件,有两种方式:
方式 1:实现 ApplicationListener 接口
java
@Component
public class EmailListener implements ApplicationListener<UserRegisteredEvent> {
@Override
public void onApplicationEvent(UserRegisteredEvent event) {
User user = event.getUser();
// 发送欢迎邮件
emailService.sendWelcomeEmail(user);
}
}
方式 2:使用 @EventListener 注解(推荐)
java
@Component
public class UserEventListener {
@EventListener
public void handleUserRegistered(UserRegisteredEvent event) {
User user = event.getUser();
// 发送欢迎邮件
emailService.sendWelcomeEmail(user);
}
}
2.2 核心接口与类
| 接口/类 | 作用 | 说明 |
|---|---|---|
ApplicationEvent |
事件基类 | 所有事件的父类(可选继承) |
ApplicationListener<E> |
监听器接口 | 泛型指定监听的事件类型 |
ApplicationEventPublisher |
事件发布器 | 提供 publishEvent() 方法 |
ApplicationEventMulticaster |
事件多播器 | 负责将事件分发给监听器 |
@EventListener |
注解式监听器 | Spring 4.2+ 推荐方式 |
@Async |
异步执行 | 配合 @EnableAsync 使用 |
@TransactionalEventListener |
事务事件监听 | 在事务特定阶段执行 |
3. 基础使用:同步事件
3.1 快速开始
Step 1: 定义事件
java
package com.example.event;
public class OrderCreatedEvent {
private final Long orderId;
private final String orderNo;
private final LocalDateTime createdTime;
public OrderCreatedEvent(Long orderId, String orderNo) {
this.orderId = orderId;
this.orderNo = orderNo;
this.createdTime = LocalDateTime.now();
}
// Getters
public Long getOrderId() { return orderId; }
public String getOrderNo() { return orderNo; }
public LocalDateTime getCreatedTime() { return createdTime; }
}
Step 2: 发布事件
java
package com.example.service;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
private final ApplicationEventPublisher eventPublisher;
public OrderService(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
public Order createOrder(OrderRequest request) {
// 1. 核心业务逻辑
Order order = new Order();
order.setOrderNo(generateOrderNo());
order.setAmount(request.getAmount());
orderRepository.save(order);
// 2. 发布事件
eventPublisher.publishEvent(
new OrderCreatedEvent(order.getId(), order.getOrderNo())
);
return order;
}
}
Step 3: 监听事件
java
package com.example.listener;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class OrderEventListener {
// 监听订单创建事件
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
System.out.println("订单创建成功: " + event.getOrderNo());
// 执行后续逻辑:发送短信、记录日志等
}
}
3.2 同步事件的执行流程
┌──────────────┐
│ 发布事件 │
│ publishEvent │
└──────┬───────┘
│
↓
┌──────────────────────┐
│ ApplicationContext │
│ 查找所有监听器 │
└──────┬───────────────┘
│
↓
┌──────────────────────┐
│ 按注册顺序依次调用 │ ← 同步执行,阻塞主线程
│ Listener 1 │
│ Listener 2 │
│ Listener 3 │
└──────┬───────────────┘
│
↓
┌──────────────┐
│ 返回调用方 │
└──────────────┘
关键特性:
- ⚠️ 同步执行:监听器在发布者的同一线程中执行
- ⚠️ 顺序执行:监听器按注册顺序依次执行
- ⚠️ 阻塞主流程:如果监听器耗时长,会影响主业务响应时间
3.3 多个监听器的执行顺序
java
@Component
public class OrderEventListeners {
@EventListener
@Order(1) // 优先级最高
public void listener1(OrderCreatedEvent event) {
System.out.println("Listener 1 执行");
}
@EventListener
@Order(2)
public void listener2(OrderCreatedEvent event) {
System.out.println("Listener 2 执行");
}
@EventListener
@Order(3)
public void listener3(OrderCreatedEvent event) {
System.out.println("Listener 3 执行");
}
}
输出:
Listener 1 执行
Listener 2 执行
Listener 3 执行
3.4 条件监听(SpEL 表达式)
只有满足条件时才执行监听器:
java
@Component
public class ConditionalListener {
// 只监听金额大于 1000 的订单
@EventListener(condition = "#event.amount > 1000")
public void handleLargeOrder(OrderCreatedEvent event) {
System.out.println("大额订单: " + event.getOrderNo());
}
// 只监听 VIP 用户的订单
@EventListener(condition = "#event.userType == 'VIP'")
public void handleVipOrder(OrderCreatedEvent event) {
System.out.println("VIP 订单: " + event.getOrderNo());
}
}
4. 进阶使用:异步事件
4.1 为什么需要异步事件?
同步事件的问题:
- 监听器耗时操作会阻塞主线程
- 影响主业务的响应时间
- 无法充分利用多核 CPU
场景示例:
java
// 同步执行,发送邮件需要 2 秒
@EventListener
public void sendEmail(UserRegisteredEvent event) {
Thread.sleep(2000); // 模拟耗时操作
emailService.send(event.getUser().getEmail());
}
// 主流程需要等待 2 秒才能返回
public void registerUser(User user) {
userRepository.save(user);
eventPublisher.publishEvent(new UserRegisteredEvent(user)); // 阻塞 2 秒
return "注册成功"; // 2 秒后才返回
}
4.2 启用异步支持
Step 1: 开启异步配置
java
package com.example.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
@Configuration
@EnableAsync // 开启异步支持
public class AsyncConfig {
}
Step 2: 使用 @Async 注解
java
@Component
public class AsyncEventListener {
@Async // 异步执行
@EventListener
public void handleUserRegistered(UserRegisteredEvent event) {
// 这里的代码在独立线程中执行,不阻塞主流程
Thread.sleep(2000); // 模拟耗时操作
emailService.sendWelcomeEmail(event.getUser());
}
}
4.3 自定义线程池
默认情况下,@Async 使用 SimpleAsyncTaskExecutor(每次创建新线程,性能差)。
推荐配置自定义线程池:
java
package com.example.config;
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(name = "eventTaskExecutor")
public Executor eventTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数
executor.setCorePoolSize(5);
// 最大线程数
executor.setMaxPoolSize(10);
// 队列容量
executor.setQueueCapacity(100);
// 线程名称前缀
executor.setThreadNamePrefix("event-");
// 拒绝策略:调用者运行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务完成后再关闭
executor.setWaitForTasksToCompleteOnShutdown(true);
// 等待时间
executor.setAwaitTerminationSeconds(60);
executor.initialize();
return executor;
}
}
使用自定义线程池:
java
@Component
public class AsyncEventListener {
@Async("eventTaskExecutor") // 指定线程池
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
// 在 eventTaskExecutor 线程池中执行
System.out.println("当前线程: " + Thread.currentThread().getName());
}
}
4.4 异步事件的注意事项
问题 1: 异步事件无法传播事务
java
@Service
public class OrderService {
@Transactional
public void createOrder(Order order) {
orderRepository.save(order);
// 发布异步事件
eventPublisher.publishEvent(new OrderCreatedEvent(order));
// 如果这里抛异常,订单会回滚
// 但异步监听器可能已经执行(或正在执行),无法回滚
throw new RuntimeException("模拟异常");
}
}
@Component
public class AsyncListener {
@Async
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
// ⚠️ 这里没有事务上下文
// 如果主事务回滚,这里的操作无法回滚
inventoryService.reduceStock(event.getOrderId());
}
}
解决方案 :使用 @TransactionalEventListener(见下一节)
问题 2: 异步事件的异常处理
异步监听器中的异常不会传播到发布者:
java
@Async
@EventListener
public void handleEvent(OrderCreatedEvent event) {
throw new RuntimeException("异步监听器异常");
// ⚠️ 这个异常不会传播到发布者,需要手动处理
}
解决方案:自定义异常处理器
java
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new CustomAsyncExceptionHandler();
}
}
public class CustomAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
System.err.println("异步方法异常: " + method.getName());
System.err.println("异常信息: " + ex.getMessage());
// 记录日志、发送告警等
}
}
5. 高级特性:事务事件监听
5.1 为什么需要事务事件监听?
问题场景:
java
@Service
public class OrderService {
@Transactional
public void createOrder(Order order) {
orderRepository.save(order);
// 发布事件
eventPublisher.publishEvent(new OrderCreatedEvent(order));
// 如果这里抛异常,订单会回滚
// 但事件可能已经被监听器处理了(比如发送了短信)
if (someCondition) {
throw new RuntimeException("订单创建失败");
}
}
}
@Component
public class OrderListener {
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
// ⚠️ 这里可能在事务提交前就执行了
// 如果订单最终回滚,这条短信就是"脏数据"
smsService.sendOrderConfirmation(event.getOrderId());
}
}
5.2 @TransactionalEventListener 详解
@TransactionalEventListener 可以指定监听器在事务的哪个阶段执行:
| 阶段 | 说明 | 使用场景 |
|---|---|---|
AFTER_COMMIT(默认) |
事务提交后 | 发送通知、清理缓存 |
AFTER_ROLLBACK |
事务回滚后 | 记录失败日志 |
AFTER_COMPLETION |
事务完成后(提交或回滚) | 清理资源 |
BEFORE_COMMIT |
事务提交前 | 校验数据一致性 |
示例 1: 事务提交后执行
java
@Component
public class TransactionalListener {
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleOrderCreated(OrderCreatedEvent event) {
// ✅ 只有事务成功提交后才执行
// 此时订单已经持久化到数据库
smsService.sendOrderConfirmation(event.getOrderId());
}
}
示例 2: 事务回滚后执行
java
@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void handleOrderFailed(OrderCreatedEvent event) {
// 只有事务回滚时才执行
System.out.println("订单创建失败,记录日志: " + event.getOrderNo());
}
示例 3: 事务完成后执行(无论成功或失败)
java
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMPLETION)
public void cleanupResources(OrderCreatedEvent event) {
// 无论事务成功或失败都执行
// 适合清理临时资源、释放锁等
lockService.releaseLock(event.getOrderNo());
}
5.3 fallbackExecution 参数
如果发布事件时没有事务,监听器默认不会执行。可以通过 fallbackExecution 控制:
java
@TransactionalEventListener(
phase = TransactionPhase.AFTER_COMMIT,
fallbackExecution = true // 没有事务时也执行
)
public void handleEvent(OrderCreatedEvent event) {
// 如果发布者有事务,等事务提交后执行
// 如果发布者没有事务,立即执行
}
5.4 完整示例:订单支付场景
java
// 1. 事件定义
public class OrderPaidEvent {
private Long orderId;
private BigDecimal amount;
// 构造器、getter、setter
}
// 2. 发布者(有事务)
@Service
public class PaymentService {
@Transactional
public void payOrder(Long orderId, BigDecimal amount) {
// 更新订单状态
Order order = orderRepository.findById(orderId);
order.setStatus(OrderStatus.PAID);
orderRepository.save(order);
// 发布事件
eventPublisher.publishEvent(new OrderPaidEvent(orderId, amount));
// 如果这里抛异常,事务回滚,监听器不会执行
}
}
// 3. 监听器(事务提交后执行)
@Component
public class PaymentEventListener {
// 扣减库存(事务提交后)
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void reduceStock(OrderPaidEvent event) {
inventoryService.reduce(event.getOrderId());
}
// 发送通知(事务提交后)
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void sendNotification(OrderPaidEvent event) {
notificationService.notifyUser(event.getOrderId());
}
// 记录失败日志(事务回滚后)
@TransactionalEventListener(phase = TransactionPhase.AFTER_ROLLBACK)
public void logFailure(OrderPaidEvent event) {
logger.error("支付失败: orderId={}", event.getOrderId());
}
}
6. Spring Event vs 消息队列
6.1 核心区别
| 特性 | Spring Event | 消息队列(RabbitMQ/Kafka) |
|---|---|---|
| 作用范围 | 单应用内(JVM 内) | 跨应用、分布式 |
| 可靠性 | 无持久化,应用重启后丢失 | 持久化,高可用 |
| 性能 | 内存操作,极快(纳秒级) | 网络通信,相对慢(毫秒级) |
| 顺序保证 | 同步事件有序 | 需要配置 partition/queue |
| 事务支持 | @TransactionalEventListener |
需要手动实现分布式事务 |
| 异步能力 | 支持(@Async) | 天然异步 |
| 解耦程度 | 应用内解耦 | 应用间解耦 |
| 运维成本 | 无额外组件 | 需要部署和维护 MQ |
| 扩展性 | 垂直扩展 | 水平扩展 |
| 削峰填谷 | 不支持 | 支持 |
6.2 选型建议
使用 Spring Event 的场景:
✅ 单体应用内部通信
✅ 轻量级业务解耦 (如日志记录、缓存刷新)
✅ 事务一致性要求高 (利用 @TransactionalEventListener)
✅ 低延迟要求 (纳秒级响应)
✅ 不需要持久化(应用重启后可以丢失)
使用消息队列的场景:
✅ 微服务间通信
✅ 高可靠性要求 (消息不能丢失)
✅ 削峰填谷 (处理流量突发)
✅ 异步解耦 (生产者和消费者独立部署)
✅ 分布式事务(最终一致性)
6.3 混合使用方案
在实际项目中,可以结合两者优势:
java
@Service
public class OrderService {
@Transactional
public void createOrder(Order order) {
// 1. 核心业务逻辑
orderRepository.save(order);
// 2. 发布 Spring Event(应用内通知)
eventPublisher.publishEvent(new OrderCreatedEvent(order));
}
}
@Component
public class OrderEventListener {
// 应用内处理:更新缓存
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void updateCache(OrderCreatedEvent event) {
cacheService.updateOrderCache(event.getOrderId());
}
// 发送到消息队列:跨服务通知
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void publishToMQ(OrderCreatedEvent event) {
// 将事件发送到 RabbitMQ,通知其他微服务
rabbitTemplate.convertAndSend("order.exchange", "order.created", event);
}
}
架构示意:
┌──────────────┐
│ Order Service│
└──────┬───────┘
│ publishEvent
↓
┌──────────────────┐
│ Spring Event │
│ (应用内) │
└──────┬───────────┘
│
├─────────> 更新缓存(本地)
│
└─────────> 发送到 RabbitMQ
│
↓
┌───────────────┐
│ RabbitMQ │
│ (跨服务) │
└───────┬───────┘
│
┌───────┴────────┐
↓ ↓
┌──────────────┐ ┌──────────────┐
│ Inventory │ │ Notification │
│ Service │ │ Service │
└──────────────┘ └──────────────┘
7. 最佳实践
7.1 事件设计原则
1. 事件命名规范
java
// ✅ 推荐:使用过去式 + Event 后缀
public class OrderCreatedEvent { }
public class UserRegisteredEvent { }
public class PaymentCompletedEvent { }
// ❌ 不推荐
public class OrderCreate { }
public class CreateOrder { }
2. 事件应该是不可变对象
java
// ✅ 推荐:使用 final 字段 + 构造器
public class OrderCreatedEvent {
private final Long orderId;
private final String orderNo;
private final LocalDateTime createdTime;
public OrderCreatedEvent(Long orderId, String orderNo) {
this.orderId = orderId;
this.orderNo = orderNo;
this.createdTime = LocalDateTime.now();
}
// 只提供 getter,没有 setter
public Long getOrderId() { return orderId; }
public String getOrderNo() { return orderNo; }
public LocalDateTime getCreatedTime() { return createdTime; }
}
3. 事件应该只包含必要信息
java
// ✅ 推荐:只包含 ID 和关键字段
public class OrderPaidEvent {
private final Long orderId;
private final BigDecimal amount;
}
// ❌ 不推荐:包含完整实体对象
public class OrderPaidEvent {
private final Order order; // 可能包含大量无关数据
}
4. 为事件添加元数据
java
public class BaseEvent {
private final String eventId; // 事件唯一 ID
private final LocalDateTime timestamp; // 事件发生时间
private final String source; // 事件来源
public BaseEvent() {
this.eventId = UUID.randomUUID().toString();
this.timestamp = LocalDateTime.now();
this.source = "OrderService";
}
}
public class OrderCreatedEvent extends BaseEvent {
private final Long orderId;
// ...
}
7.2 监听器设计原则
1. 监听器职责单一
java
// ✅ 推荐:每个监听器只做一件事
@Component
public class OrderEventListeners {
@EventListener
public void sendEmail(OrderCreatedEvent event) {
emailService.sendOrderConfirmation(event.getOrderId());
}
@EventListener
public void updateCache(OrderCreatedEvent event) {
cacheService.updateOrderCache(event.getOrderId());
}
}
// ❌ 不推荐:一个监听器做多件事
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
emailService.sendEmail(...);
cacheService.updateCache(...);
logService.recordLog(...);
}
2. 监听器应该幂等
java
@Component
public class OrderEventListener {
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
// ✅ 检查是否已经处理过
if (orderProcessedRepository.exists(event.getOrderId())) {
return; // 幂等性保证
}
// 执行业务逻辑
inventoryService.reduce(event.getOrderId());
// 记录已处理
orderProcessedRepository.save(event.getOrderId());
}
}
3. 监听器异常处理
java
@Component
public class OrderEventListener {
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
try {
// 业务逻辑
smsService.sendNotification(event.getOrderId());
} catch (Exception e) {
// ✅ 记录日志,但不抛出异常(避免影响其他监听器)
logger.error("发送短信失败: orderId={}", event.getOrderId(), e);
// 可以发送告警、记录到数据库等
alertService.sendAlert("订单通知失败", e.getMessage());
}
}
}
7.3 性能优化
1. 异步处理耗时操作
java
@Component
public class OrderEventListener {
// ❌ 同步执行,阻塞主线程
@EventListener
public void handleOrderCreated(OrderCreatedEvent event) {
Thread.sleep(2000); // 耗时操作
emailService.sendEmail(...);
}
// ✅ 异步执行,不阻塞主线程
@Async
@EventListener
public void handleOrderCreatedAsync(OrderCreatedEvent event) {
emailService.sendEmail(...);
}
}
2. 使用条件监听减少不必要的执行
java
@Component
public class OrderEventListener {
// 只监听大额订单
@EventListener(condition = "#event.amount > 10000")
public void handleLargeOrder(OrderCreatedEvent event) {
vipService.notifyManager(event.getOrderId());
}
}
3. 批量处理事件
java
@Component
public class BatchEventListener {
private final List<OrderCreatedEvent> eventBuffer = new CopyOnWriteArrayList<>();
@EventListener
public void bufferEvent(OrderCreatedEvent event) {
eventBuffer.add(event);
// 达到批次大小,批量处理
if (eventBuffer.size() >= 100) {
processBatch();
}
}
@Scheduled(fixedDelay = 5000) // 定时处理
public void processBatch() {
if (eventBuffer.isEmpty()) return;
List<OrderCreatedEvent> batch = new ArrayList<>(eventBuffer);
eventBuffer.clear();
// 批量处理
batchService.processBatch(batch);
}
}
7.4 测试最佳实践
1. 单元测试监听器
java
@SpringBootTest
class OrderEventListenerTest {
@Autowired
private ApplicationEventPublisher eventPublisher;
@MockBean
private EmailService emailService;
@Test
void shouldSendEmailWhenOrderCreated() {
// 发布事件
OrderCreatedEvent event = new OrderCreatedEvent(1L, "ORDER-001");
eventPublisher.publishEvent(event);
// 验证监听器是否执行
verify(emailService, times(1)).sendOrderConfirmation(1L);
}
}
2. 测试异步监听器
java
@Test
void shouldHandleEventAsynchronously() throws InterruptedException {
// 发布事件
eventPublisher.publishEvent(new OrderCreatedEvent(1L, "ORDER-001"));
// 等待异步执行完成
Thread.sleep(1000);
// 验证结果
verify(emailService, times(1)).sendEmail(...);
}
8. 常见问题与解决方案
8.1 事件没有被监听到
症状:
- 发布了事件,但监听器没有执行
- 日志中看不到监听器的输出
可能原因与解决方案:
原因 1: 监听器没有注册为 Spring Bean
java
// ❌ 错误:没有 @Component 注解
public class OrderEventListener {
@EventListener
public void handleEvent(OrderCreatedEvent event) { }
}
// ✅ 正确:添加 @Component
@Component
public class OrderEventListener {
@EventListener
public void handleEvent(OrderCreatedEvent event) { }
}
原因 2: 事件类型不匹配
java
// 发布的事件
eventPublisher.publishEvent(new OrderCreatedEvent(...));
// ❌ 监听器监听的是其他类型
@EventListener
public void handleEvent(UserRegisteredEvent event) { }
// ✅ 正确:类型匹配
@EventListener
public void handleEvent(OrderCreatedEvent event) { }
原因 3: 条件表达式错误
java
// ❌ 条件表达式错误,永远不会执行
@EventListener(condition = "#event.amount < 0")
public void handleEvent(OrderCreatedEvent event) { }
// ✅ 检查条件是否正确
@EventListener(condition = "#event.amount > 0")
public void handleEvent(OrderCreatedEvent event) { }
8.2 异步事件不执行
症状:
- 添加了
@Async但监听器仍然同步执行 - 或者异步监听器根本不执行
解决方案:
1. 确保开启了异步支持
java
@Configuration
@EnableAsync // ✅ 必须添加此注解
public class AsyncConfig {
}
2. 不要在同一个类中调用
java
// ❌ 错误:同一个类中的方法调用,@Async 不生效
@Component
public class OrderService {
public void createOrder() {
// ...
handleAsync(); // @Async 不会生效
}
@Async
public void handleAsync() {
// 这个方法不会异步执行
}
}
// ✅ 正确:拆分到不同的类
@Component
public class OrderService {
@Autowired
private AsyncService asyncService;
public void createOrder() {
asyncService.handleAsync(); // 会异步执行
}
}
@Component
public class AsyncService {
@Async
public void handleAsync() {
// 这个方法会异步执行
}
}
8.3 事务事件不执行
症状:
- 使用了
@TransactionalEventListener但监听器不执行
解决方案:
1. 确保发布者有事务
java
// ❌ 发布者没有事务
public void createOrder() {
orderRepository.save(order);
eventPublisher.publishEvent(event); // 监听器不会执行
}
// ✅ 发布者有事务
@Transactional
public void createOrder() {
orderRepository.save(order);
eventPublisher.publishEvent(event); // 监听器会在事务提交后执行
}
2. 或者设置 fallbackExecution = true
java
@TransactionalEventListener(
phase = TransactionPhase.AFTER_COMMIT,
fallbackExecution = true // 没有事务时也执行
)
public void handleEvent(OrderCreatedEvent event) {
// ...
}
8.4 循环依赖问题
症状:
The dependencies of some of the beans in the application context form a cycle
场景:
java
@Component
public class UserService {
@Autowired
private OrderService orderService;
public void registerUser() {
eventPublisher.publishEvent(new UserRegisteredEvent(...));
}
}
@Component
public class OrderService {
@Autowired
private UserService userService; // 循环依赖
@EventListener
public void handleUserRegistered(UserRegisteredEvent event) {
// ...
}
}
解决方案:
1. 使用 @Lazy 注解
java
@Component
public class UserService {
@Autowired
@Lazy // 延迟注入
private OrderService orderService;
}
2. 重构代码,消除循环依赖
java
// 将监听器拆分到独立的类
@Component
public class UserEventListener {
@Autowired
private OrderService orderService;
@EventListener
public void handleUserRegistered(UserRegisteredEvent event) {
orderService.createWelcomeOrder(event.getUserId());
}
}
8.5 监听器执行顺序问题
症状:
- 需要保证监听器按特定顺序执行
- 监听器 A 必须在监听器 B 之前执行
解决方案:
java
@Component
public class OrderEventListeners {
@EventListener
@Order(1) // 优先级高,先执行
public void updateInventory(OrderCreatedEvent event) {
inventoryService.reduce(event.getOrderId());
}
@EventListener
@Order(2) // 优先级低,后执行
public void sendNotification(OrderCreatedEvent event) {
notificationService.notify(event.getOrderId());
}
}
9. 源码原理解析
9.1 Spring Event 核心类图
┌─────────────────────────────────┐
│ ApplicationEventPublisher │
│ (事件发布接口) │
└────────────┬────────────────────┘
│
↓
┌─────────────────────────────────┐
│ ApplicationContext │
│ (实现了事件发布接口) │
└────────────┬────────────────────┘
│
↓
┌─────────────────────────────────┐
│ ApplicationEventMulticaster │
│ (事件多播器,负责分发事件) │
└────────────┬────────────────────┘
│
├──────────────────────┐
↓ ↓
┌─────────────────────┐ ┌─────────────────────┐
│ ApplicationListener │ │ @EventListener │
│ (监听器接口) │ │ (注解式监听器) │
└─────────────────────┘ └─────────────────────┘
9.2 事件发布流程(源码级别)
Step 1: 发布事件
java
// AbstractApplicationContext.java
public void publishEvent(Object event) {
publishEvent(event, null);
}
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
// 1. 包装事件对象
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
} else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
// 2. 委托给事件多播器
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
Step 2: 多播事件
java
// SimpleApplicationEventMulticaster.java
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
// 遍历所有监听器
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
// 异步执行
executor.execute(() -> invokeListener(listener, event));
} else {
// 同步执行
invokeListener(listener, event);
}
}
}
Step 3: 调用监听器
java
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
} catch (Throwable err) {
errorHandler.handleError(err);
}
} else {
doInvokeListener(listener, event);
}
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
listener.onApplicationEvent(event);
}
9.3 @EventListener 注解的处理
Spring 通过 EventListenerMethodProcessor 处理 @EventListener 注解:
java
// EventListenerMethodProcessor.java
public void afterSingletonsInstantiated() {
// 扫描所有 Bean
String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
for (String beanName : beanNames) {
Class<?> type = beanFactory.getType(beanName);
// 查找带有 @EventListener 的方法
Map<Method, EventListener> annotatedMethods =
MethodIntrospector.selectMethods(type,
(Method method) -> method.getAnnotation(EventListener.class));
// 为每个方法创建 ApplicationListener
for (Map.Entry<Method, EventListener> entry : annotatedMethods.entrySet()) {
Method method = entry.getKey();
EventListener eventListener = entry.getValue();
ApplicationListener<?> applicationListener =
factory.createApplicationListener(beanName, type, method);
// 注册监听器
context.addApplicationListener(applicationListener);
}
}
}
9.4 @TransactionalEventListener 的实现原理
java
// TransactionSynchronizationEventAdapter.java
class TransactionSynchronizationEventAdapter extends TransactionSynchronizationAdapter {
private final ApplicationListener<ApplicationEvent> listener;
private final ApplicationEvent event;
private final TransactionPhase phase;
@Override
public void afterCommit() {
if (this.phase == TransactionPhase.AFTER_COMMIT) {
processEvent();
}
}
@Override
public void afterCompletion(int status) {
if (this.phase == TransactionPhase.AFTER_ROLLBACK && status == STATUS_ROLLED_BACK) {
processEvent();
} else if (this.phase == TransactionPhase.AFTER_COMPLETION) {
processEvent();
}
}
protected void processEvent() {
this.listener.onApplicationEvent(this.event);
}
}
核心机制:
- 监听器被包装成
TransactionSynchronization - 注册到当前事务的同步回调中
- 在事务提交/回滚时触发监听器
10. 实战案例
10.1 案例 1: 用户注册系统
需求:
用户注册成功后需要:
- 发送欢迎邮件
- 赠送新用户积分
- 发送短信通知
- 记录注册日志
实现:
java
// 1. 定义事件
public class UserRegisteredEvent {
private final Long userId;
private final String username;
private final String email;
private final String phone;
public UserRegisteredEvent(Long userId, String username, String email, String phone) {
this.userId = userId;
this.username = username;
this.email = email;
this.phone = phone;
}
// Getters
}
// 2. 用户服务(发布事件)
@Service
public class UserService {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Transactional
public User registerUser(RegisterRequest request) {
// 核心业务逻辑
User user = new User();
user.setUsername(request.getUsername());
user.setEmail(request.getEmail());
user.setPhone(request.getPhone());
user.setPassword(passwordEncoder.encode(request.getPassword()));
User savedUser = userRepository.save(user);
// 发布事件
eventPublisher.publishEvent(new UserRegisteredEvent(
savedUser.getId(),
savedUser.getUsername(),
savedUser.getEmail(),
savedUser.getPhone()
));
return savedUser;
}
}
// 3. 事件监听器
@Component
@Slf4j
public class UserRegisteredEventListener {
@Autowired
private EmailService emailService;
@Autowired
private PointsService pointsService;
@Autowired
private SmsService smsService;
// 发送欢迎邮件(异步)
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void sendWelcomeEmail(UserRegisteredEvent event) {
try {
emailService.sendWelcomeEmail(event.getEmail(), event.getUsername());
log.info("欢迎邮件发送成功: {}", event.getEmail());
} catch (Exception e) {
log.error("欢迎邮件发送失败: {}", event.getEmail(), e);
}
}
// 赠送新用户积分(同步,确保成功)
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void grantNewUserPoints(UserRegisteredEvent event) {
try {
pointsService.grantPoints(event.getUserId(), 100, "新用户注册");
log.info("新用户积分赠送成功: userId={}", event.getUserId());
} catch (Exception e) {
log.error("新用户积分赠送失败: userId={}", event.getUserId(), e);
}
}
// 发送短信通知(异步)
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void sendSmsNotification(UserRegisteredEvent event) {
try {
smsService.sendRegistrationSuccess(event.getPhone());
log.info("注册短信发送成功: {}", event.getPhone());
} catch (Exception e) {
log.error("注册短信发送失败: {}", event.getPhone(), e);
}
}
// 记录注册日志(同步)
@EventListener
public void logRegistration(UserRegisteredEvent event) {
log.info("用户注册成功: userId={}, username={}",
event.getUserId(), event.getUsername());
}
}
10.2 案例 2: 订单支付流程
需求:
订单支付成功后需要:
- 扣减库存
- 创建物流单
- 生成发票
- 发送支付成功通知
- 更新用户会员积分
实现:
java
// 1. 定义事件
public class OrderPaidEvent {
private final Long orderId;
private final String orderNo;
private final Long userId;
private final BigDecimal amount;
private final List<OrderItem> items;
// 构造器、Getters
}
// 2. 支付服务
@Service
public class PaymentService {
@Autowired
private ApplicationEventPublisher eventPublisher;
@Transactional
public PaymentResult pay(PaymentRequest request) {
// 1. 查询订单
Order order = orderRepository.findById(request.getOrderId())
.orElseThrow(() -> new OrderNotFoundException());
// 2. 调用支付接口
PaymentResult result = paymentGateway.pay(request);
if (result.isSuccess()) {
// 3. 更新订单状态
order.setStatus(OrderStatus.PAID);
order.setPaidTime(LocalDateTime.now());
orderRepository.save(order);
// 4. 发布事件
eventPublisher.publishEvent(new OrderPaidEvent(
order.getId(),
order.getOrderNo(),
order.getUserId(),
order.getAmount(),
order.getItems()
));
}
return result;
}
}
// 3. 事件监听器
@Component
@Slf4j
public class OrderPaidEventListener {
// 扣减库存(同步,事务提交后执行)
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
@Order(1)
public void reduceInventory(OrderPaidEvent event) {
try {
for (OrderItem item : event.getItems()) {
inventoryService.reduce(item.getProductId(), item.getQuantity());
}
log.info("库存扣减成功: orderId={}", event.getOrderId());
} catch (Exception e) {
log.error("库存扣减失败: orderId={}", event.getOrderId(), e);
// 发送告警,人工介入
alertService.sendAlert("库存扣减失败", event.getOrderNo());
}
}
// 创建物流单(异步)
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
@Order(2)
public void createShipment(OrderPaidEvent event) {
try {
logisticsService.createShipment(event.getOrderId());
log.info("物流单创建成功: orderId={}", event.getOrderId());
} catch (Exception e) {
log.error("物流单创建失败: orderId={}", event.getOrderId(), e);
}
}
// 生成发票(异步)
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void generateInvoice(OrderPaidEvent event) {
try {
invoiceService.generate(event.getOrderId());
log.info("发票生成成功: orderId={}", event.getOrderId());
} catch (Exception e) {
log.error("发票生成失败: orderId={}", event.getOrderId(), e);
}
}
// 发送通知(异步)
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void sendNotification(OrderPaidEvent event) {
try {
notificationService.sendPaymentSuccess(
event.getUserId(),
event.getOrderNo(),
event.getAmount()
);
log.info("支付成功通知发送: orderId={}", event.getOrderId());
} catch (Exception e) {
log.error("支付成功通知发送失败: orderId={}", event.getOrderId(), e);
}
}
// 更新会员积分(同步)
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void updateMemberPoints(OrderPaidEvent event) {
try {
int points = event.getAmount().multiply(new BigDecimal("0.01")).intValue();
pointsService.addPoints(event.getUserId(), points, "订单消费");
log.info("会员积分更新成功: userId={}, points={}", event.getUserId(), points);
} catch (Exception e) {
log.error("会员积分更新失败: userId={}", event.getUserId(), e);
}
}
}
10.3 案例 3: 分布式事务(Saga 模式)
使用 Spring Event 实现简单的 Saga 模式(补偿事务):
java
// 1. 定义事件
public class OrderCreatedEvent {
private final Long orderId;
private final Long userId;
private final BigDecimal amount;
}
public class OrderCancelledEvent {
private final Long orderId;
private final String reason;
}
// 2. 订单服务
@Service
public class OrderService {
@Transactional
public Order createOrder(CreateOrderRequest request) {
// 1. 创建订单
Order order = new Order();
order.setUserId(request.getUserId());
order.setAmount(request.getAmount());
orderRepository.save(order);
// 2. 发布订单创建事件
eventPublisher.publishEvent(new OrderCreatedEvent(
order.getId(),
order.getUserId(),
order.getAmount()
));
return order;
}
}
// 3. Saga 协调器
@Component
@Slf4j
public class OrderSagaCoordinator {
private final Map<Long, SagaState> sagaStates = new ConcurrentHashMap<>();
// Step 1: 扣减库存
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
@Order(1)
public void reduceInventory(OrderCreatedEvent event) {
try {
inventoryService.reduce(event.getOrderId());
sagaStates.put(event.getOrderId(), SagaState.INVENTORY_REDUCED);
} catch (Exception e) {
log.error("库存扣减失败,开始补偿", e);
eventPublisher.publishEvent(new OrderCancelledEvent(event.getOrderId(), "库存不足"));
}
}
// Step 2: 扣减账户余额
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
@Order(2)
public void deductBalance(OrderCreatedEvent event) {
if (sagaStates.get(event.getOrderId()) != SagaState.INVENTORY_REDUCED) {
return;
}
try {
accountService.deduct(event.getUserId(), event.getAmount());
sagaStates.put(event.getOrderId(), SagaState.BALANCE_DEDUCTED);
} catch (Exception e) {
log.error("余额扣减失败,开始补偿", e);
eventPublisher.publishEvent(new OrderCancelledEvent(event.getOrderId(), "余额不足"));
}
}
// 补偿:恢复库存
@EventListener
public void compensateInventory(OrderCancelledEvent event) {
SagaState state = sagaStates.get(event.getOrderId());
if (state == SagaState.INVENTORY_REDUCED || state == SagaState.BALANCE_DEDUCTED) {
inventoryService.restore(event.getOrderId());
log.info("库存补偿成功: orderId={}", event.getOrderId());
}
}
// 补偿:恢复余额
@EventListener
public void compensateBalance(OrderCancelledEvent event) {
SagaState state = sagaStates.get(event.getOrderId());
if (state == SagaState.BALANCE_DEDUCTED) {
accountService.refund(event.getOrderId());
log.info("余额补偿成功: orderId={}", event.getOrderId());
}
}
}