乐于分享
好东西不私藏

若依AI学习第5天@EventListener使用

若依AI学习第5天@EventListener使用

📖 简介

@EventListener 是 Spring Framework 提供的一个强大注解,用于实现观察者模式(Observer Pattern),也称为事件驱动编程。它允许我们在 Spring 应用中发布和监听自定义事件,实现组件之间的解耦。

核心优势

  • • ✅ 松耦合:事件发布者和监听者之间无需直接依赖
  • • ✅ 异步处理:配合 @Async 注解实现异步事件处理
  • • ✅ 条件过滤:支持 SpEL 表达式进行条件监听
  • • ✅ 易于扩展:新增监听器无需修改现有代码

🎯 核心概念

  • • 事件(Event):继承自 ApplicationEvent 的类,封装事件数据
  • • 事件发布者(Publisher):通过 ApplicationEventPublisher 发布事件
  • • 事件监听器(Listener):使用 @EventListener 注解的方法监听和处理事件

💡 实战示例:订单系统

下面我们通过一个订单系统的完整示例,演示 @EventListener 的各种用法。

1. 创建订单事件

/** * 订单创建事件 */@DatapublicclassOrderCreatedEventextendsApplicationEvent {privatefinal Long orderId;      // 订单IDprivatefinal String productName; // 商品名称privatefinal Integer amount;     // 订单金额publicOrderCreatedEvent(Object source, Long orderId, String productName, Integer amount) {super(source);this.orderId = orderId;this.productName = productName;this.amount = amount;    }}

2. 发布事件

@ServicepublicclassOrderService {@Autowiredprivate ApplicationEventPublisher eventPublisher;publicvoidcreateOrder(Long orderId, Integer userId, String productName, int amount) {        System.out.println("create order : " + orderId);// 发布订单创建事件OrderCreatedEventevent=newOrderCreatedEvent(this, orderId, productName, amount);        eventPublisher.publishEvent(event);    }}

3. 监听事件

@ComponentpublicclassOrderEventListener {// 同步监听 - 发送短信通知@EventListenerpublicvoidhandleOrderEvent(OrderCreatedEvent event) {        System.out.println("【同步】发送短信通知,订单号: " + event.getOrderId());    }// 异步监听 - 更新库存@Async@EventListenerpublicvoidupdateInventory(OrderCreatedEvent event) {        System.out.println("【异步】更新库存 - 商品: " + event.getProductName());try {            Thread.sleep(1000); // 模拟耗时操作        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("【异步】库存更新完成");    }// 条件监听 - 金额大于1000的订单@EventListener(condition = "#event.amount > 1000")publicvoidhandleHighValueOrder(OrderCreatedEvent event) {        System.out.println("【高价值订单】订单金额: " + event.getAmount() + ",需要人工审核");    }}

4. 测试接口

@RestController@RequestMapping("/api/event")publicclassEventController {@Autowiredprivate OrderService orderService;/**     * 创建订单     */@GetMapping("/create-order")public String createOrder(@RequestParam Long orderId,@RequestParam String productName,@RequestParam Integer userId,@RequestParam Integer amount) {        System.out.println("========== 开始创建订单 ==========");        orderService.createOrder(orderId, userId, productName, amount);        System.out.println("========== 订单创建请求完成 ==========");return"订单创建成功,订单ID: " + orderId;    }}

🚀 测试验证

测试命令

# 测试普通订单(金额 < 1000)curl "http://localhost:9005/api/event/create-order?orderId=1001&productName=手机&userId=1&amount=500"# 测试高价值订单(金额 > 1000)curl "http://localhost:9005/api/event/create-order?orderId=1002&productName=笔记本电脑&userId=1&amount=5000"

执行结果分析

高价值订单(金额 5000)

========== 开始创建订单 ==========create order : 1002【同步】发送短信通知,订单号: 1002【高价值订单】订单金额: 5000,需要人工审核【异步】更新库存 - 商品: 笔记本电脑========== 订单创建请求完成 ==========【异步】库存更新完成

🔧 高级特性

1. 同步监听

默认情况下,@EventListener 是同步执行的,监听器会在发布事件的线程中执行。

@EventListenerpublicvoidhandleOrderEvent(OrderCreatedEvent event) {// 同步执行    System.out.println("【同步】发送短信通知,订单号: " + event.getOrderId());}

适用场景: 需要事务一致性、事件处理逻辑简单

2. 异步监听

配合 @Async 注解,可以在独立线程中异步处理事件。

@Async@EventListenerpublicvoidupdateInventory(OrderCreatedEvent event) {// 异步执行    System.out.println("【异步】更新库存 - 商品: " + event.getProductName());}

适用场景: 耗时操作(如发送邮件、调用第三方接口)、需要提高系统吞吐量

注意: 使用 @Async 需要先在配置类上添加 @EnableAsync 注解。

3. 条件监听

使用 SpEL 表达式进行条件过滤,只有满足条件的事件才会被处理。

@EventListener(condition = "#event.amount > 1000")publicvoidhandleHighValueOrder(OrderCreatedEvent event) {    System.out.println("【高价值订单】订单金额: " + event.getAmount() + ",需要人工审核");}

常用 SpEL 表达式:

  • • #event.amount > 1000 - 订单金额大于1000
  • • #event.amount > 1000 and #event.amount < 5000 - 金额在1000到5000之间

⚠️ 注意事项

1. 企业级应用的局限性

@EventListener 是 Spring 容器内的内存机制,存在以下限制:

局限性
说明
应用重启
未完成的异步任务会丢失
应用崩溃
内存中待处理的事件会丢失
分布式环境
无法跨 JVM 传递事件

2. 异步监听器的异常处理

异步监听器中的异常不会影响主流程,但需要单独处理。

@Async@EventListenerpublicvoidhandleAsyncEvent(OrderCreatedEvent event) {try {// 业务逻辑    } catch (Exception e) {        System.err.println("异步事件处理失败: " + e.getMessage());    }}

3. 事务传播

同步监听器默认参与当前事务,异步监听器在新事务中执行。


🎓 最佳实践

1. 企业级组合使用策略

在实际项目中,通常组合使用多种技术形成完整的事件驱动架构:

【同步关键业务】→ 【EventListener处理非关键逻辑】→ 【消息队列处理跨服务/持久化任务】

典型场景示例:

场景
技术选择
说明
订单创建 → 更新本地缓存
@EventListener
轻量级解耦
订单创建 → 记录操作日志
@EventListener
 + @Async
异步日志,不阻塞主流程
订单创建 → 发送短信通知
消息队列(RabbitMQ/Kafka)
需要持久化,可重试
订单创建 → 跨服务数据同步
消息队列
分布式环境

适用场景判断:

  • • ✅ 适合使用 @EventListener:应用内解耦、非关键业务逻辑、单机应用
  • • ❌ 不适合使用 @EventListener:需要持久化的任务、分布式系统、高可靠性要求

3. RuoYi-AI 实际案例:登录日志异步记录

事件定义:

// LogininforEvent.javapublicclassLogininforEventextendsApplicationEvent {privatefinal HttpServletRequest request;// ... }

监听器实现:

@Async@EventListenerpublicvoidrecordLogininfor(LogininforEvent logininforEvent) {HttpServletRequestrequest= logininforEvent.getRequest();finalUserAgentuserAgent= UserAgentUtil.parse(request.getHeader("User-Agent"));finalStringip= ServletUtils.getClientIP(request);Stringaddress= AddressUtils.getRealAddressByIP(ip);Stringos= userAgent.getOs().getName();Stringbrowser= userAgent.getBrowser().getName();// 插入登录日志到数据库    insertLogininfor(logininfor);}

作用: 当用户登录时,系统发布 LogininforEvent 事件,该方法异步监听并记录登录日志(包括 IP 地址、浏览器、操作系统等),避免阻塞主线程,提升登录响应速度。

4. 命名规范

  • • 事件类以 Event 结尾:OrderCreatedEvent
  • • 监听器类以 Listener 结尾:OrderEventListener
  • • 监听方法以 handle 开头:handleOrderEvent

❓ 常见问题

Q1: 同步监听和异步监听有什么区别?

A:

  • • 同步监听:在发布事件的线程中执行,会阻塞主流程,适合需要事务一致性的场景
  • • 异步监听:在独立线程中执行,不阻塞主流程,适合耗时操作

Q2: 如何确保异步监听器的执行顺序?

A: 异步监听器的执行顺序由线程池调度决定,无法保证顺序。如果需要顺序执行,应该使用同步监听器。

Q3: 如何避免事件处理失败影响主流程?

A:

  • • 使用异步监听器
  • • 在同步监听器中添加异常处理
@EventListenerpublicvoidhandleEvent(OrderCreatedEvent event) {try {// 业务逻辑    } catch (Exception e) {        System.err.println("事件处理失败: " + e.getMessage());    }}

📝 总结

@EventListener 注解是 Spring 框架中实现事件驱动编程的核心工具,通过它可以轻松实现组件之间的解耦和异步处理。

核心要点

  1. 1. 事件驱动:通过发布-订阅模式实现组件解耦
  2. 2. 同步异步:支持同步和异步两种事件处理方式
  3. 3. 条件过滤:使用 SpEL 表达式实现灵活的事件过滤
  4. 4. 易于扩展:新增监听器无需修改现有代码

适用场景

  • • 订单系统(订单创建、支付、发货等事件)
  • • 用户系统(注册、登录、注销等事件)
  • • 消息通知(邮件、短信、推送等)
  • • 数据同步(缓存更新、索引同步等)

通过合理使用 @EventListener,可以构建出更加灵活、可维护的 Spring 应用程序。


🔗 项目源码

  • • jing-yes-java/jingyes-spring-async