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 |
EventManager | Facade 与事件发布实现之间的 contract,可被测试替身或业务自定义实现替换 |
DefaultEventManager | 包装 ApplicationEventPublisher;同步发布直接转交 Spring,本地异步发布优先使用 AsyncTaskExecutor |
EventMessage | 命名事件载体,用于字符串事件名 + payload 的简单扩展点 |
EventPublishResult | 发布结果快照,只表示事件已交给本地发布器 |
EventProperties | 绑定 spring.open.event.*,控制 starter 启停和内置异步执行器 |
运行链路:
- 应用启动时,
EventAutoConfiguration读取spring.open.event.*并注册默认 Manager。 - 如果
spring.open.event.async.enabled=true且没有业务自定义eventTaskExecutor,自动创建内置异步执行器。 EventManagerProvider在所有单例 Bean 初始化完成后绑定EventFacade,避免初始化阶段拿到半成品 Manager。Event.publish(Object)直接发布强类型事件;Event.publish(String, payload)会先封装为EventMessage。publishAsync优先提交给eventTaskExecutor;未启用或未注册异步执行器时退回同步发布,并返回已完成CompletableFuture。EventPublishResult只表示事件已交给本地 Spring 事件机制,不代表监听器全部完成、跨实例投递成功或持久化成功。
扩展边界
- Event 只负责本进程内 Spring 事件发布,不持久化、不跨实例、不做可靠重试。
- Listener 注册、事务后监听和事件分发器替换继续使用 Spring 原生
@EventListener、@TransactionalEventListener和ApplicationEventMulticaster。 - 自定义异步执行器时注册名为
eventTaskExecutor的AsyncTaskExecutorBean。 - 需要持久化任务、失败重试、延迟执行或多实例消费时,使用 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-| 配置 | 默认值 | 说明 |
|---|---|---|
enabled | true | 启用开关 |
async.enabled | true | 是否启用内置异步发布线程池 |
async.core-size | 2 | 核心线程数 |
async.max-size | 8 | 最大线程数 |
async.queue-capacity | 1000 | 队列容量 |
async.thread-name-prefix | spring-open-event- | 线程名前缀 |
如需完全复用业务自定义线程池,注册名为 eventTaskExecutor 的 AsyncTaskExecutor Bean。
Provider
不提供 Provider 切换(底层固定为 Spring ApplicationEventPublisher)。
扩展能力通过 Spring 原生机制实现:
@EventListener/@TransactionalEventListener注册监听器ApplicationEventMulticaster替换默认事件分发器- 自定义
AsyncTaskExecutor注册eventTaskExecutorBean 替换异步线程池
命名约定
- 事件名使用点分格式:
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