跳到内容

Event Starter

受众:开发者 摘要:统一事件 Facade。同步 / 异步发布,基于 Spring ApplicationEventPublisher,监听侧用 Spring 原生 @EventListener / @TransactionalEventListener

何时使用

场景建议
模块内 / 跨模块解耦的低耦合钩子Event.publish(...)
数据库写操作完成后的副作用(通知、缓存刷新、审计)@TransactionalEventListener(AFTER_COMMIT)
简单扩展点、插件式通知命名事件 Event.publish("user.created", payload)
领域核心事件(重构友好)强类型 Java 事件
耗时任务、可重试任务、跨实例消费Queue,不要把 Event 当持久化队列
延迟执行Scheduler

Event 是 Spring 本地事件机制的统一入口,持久化、跨实例、保证可靠重试。

源码机制骨架

Event starter 的核心边界是 Facade + Manager:

职责
Event业务侧静态 Facade,不保存 Spring 上下文,委托当前绑定的 EventManager
EventAutoConfiguration装配默认 EventManager、可选 eventTaskExecutor 和 Facade 绑定器
EventManagerProvider在 Spring 单例初始化完成后绑定 Facade,容器销毁时只清理当前 Manager
EventManagerFacade 与事件发布实现之间的 contract,可被测试替身或业务自定义实现替换
DefaultEventManager包装 ApplicationEventPublisher;同步发布直接转交 Spring,本地异步发布优先使用 AsyncTaskExecutor
EventMessage命名事件载体,用于字符串事件名 + payload 的简单扩展点
EventPublishResult发布结果快照,只表示事件已交给本地发布器
EventProperties绑定 spring.open.event.*,控制 starter 启停和内置异步执行器

运行链路:

  1. 应用启动时,EventAutoConfiguration 读取 spring.open.event.* 并注册默认 Manager。
  2. 如果 spring.open.event.async.enabled=true 且没有业务自定义 eventTaskExecutor,自动创建内置异步执行器。
  3. EventManagerProvider 在所有单例 Bean 初始化完成后绑定 Event Facade,避免初始化阶段拿到半成品 Manager。
  4. Event.publish(Object) 直接发布强类型事件;Event.publish(String, payload) 会先封装为 EventMessage
  5. publishAsync 优先提交给 eventTaskExecutor;未启用或未注册异步执行器时退回同步发布,并返回已完成 CompletableFuture
  6. EventPublishResult 只表示事件已交给本地 Spring 事件机制,不代表监听器全部完成、跨实例投递成功或持久化成功。

扩展边界

  • Event 只负责本进程内 Spring 事件发布,不持久化、不跨实例、不做可靠重试。
  • Listener 注册、事务后监听和事件分发器替换继续使用 Spring 原生 @EventListener@TransactionalEventListenerApplicationEventMulticaster
  • 自定义异步执行器时注册名为 eventTaskExecutorAsyncTaskExecutor Bean。
  • 需要持久化任务、失败重试、延迟执行或多实例消费时,使用 Queue / Scheduler,不把 Event 当作轻量 MQ。
  • 命名事件 payload 只放稳定字段,不放完整 Entity、敏感配置、密钥或私钥类字段。

快速开始

yaml
spring:
  open:
    event:
      enabled: true

发布事件:

java
// 命名事件(适合简单钩子)
Event.publish("user.created", Map.of("userId", 1L));

// 强类型事件(适合领域核心事件)
public record UserCreatedEvent(Long userId, String username) {}
Event.publish(new UserCreatedEvent(1L, "admin"));

监听事件:

java
@Component
public class UserEventListener {
    @EventListener
    public void onUserCreated(UserCreatedEvent event) {
        // 业务 Service 调用
    }
}

Facade API

方法说明
Event.publish(Object event)发布强类型事件
Event.publish(String name)发布无载荷命名事件
Event.publish(String name, Map<String, Object> payload)发布带载荷命名事件
Event.publishAsync(Object event)异步发布强类型事件,返回 CompletableFuture<EventPublishResult>
Event.publishAsync(String name)异步发布无载荷命名事件
Event.publishAsync(String name, Map<String, Object> payload)异步发布带载荷命名事件

EventPublishResult 包含事件类型、命名事件名称、是否异步、发布时间。关闭内置异步线程池时异步发布自动退回同步。

监听命名事件

java
@Component
public class NamedEventListener {
    @EventListener
    public void onEvent(EventMessage event) {
        if (!"user.created".equals(event.getName())) return;
        Long userId = ((Number) event.getPayload().get("userId")).longValue();
        // 业务处理
    }
}

事务提交后监听

java
@Component
public class UserAfterCommitListener {
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void onUserCreated(UserCreatedEvent event) {
        // 只在数据库事务提交成功后执行
    }
}

涉及通知、缓存刷新、审计、跨模块副作用,优先用 AFTER_COMMIT,避免事务回滚后仍执行副作用。

配置项

yaml
spring:
  open:
    event:
      enabled: true
      async:
        enabled: true
        core-size: 2
        max-size: 8
        queue-capacity: 1000
        thread-name-prefix: spring-open-event-
配置默认值说明
enabledtrue启用开关
async.enabledtrue是否启用内置异步发布线程池
async.core-size2核心线程数
async.max-size8最大线程数
async.queue-capacity1000队列容量
async.thread-name-prefixspring-open-event-线程名前缀

如需完全复用业务自定义线程池,注册名为 eventTaskExecutorAsyncTaskExecutor Bean。

Provider

不提供 Provider 切换(底层固定为 Spring ApplicationEventPublisher)。

扩展能力通过 Spring 原生机制实现:

  • @EventListener / @TransactionalEventListener 注册监听器
  • ApplicationEventMulticaster 替换默认事件分发器
  • 自定义 AsyncTaskExecutor 注册 eventTaskExecutor Bean 替换异步线程池

命名约定

  • 事件名使用点分格式:user.created / payment.paid / config.changed
  • 强类型事件类名使用 XxxEvent 后缀:UserCreatedEvent / PaymentPaidEvent
  • 命名规则与 Notification 事件 payload 保持一致(详见 .ai/decisions/adr-029-dynamic-metadata-i18n.md

使用建议

  • 领域内核心事件优先用强类型 Java 对象(便于重构、IDE 类型检查)
  • 简单扩展点、插件式通知用命名事件
  • Listener 不写 Controller 逻辑,业务处理放在 Service
  • Listener 中有写库操作时按业务边界加事务
  • 重任务、可靠重试、延迟执行、跨实例消费用 Queue / Scheduler,不要把 Event 当持久化队列

组合建议

仓库不再维护独立 Event 样例模块。命名 / 强类型 / 异步 / 事务后监听 / Event → Queue / Event → Scheduler 这类组合用法应直接写在真实业务模块或 starter 测试中;需要持久化、重试或跨实例投递时,优先把可靠性边界交给 Queue / Scheduler

注意事项

  • Event 是本地事件机制:不跨实例、不持久化、不可靠重试
  • 跨实例事件总线、MQ 事件桥接、事件持久化等需求后续扩展应放在 Event starter 边界后,不改变业务侧 Facade
  • 异步发布失败时通过日志感知,无重试表

验证命令

bash
./mvnw -pl spring-open-starters/spring-open-starter-event -am test -DskipITs

相关

  • Queue(持久化异步任务):queue.md
  • Scheduler(定时调度):scheduler.md
  • Notification(通知通道):notification.md
  • 通知事件 payload 标准化:.ai/decisions/adr-029-dynamic-metadata-i18n.md
  • Provider 扩展模型:provider.md

Released under the Apache License 2.0.