Spring Event 学习笔记

Spring Event 学习笔记

从入门到进阶的系统化指南


📚 目录

  1. 背景与动机
  2. 核心概念与定义
  3. 基础使用:同步事件
  4. 进阶使用:异步事件
  5. 高级特性:事务事件监听
  6. [Spring Event vs 消息队列](#Spring Event vs 消息队列)
  7. 最佳实践
  8. 常见问题与解决方案
  9. 源码原理解析
  10. 实战案例

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);
    }
}

核心机制

  1. 监听器被包装成 TransactionSynchronization
  2. 注册到当前事务的同步回调中
  3. 在事务提交/回滚时触发监听器

10. 实战案例

10.1 案例 1: 用户注册系统

需求:

用户注册成功后需要:

  1. 发送欢迎邮件
  2. 赠送新用户积分
  3. 发送短信通知
  4. 记录注册日志
实现:
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: 订单支付流程

需求:

订单支付成功后需要:

  1. 扣减库存
  2. 创建物流单
  3. 生成发票
  4. 发送支付成功通知
  5. 更新用户会员积分
实现:
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());
        }
    }
}
相关推荐
努力也学不会java2 小时前
【Spring Cloud】负载均衡-LoadBalance
java·人工智能·后端·spring·spring cloud·负载均衡
承渊政道2 小时前
C++学习之旅【C++Stack和Queue类介绍—入门指南与核心概念解析】
c语言·数据结构·c++·学习·visual studio
嗯? 嗯。2 小时前
S32K144开发笔记-S32k系列芯片基础知识
笔记·s32k
Gain_chance2 小时前
18-学习笔记尚硅谷数仓搭建-数据仓库运行环境搭建(hive的安装及配置)
数据仓库·hive·笔记·学习
Tina Tang2 小时前
【Hello Agent】第一章 初识智能体笔记
笔记
好学且牛逼的马2 小时前
【手写Easy-Spring|1】
java·后端·spring
QZ_orz_freedom2 小时前
后端学习笔记-WebSocket
笔记·学习
Gain_chance2 小时前
20-学习笔记尚硅谷数仓搭建-数据仓库开发环境搭建-hive连接DataGrip
数据仓库·hive·笔记·学习·datagrip
近津薪荼2 小时前
优选算法——双指针专题3(快慢双指针)
c++·学习·算法