CommandHandlerExtension
CommandHandlerExtension 是将自定义 Java 逻辑注入 AuraBoot Command Pipeline 的主要扩展点。平台中的所有写操作 —— 创建、更新、删除、状态流转、自定义命令 —— 都经过 CommandExecutor,由其在 16 个 pipeline 阶段中进行分发(Load → Command Authorization → Schema Validate → Idempotency → Entitlement → SoD Check → State Check → Assert → Pre-Actions → Pre-Invariant → Auto Set → Field Map → Computed Fields → Handler → Post-Execution → Completion)。一个 CommandHandlerExtension 绑定到特定的 command type,并在 Handler 阶段 贡献逻辑。
概念
一个 handler 的作用域限定到单个 command type(通过 getCommandType() 返回的 namespace:command-name 匹配)。Pipeline 在 Handler 阶段调用 execute(CommandContext),由其返回结果对象(可为 null)。
平台对每个 command 默认只运行一个 primary handler —— 即 chainsAfterPrimary() 返回 false 的 handler 中 getPriority() 最高的那个(priority 越大越优先)。chainsAfterPrimary() 返回 true 的 handler 是 secondary:它不参与 primary 选择,而是在 primary 之后、同一个命令事务内运行,从而让下游插件为另一个插件已拥有的 command 追加行为。多个 secondary 按 getPriority() 降序执行。
execute 内的所有写副作用都参与由 CommandExecutor 持有的外层命令事务边界。不要吞掉异常 —— 详见常见陷阱。
接口签名
package com.auraboot.framework.plugin.extension;
import org.pf4j.ExtensionPoint;
import java.util.Set;
public interface CommandHandlerExtension extends ExtensionPoint {
/** 当前 handler 处理的 command type。格式 "namespace:command-name",例如 "billing:generate-invoice"。 */
String getCommandType();
/** 该 handler 能处理的全部 command type(import 期校验需要有限列表)。默认返回 {getCommandType()}。 */
default Set<String> getSupportedCommandTypes() {
String commandType = getCommandType();
return commandType == null ? Set.of() : Set.of(commandType);
}
/** 执行命令。返回结果对象(可为 null)。 */
Object execute(CommandContext context) throws Exception;
/** 是否支持给定的 command type,默认按 getCommandType() 精确匹配。 */
default boolean supports(String commandType) {
return getCommandType().equals(commandType);
}
/** handler 优先级,越高越先执行,默认 0。 */
default int getPriority() {
return 0;
}
/** 是否作为 secondary 在 primary 之后链式执行,默认 false(参与 primary 选择)。 */
default boolean chainsAfterPrimary() {
return false;
}
/** 在 dryRun 下是否安全执行,默认 false(dry-run 时跳过该 handler)。 */
default boolean supportsDryRun() {
return false;
}
}CommandContext 是一个 record,字段通过 record accessor 访问:ctx.tenantId()、ctx.commandType()、ctx.modelCode()、ctx.recordId()、ctx.payload()(Map<String, Object>)、ctx.settings()(Map<String, Object>)、ctx.pluginId()、ctx.namespace()、ctx.dryRun()。数据访问通过 ctx.dataAccessor()(或 ctx.settings().get("__dataAccessor"))拿到平台注入的 DataAccessor;还有 ctx.aiProviderAccessor()、ctx.fileAccessor()、ctx.asyncTaskAccessor() 等桥接器。
实现示例
下面的 handler 处理 crm:create_lead 命令,读取记录、计算分数并写回:
package com.acme.crm.handler;
import com.auraboot.framework.plugin.extension.CommandHandlerExtension;
import com.auraboot.framework.plugin.extension.DataAccessor;
import org.pf4j.Extension;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Map;
@Extension
public class LeadScoringHandler implements CommandHandlerExtension {
@Autowired private LeadScoringService scoring;
@Override
public String getCommandType() {
return "crm:create_lead";
}
@Override
public Object execute(CommandContext ctx) throws Exception {
DataAccessor db = ctx.dataAccessor();
Map<String, Object> payload = ctx.payload();
Integer score = scoring.score(payload);
payload.put("lead_score", score);
return Map.of("recordId", ctx.recordId(), "score", score);
}
}注册
AuraBoot 使用基于 Spring 的 PF4J 风格扩展发现(@Extension)。两个步骤:
- 在类上使用
@Extension(来自org.pf4j)注解。 META-INF/extensions.idx列出扩展类,由org.pf4j:pf4j注解处理器(annotationProcessor 'org.pf4j:pf4j')在编译期自动生成。
在 plugin.json 中,确保扩展所在的包包含于插件类扫描范围。handler 与 command type 的绑定基于接口 + 注解 + getCommandType()(运行时再经 supports(...) 分发),而非通过 commands.json 或 bindingRules.json 中的 handler 引用。
如需调用其他服务,使用标准的 Spring @Autowired。Plugin classloader 会通过宿主的 ApplicationContext 解析平台 bean。
常见陷阱
- 吞掉异常。 在 pipeline 持有活动事务的情况下,
execute中的catch (Exception e) { log.warn(...) }会导致rollback-only标记但不抛出错误 —— 见红线 §8。重新抛出,或在带外副作用上使用@Transactional(NOT_SUPPORTED)。 commands.json中的 inlinebindingRules。 不会被 importer 加载 —— 见红线 §6。Handler 与 command type 的绑定完全通过getCommandType()完成。- dry-run 下的外部副作用。 事务回滚只撤销 JDBC 写;HTTP/邮件/MQ/对象存储等外部副作用会逃出回滚边界。
supportsDryRun()默认false,dry-run 时该 handler 直接被跳过;返回true前必须自查ctx.dryRun()并短路所有外部调用。 - 在
execute中执行长耗时 I/O。 你正占用请求线程和事务。请发布到 outbox 或经ctx.asyncTaskAccessor()异步处理。
相关
- Commands —— pipeline 阶段参考
- CommandExecutor service
- ValidatorExtension —— 字段级 Validator
- EventListenerExtension —— 基于 outbox 的异步反应