事件
1.介绍
事件消息,简称事件。这类消息用于传达业务领域或应用中发生了某件事,应用的其他部分甚至其他外部应用,都可以对这些事件做出响应
2.@EventHandler
介绍
@EventHandler 是能够处理事件消息的方法,其作用是对应用中发生的事件做出响应
在Axon中,一个对象可通过为多个方法标注@EventHandler注解来声明多个事件处理器,这类对象通常被称为事件处理组件(事件处理器)
编写标注@EventHandler的方法时,方法声明的参数会决定它能接收哪些事件,参数的匹配逻辑直接关联事件的分发目标
参数
第一个参数,它对应事件消息的负载。若事件处理器无需直接访问消息负载,也可在@EventHandler注解上显式指定预期的负载类型
注意:若希望将负载作为参数传入方法,则不要在@EventHandler注解上配置负载类型
匹配规则
在任何情况下,每个事件处理器针对单个事件最多只会调用一个@EventHandler标注的方法。Axon会按照以下规则查找 最匹配”的方法并执行:
- 当前类:评估所有标注@EventHandler的方法
- 参数最具体:若找到多个匹配的@EventHandler方法,则会选择参数类型最具体的方法并调用
- 父类重走逻辑:若在当前类层级未找到匹配方法,则按相同逻辑评估其父类
- 忽略:若遍历到类层级的最顶层(如Object类)仍未找到合适的事件处理器,该事件会被忽略
注册
介绍
事件处理组件需通过EventProcessingConfigurer进行注册,该配置器可从全局的Axon Configurer中获取
- 通常,一个应用只需定义一个EventProcessingConfiguration
- 对于大型、模块化的应用,也可为每个模块单独定义一个EventProcessingConfiguration
原生API
public class AxonConfig {
// 省略其他配置方法...
public void configureEventHandler(Configurer configurer) {
// 方式1:直接注册事件处理组件(仅处理 @EventHandler 方法)
configurer.registerEventHandler(
config -> new MyEventHandlingComponent()
);
// 方式2:注册包含多种消息处理器的组件(如同时有 @EventHandler、@CommandHandler)
configurer.registerMessageHandler(
config -> new MyEventHandlingComponent()
);
// 方式3:通过 EventProcessingConfigurer 直接注册(更显式的事件处理配置)
configurer.eventProcessing()
.registerEventHandler(config ->
new MyEventHandlingComponent()
);
}
}
SpringBoot
@Component // 纳入 Spring 上下文,Axon 会自动扫描 @EventHandler 方法
public class MyEventHandlingComponent {
@EventHandler
public void on(SomeEvent event) {
// 处理 SomeEvent 事件的业务逻辑
// 示例:更新查询模型、发送通知、触发后续命令等
}
// 可添加更多 @EventHandler 方法处理其他事件
@EventHandler
public void on(AnotherEvent event) {
// 处理 AnotherEvent 事件
}
}
3.发布事件
介绍
在Axon Framework应用中,事件发布的源头可能来自多个位置,总体可归纳为两大场景:
- 从聚合中分发事件
- 从常规组件中分发事件
从聚合中分发事件
介绍
聚合或其内部的实体通常是所有事件消息的起点。事件消息本质上是业务决策已完成的通知,即命令消息处理成功后的结果
约束
从聚合中发布事件时,必须在聚合实例的生命周期内执行。这一约束的原因有两点:
- 确保聚合标识符能与事件消息绑定,建立事件与聚合的归属关系
- 保证事件按顺序生成:Axon会为聚合产生的每个事件添加序号,以此维护事件的时序一致性
代码
AggregateLifecycle提供了简洁的API来实现上述需求,核心方法为静态的apply(),使用时需静态导入:
import static org.axonframework.modelling.command.AggregateLifecycle.apply;
public class GiftCard {
@AggregateIdentifier
private String id;
private int balance;
// 命令处理器:处理“发行礼品卡”命令
@CommandHandler
public GiftCard(IssueCardCommand cmd) {
// 发布“礼品卡已发行”事件,事件数据来自命令
apply(new CardIssuedEvent(cmd.getCardId(), cmd.getAmount()));
}
// 事件溯源处理器:通过事件重建聚合状态(示例)
@EventSourcingHandler
public void on(CardIssuedEvent event) {
this.id = event.getCardId();
this.balance = event.getAmount();
}
// 省略其他命令处理器、事件处理器及状态字段
}
调用AggregateLifecycle.apply方法后,Axon会按以下步骤处理:
- 获取当前聚合的作用域,确认事件所属的聚合实例
- 读取聚合的 最后已知序号,并将其作为待发布事件的序号(确保时序连续)
- 将传入的事件负载包装为EventMessage,同时为消息附加步骤2中获取的序号,以及当前聚合的标识符
- 先将事件分发给聚合内部所有关注该事件的事件处理器(通常是@EventSourcingHandler标注的方法)。这一步对事件溯源至关重要,通过处理事件来更新聚合的状态
- 聚合内部处理完事件后,再将该事件发布到EventBus,供应用其他组件或外部应用订阅
带元数据与后续逻辑的事件发布代码如下:
@CommandHandler
public void handle(RedeemCardCommand cmd) {
if (this.balance < cmd.getRedeemAmount()) {
throw new InsufficientBalanceException(id, balance, cmd.getRedeemAmount());
}
// 1. 发布“礼品卡已兑换”事件,并附加元数据(兑换门店ID)
apply(
new CardRedeemedEvent(id, cmd.getRedeemAmount()),
MetaData.with("shopId", cmd.getShopId()) // 元数据:门店ID
)
// 2. 事件发布后,执行后续逻辑(如记录兑换日志)
.andThen(() -> log.info("Card {} redeemed at shop {}", id, cmd.getShopId()))
// 3. 若余额为0,发布“礼品卡已失效”事件
.andThenApplyIf(
() -> this.balance == 0, // 条件:余额为0
() -> new CardExpiredEvent(id) // 满足条件时发布的事件
);
}
// 事件溯源处理器:处理 CardRedeemedEvent 以更新余额
@EventSourcingHandler
public void on(CardRedeemedEvent event) {
this.balance -= event.getRedeemAmount();
}
从非聚合组件中分发事件
介绍
绝大多数情况下,事件由聚合通过apply()方法发布。但偶尔也会有需求:从其他组件中直接向EventGateway发布事件
EventGateway是Axon提供的事件发布网关,封装了向EventBus发送事件的逻辑,是非聚合组件发布事件的推荐方式
代码
import org.axonframework.eventhandling.gateway.EventGateway;
import org.springframework.stereotype.Service;
@Service // 常规 Spring 服务组件(非聚合)
public class GiftCardNotificationService {
// 注入 EventGateway(Axon 自动配置,或手动配置)
private final EventGateway eventGateway;
// 构造函数注入
public GiftCardNotificationService(EventGateway eventGateway) {
this.eventGateway = eventGateway;
}
// 发布“礼品卡兑换通知”事件(非聚合发起)
public void sendRedeemNotification(String cardId, int amount, String userId) {
// 直接通过 EventGateway 发布事件
eventGateway.publish(
new CardRedeemNotifiedEvent(cardId, amount, userId, System.currentTimeMillis())
);
}
}
注意
- 非聚合组件发布的事件不会自动附加聚合标识符和序号(因不属于任何聚合),若需关联聚合信息,需手动在事件负载或元数据中添加
- EventGateway的publish()方法默认是异步的,若需同步等待事件发布结果,可使用publishAndWait()方法
4.处理器组织结构与管理
组织结构
- 事件处理程序(EventHandler):接收事件时需执行的业务逻辑
- 事件处理器(EventProcessor):则是负责处理事件处理过程中技术层面工作的组件。它会启动一个工作单元,可能还会开启事务;此外,它还需确保事件处理期间创建的所有消息能正确附加关联数据,同时满足其他非功能需求(如性能、容错等)
- 处理组(Processing Group):每个EventHandler仅属于一个处理组。处理组为事件处理提供了可配置的非功能需求能力,例如错误处理、排序策略等
关系
- 处理组与事件处理程序:一对多
- 事件处理器与处理组:一对多(大多数场景下是一对一映射)
事件处理器的两种类型
- 订阅式事件处理器:订阅事件源,由事件发布机制管理的线程调用执行
- 流处理式事件处理器:通过自身管理的线程,从事件源中拉取消息执行处理
注意
配置过程中会用到EventProcessingConfigure,它是Axon配置API的一部分,专门用于配置事件处理器
为事件处理程序分配处理组与事件处理器
介绍
所有事件处理器都有一个名称,该名称在多个JVM实例间唯一标识一个处理器实例,两个名称相同的处理器会被视为同一处理器的不同实例
所有事件处理程序默认会附加到名称为其类所在包名的事件处理器上。此外,Axon默认使用的事件处理器实现是TrackingEventProcessor(跟踪式事件处理器)
注意
事件处理程序大致分为两类:
- 常规事件处理程序
- Saga
本文介绍常规事件处理程序的流程
案例
默认分组
假设注册了以下3个事件处理程序:
- org.axonframework.example.eventhandling.MyHandler
- org.axonframework.example.eventhandling.MyOtherHandler
- org.axonframework.example.eventhandling.module.ModuleHandler
若不进行任何干预,Axon会自动创建两个事件处理器:
- 名称为org.axonframework.example.eventhandling的处理器:管理MyHandler和MyOtherHandler两个事件处理程序
- 名称为org.axonframework.example.eventhandling.module的处理器:管理ModuleHandler一个事件处理程序
自定义分组
按包名分组是合理的默认行为,但推荐为事件处理器程序手动指定处理组。最简洁的方式是使用@ProcessingGroup注解,该注解对应前文提到的处理组层级,可直接为事件处理程序指定所属处理组
@ProcessingGroup注解需传入一个名称,且仅能标注在类上。对上述示例进行调整,使用该注解替代包名分组:
@ProcessingGroup("my-handlers") // 指定所属处理组为 "my-handlers"
class MyHandler {
// 省略事件处理函数...
}
@ProcessingGroup("my-handlers") // 与 MyHandler 同属一个处理组
class MyOtherHandler{
// ...
}
@ProcessingGroup("module-handlers") // 单独属于 "module-handlers" 处理组
class ModuleHandler {
// ...
}
通过上述配置,Axon会创建两个事件处理器:
- 名称为my-handlers的处理器:管理MyHandler和MyOtherHandler
- 名称为module-handlers的处理器:管理ModuleHandler
事件处理程序分配规则
介绍
Axon的配置API允许自定义事件处理类到处理器的分配策略,或特定处理器实例到指定事件处理器的分配策略。这些规则可分为两大类:
- 事件处理程序 → 处理组
- 处理组 → 事件处理器
以下是EventProcessingConfigurer提供的所有分配规则:
事件处理程序到处理组
- byDefaultAssignTo(String):定义事件处理程序的默认处理组名称。仅在无更具体规则且未标注@ProcessingGroup注解时生效
- byDefaultAssignHandlerInstancesTo(Function<Object, String>):通过Lambda表达式为事件处理程序实例分配处理组(返回处理组名称)。仅在无更具体规则且未标注@ProcessingGroup注解时生效
- byDefaultAssignHandlerTypesTo(Function<Class<?>, String>):通过Lambda表达式为事件处理程序类型分配处理组(返回处理组名称)。仅在无更具体规则且未标注@ProcessingGroup注解时生效
- assignHandlerInstancesMatching(String, Predicate<Object>):根据事件处理程序实例是否匹配Predicate条件,将其分配到指定处理组。默认优先级为0;若一个实例匹配多个条件,结果未定义
- assignHandlerTypesMatching(String, Predicate<Class<?>>):根据事件处理程序类型是否匹配Predicate条件,将其分配到指定处理组。默认优先级为0;若一个类型匹配多个条件,结果未定义
- assignHandlerInstancesMatching(String, int, Predicate<Object>):功能与第4点类似,但可自定义优先级(数值越高,规则优先级越高)
- assignHandlerTypesMatching(String, int, Predicate<Class<?>>):功能与第5点类似,但可自定义优先级(数值越高,规则优先级越高)
处理组到事件处理器
- assignProcessingGroup(String, String):将指定名称的处理组,分配给指定名称的事件处理器
- assignProcessingGroup(Function<String, String>):通过Lambda表达式为处理组分配事件处理器(输入处理组名称,返回事件处理器名称)
事件处理器内部的事件处理器程序顺序问题
介绍
事件处理器内部的事件处理程序顺序,由事件处理程序的注册顺序决定
即:事件处理器调用事件处理程序的顺序,与它们在配置API中的注册顺序一致
Spring环境下的显式排序
若使用Spring进行依赖注入,可通过@Order注解显式指定事件处理程序的排序。该注解标注在事件处理程序类上,通过整数参数指定优先级(数值越小,优先级越高):
@ProcessingGroup("my-handlers")
@Order(1) // 优先级高于 @Order(2) 的组件
public class HighPriorityHandler {
@EventHandler
public void on(ImportantEvent event) {
// 高优先级逻辑
}
}
@ProcessingGroup("my-handlers")
@Order(2) // 优先级低于 @Order(1) 的组件
public class LowPriorityHandler {
@EventHandler
public void on(ImportantEvent event) {
// 低优先级逻辑(在 HighPriorityHandler 之后执行)
}
}
注意事项
- 无法对属于不同事件处理器的事件处理程序进行排序。每个事件处理器都是独立组件,不受其他事件处理器干预
- 虽然支持事件处理器内部排序,但推荐尽量拆分事件处理器;排序意味着组件间存在耦合,会增加事件处理流程的复杂度(如新人理解成本);此外,全局排序可能导致所有事件处理程序集中在一个处理流程中,降低整体处理速度。因此,除非必要,否则不建议过度使用排序功能
错误处理
介绍
任何应用都无法避免错误,处理方式需根据错误发生的位置而定
处理组和事件处理器两个层级,均支持自定义错误处理行为
默认行为
- 事件处理程序抛出异常:默认由处理组层级捕获异常,记录日志后继续处理下一个事件
- 事务提交、令牌更新等非事件处理器环节抛出异常:异常会向上传播,处理方式因事件处理器类型而异:
- 流处理式事件处理器:进入错误模式,释放所有令牌,并以递增间隔重试(初始1秒,最大60秒)
- 订阅式事件处理器:向提供事件的组件报告发布错误
处理组层级(ListenerInvocationErrorHandler)
介绍
ListenerInvocationErrorHandler负责处理事件处理程序方法抛出的异常。默认实现为LoggingErrorHandler:记录异常日志后,继续处理下一个处理程序或事件
配置
原生API:
public class AxonConfig {
// 省略其他配置方法...
public void configureProcessingGroupErrorHandling(EventProcessingConfigurer processingConfigurer) {
// 1. 配置所有处理组的默认错误处理器
processingConfigurer
.registerDefaultListenerInvocationErrorHandler(conf -> {
// 示例:返回自定义 ListenerInvocationErrorHandler 实例
return (exception, event, eventHandler) -> {
// 自定义逻辑:如重试、忽略、死信队列投递等
log.error("EventHandler [{}] failed to process event [{}]",
eventHandler.getClass().getSimpleName(),
event.getPayloadType().getSimpleName(),
exception);
// 若需向上传播异常,直接抛出即可
// throw new RuntimeException("Propagate error", exception);
};
})
// 2. 为特定处理组配置专属错误处理器
.registerListenerInvocationErrorHandler("my-processing-group", conf -> {
// 为 "my-processing-group" 处理组配置自定义错误处理器
return new CustomMyGroupErrorHandler();
});
}
}
SpringBoot环境:
@Configuration
public class AxonConfig {
// 省略其他配置方法...
@Bean
public ConfigurerModule processingGroupErrorHandlingConfigurerModule() {
return configurer -> configurer.eventProcessing(processingConfigurer ->
processingConfigurer
// 配置默认错误处理器
.registerDefaultListenerInvocationErrorHandler(conf -> {
return new LoggingErrorHandler(); // 也可使用自定义实现
})
// 为特定处理组配置错误处理器
.registerListenerInvocationErrorHandler(
"my-processing-group",
conf -> new CustomMyGroupErrorHandler()
)
);
}
}
自定义ListenerInvocationErrorHandler
实现ListenerInvocationErrorHandler接口即可自定义错误处理逻辑,接口提供以下参数:
public interface ListenerInvocationErrorHandler {
void onError(Exception exception,
EventMessage<?> event,
EventMessageHandler eventHandler) throws Exception;
}
- exception:事件处理程序抛出的异常
- event:待处理的事件
- eventHandler:抛出异常的事件处理程序
可根据需求选择重试忽略或向上传播异常;若向上传播,异常会进入事件处理器层级
事件处理器层级(ErrorHandler)
介绍
事件处理程序方法外抛出的异常或从事件处理程序层级传播上来的异常,由ErrorHandler处理。默认实现为PropagatingErrorHandler:直接重新抛出所有捕获的异常
不同事件处理器类型的异常传播行为
- 订阅式事件处理器(SubscribingEventProcessor):异常会传播给事件发布者
- 流处理式事件处理器(StreamingEventProcessor):进入错误模式,重试失败后暂停处理
配置
原生API:
public class AxonConfig {
public void configure(EventProcessingConfigurer configurer) {
configurer
// 1. 配置所有事件处理器的默认错误处理器
.registerDefaultErrorHandler(conf -> {
// 示例:自定义 ErrorHandler,支持重试
return new RetryingErrorHandler(
IntervalRetryScheduler.builder()
.retryCount(3)
.interval(Duration.ofSeconds(1))
.build()
);
})
// 2. 为特定事件处理器配置专属错误处理器
.registerErrorHandler("my-processor", conf -> {
return new CustomProcessorErrorHandler();
});
}
}
SpringBoot环境:
@Configuration
public class AxonConfig {
@Bean
public ConfigurerModule processorErrorHandlingConfigurerModule() {
return configurer -> configurer.eventProcessing(processing ->
processing
// 配置全局默认错误处理器
.registerDefaultErrorHandler(conf -> new RetryingErrorHandler())
// 为特定事件处理器配置错误处理器
.registerErrorHandler("my-processor", conf -> new CustomProcessorErrorHandler())
);
}
}
自定义ErrorHandler
实现ErrorHandler接口,通过ErrorContext获取错误上下文信息,自定义处理逻辑(如忽略、重试、死信队列投递等):
public interface ErrorHandler {
void handleError(ErrorContext errorContext) throws Exception;
}
// 示例:自定义 ErrorHandler
public class CustomErrorHandler implements ErrorHandler {
@Override
public void handleError(ErrorContext errorContext) throws Exception {
Exception cause = errorContext.getCause();
EventMessage<?> event = errorContext.getEvent();
// 自定义逻辑:如判断异常类型,决定是否投递到死信队列
if (cause instanceof NonTransientException) {
// 非暂时性异常:投递到死信队列
deadLetterQueue.send(event, cause);
} else {
// 暂时性异常:重试
throw cause; // 向上传播,触发重试
}
}
}
事件处理器通用配置
介绍
除了处理程序分配和错误处理,事件处理器还支持其他组件的配置。订阅式和流处理式事件处理器的专属配置以后说,这里是通用配置
事件处理器构建器
介绍
EventProcessingConfigurer提供了大量事件处理器的可配置组件,但有时直接提供构建事件处理器的完整逻辑”会更便捷。此时可通过EventProcessorBuilder自定义构建逻辑
代码
@FunctionalInterface
interface EventProcessorBuilder {
// 参数说明:
// - name:事件处理器名称
// - configuration:Axon 全局配置(可获取 EventStore 等组件)
// - eventHandlerInvoker:持有事件处理函数的组件
EventProcessor build(String name,
Configuration configuration,
EventHandlerInvoker eventHandlerInvoker);
}
配置方式
EventProcessingConfigurer提供两种配置EventProcessorBuilder的方法:
- registerEventProcessorFactory(EventProcessorBuilder):为未配置专属构建器的事件处理器,配置默认构建工厂
- registerEventProcessor(String, EventProcessorBuilder):为指定名称的事件处理器,配置专属构建器
public class AxonConfig {
public void configureEventProcessorBuilder(EventProcessingConfigurer processingConfigurer) {
// 1. 配置默认构建器:所有未指定专属构建器的处理器使用此逻辑
processingConfigurer.registerEventProcessorFactory((name, config, invoker) -> {
// 示例:根据处理器名称,选择构建 Subscribing 或 Tracking 处理器
if (name.startsWith("subscribing-")) {
return SubscribingEventProcessor.builder()
.name(name)
.eventHandlerInvoker(invoker)
.messageSource(config.eventBus())
.build();
} else {
return TrackingEventProcessor.builder()
.name(name)
.eventHandlerInvoker(invoker)
.tokenStore(config.tokenStore())
.build();
}
});
// 2. 为 "custom-processor" 配置专属构建器
processingConfigurer.registerEventProcessor("custom-processor",
(name, config, invoker) -> new CustomEventProcessor(name, invoker, config.eventStore())
);
}
}
事件处理器拦截器
介绍
事件处理器是事件处理程序的调用者,因此也是配置消息处理程序拦截器的合适位置。由于事件处理器专门处理事件,此处的拦截器需针对EventMessage,即EventHandlerInterceptor
配置方式
EventProcessingConfigurer提供两种配置拦截器的方法:
- registerDefaultHandlerInterceptor(BiFunction<Configuration, String, MessageHandlerInterceptor<? super EventMessage<?>>>):为所有事件处理器配置默认拦截器(输入全局配置和处理器名称,返回拦截器实例)
- registerHandlerInterceptor(String, Function<Configuration, MessageHandlerInterceptor<? super EventMessage<?>>>):为指定名称的事件处理器配置专属拦截器
public class AxonConfig {
public void configureInterceptors(EventProcessingConfigurer processingConfigurer) {
// 1. 配置全局默认拦截器:为所有处理器添加关联数据拦截
processingConfigurer.registerDefaultHandlerInterceptor((config, processorName) ->
new CorrelationDataInterceptor<>(config.correlationDataProviders())
);
// 2. 为 "audit-processor" 配置专属审计拦截器
processingConfigurer.registerHandlerInterceptor("audit-processor", config ->
new AuditLoggingInterceptor() // 自定义审计拦截器,记录事件处理日志
);
}
}
消息监控
介绍
所有事件处理器实例都支持配置MessageMonitor。消息监控器用于监控Axon应用中消息的流转过程,对于事件处理器而言,它会专门监控从事件处理器流向事件处理程序的事件
配置方式
MessageMonitorFactory是Axon配置API中通用的监控器构建接口,支持更灵活的监控器创建逻辑:
@FunctionalInterface
public interface MessageMonitorFactory {
// 参数说明:
// - configuration:全局配置(可获取依赖组件)
// - componentType:组件类型(事件处理器场景下为 EventProcessor 实现类)
// - componentName:组件名称(事件处理器名称)
MessageMonitor<Message<?>> create(Configuration configuration,
Class<?> componentType,
String componentName);
}
示例:
public class AxonConfig {
public void configureMessageMonitor(EventProcessingConfigurer processingConfigurer) {
// 使用工厂为 "metrics-processor" 配置监控器
processingConfigurer.registerMessageMonitorFactory("metrics-processor",
(config, componentType, componentName) -> {
// 示例:基于 Micrometer 实现事件处理指标监控
return MicrometerMessageMonitor.builder(componentName)
.meterRegistry(config.getComponent(MeterRegistry.class))
.build();
}
);
}
}
事务管理
介绍
事件处理器负责事件处理,因此也是配置事务的合理位置。大多数场景下,默认配置已足够,本节仅介绍可调整的选项(如需自定义时参考)
TransactionManager
Axon使用TransactionManager为每个工作单元附加事务:
- Spring环境:默认使用SpringTransactionManager,底层依赖Spring的PlatformTransactionManager
- 非Spring环境:若需事务管理,需自定义TransactionManager实现,仅需实现TransactionManager#startTransaction()方法
配置方式
通过EventProcessingConfigurer的registerTransactionManager方法配置:
public class AxonConfig {
public void configureTransactionManager(EventProcessingConfigurer processingConfigurer) {
// 为 "transactional-processor" 配置自定义事务管理器
processingConfigurer.registerTransactionManager("transactional-processor",
config -> new CustomTransactionManager()
);
}
}
回滚配置
RollbackConfiguration用于决定工作单元何时需要回滚事务。默认配置为任何Throwable都会触发回滚,其他可选配置可参考工作单元
通过EventProcessingConfigurer的registerRollbackConfiguration方法配置:
public class AxonConfig {
public void configureRollback(EventProcessingConfigurer processingConfigurer) {
// 为 "custom-rollback-processor" 配置自定义回滚策略:仅 RuntimeException 触发回滚
processingConfigurer.registerRollbackConfiguration("custom-rollback-processor",
config -> RollbackConfigurationType.ANY_RUNTIME_EXCEPTION
);
}
}