钩子 / 管道 / 责任链 专题
受众:开发者 摘要:Hook、Pipeline、Chain 的快速选型、复制即用语法、推荐场景和组合使用指南。
钩子 / 管道 / 责任链都是进程内同步执行模型,依赖同一个 Execution Context 底座。开发者可以按业务需要选择注解语法糖、Facade 手动调用或实例 Bean 入口;推荐优先使用 Facade 和注解,复杂装配、测试替换或按名称注入时再使用实例 Bean。
一句话区分
| 能力 | 一句话 | 常见场景 |
|---|---|---|
| Hook | 包住一个业务方法,在方法前后做同步增强 | 日志、审计、指标、鉴权、幂等、轻量风控 |
| Pipeline | 一个业务动作内部天然分成多个固定步骤 | 发布前检查、下单前校验、内容预处理、AI 任务步骤 |
| Chain | 当前规则自己决定是否继续传给下一个规则 | 风控、审批、策略分流、内容审核、人工复核前置判断 |
快速选型
| 你要做什么 | 推荐 |
|---|---|
| 方法入口统一记录审计、指标、Trace 或鉴权 | Hook |
| 方法执行后统一记录结果、补充返回值或失败留痕 | Hook |
| 发布、下单、结算、AI 任务等流程有固定步骤 | Pipeline |
某一步可以 next、skip、stop,并希望看到步骤 trace | Pipeline |
| 每条规则都可能决定“不继续往后传” | Chain |
规则类似中间件,需要显式 proceed | Chain |
| 业务完成后通知其他模块,不要求同步阻断 | Event |
| 长耗时、延迟、失败重试或批处理 | Queue / Scheduler |
| 多个供应商、渠道或策略实现中选择一个 | Provider 扩展模型 |
| 人工审批、跨天状态流转、跨系统最终一致性 | 业务状态机、工单、Outbox 或账务补偿机制 |
推荐用法
| 能力 | 注解语法糖 | 手动 Facade | 实例 Bean |
|---|---|---|---|
| Hook | @Hook.Execute("point") | Hook.execute(point, parameter, target) | HookExecutor.execute(...) |
| Pipeline | @Pipeline.Execute("name") | Pipeline.execute(name, context) | PipelineFactory.flow(name).execute(context) |
| Pipeline 步骤 | @Pipeline.Step / @Pipeline.Action | Pipeline.step(...) / Pipeline.action(...) | PipelineHandler Bean |
| Chain | @Chain.Execute("name") | Chain.execute(name, context) | ChainFactory.flow(name).execute(context) |
| Chain 规则 | @Chain.Rule / @Chain.Action | Chain.rule(...) / Chain.action(...) | ChainHandler Bean |
注解方式走 Spring AOP 代理,请从其他 Spring Bean 调用目标方法;同一个类内部 this.xxx(...) 自调用不会触发注解。需要读取执行结果或自己决定中断处理时,用手动 Facade 调用更清楚。
复制模板:共享 Context
Pipeline 要求 PipelineContext,Chain 接受通用 ExecutionContext。如果一个业务流程里会组合 Pipeline 和 Chain,可以直接继承 DefaultPipelineContext:
public class OrderCreateContext extends DefaultPipelineContext {
private final Long userId;
private final Long amount;
public OrderCreateContext(String traceId, Long userId, Long amount) {
super(traceId);
this.userId = userId;
this.amount = amount;
}
public Long getUserId() {
return userId;
}
public Long getAmount() {
return amount;
}
}复制模板:Hook
固定方法入口用 @Hook.Execute:
@Service
public class OrderService {
@Hook.Execute("mall.order.create")
public Order createOrder(CreateOrderCommand command) {
return doCreate(command);
}
}不方便走注解时,用手动 Facade:
Order order = Hook.execute(
"mall.order.create",
command,
() -> doCreate(command));注册外层扩展:
@Component
public class OrderCreateAuditHookHandler implements HookHandler<CreateOrderCommand, Order> {
@Override
public String getPoint() {
return "mall.order.create";
}
@Override
public String getCode() {
return "audit";
}
@Override
public void before(HookContext<CreateOrderCommand, Order> context) {
auditService.recordStart(context.getParameter().userId());
}
@Override
public Order after(HookContext<CreateOrderCommand, Order> context, Order result) {
auditService.recordSuccess(result.id());
return result;
}
}复制模板:Pipeline
固定前置步骤链可以直接标业务方法:
@Service
public class OrderService {
@Pipeline.Execute("mall.order.create")
public Order createOrder(OrderCreateContext context) {
return doCreateOrder(context.getUserId(), context.getAmount());
}
}需要读取结果时用手动 Facade:
OrderCreateContext context =
new OrderCreateContext("trace-" + command.userId(), command.userId(), command.amount());
PipelineExecutionResult<OrderCreateContext> result =
Pipeline.execute("mall.order.create", context);
if (result.isInterrupted()) {
throw new IllegalStateException(result.getMessage());
}注册固定步骤:
@Component
@Pipeline.Step(value = "mall.order.create", code = "amount-check", order = 10)
public class OrderAmountCheckStep implements PipelineHandler<OrderCreateContext> {
@Override
public PipelineStepResult handle(OrderCreateContext context) {
if (context.getAmount() <= 0) {
return PipelineStepResult.stop("amount is invalid");
}
return PipelineStepResult.next();
}
}一次性步骤可直接传 lambda:
Pipeline.execute("mall.order.create", context,
Pipeline.action("print", ctx -> log.info("pipeline {}", ctx.getTraceId())),
Pipeline.step("amount-check", ctx -> {
if (ctx.getAmount() <= 0) {
return PipelineStepResult.stop("amount is invalid");
}
return PipelineStepResult.next();
}));复制模板:Chain
固定前置规则链可以直接标业务方法:
@Service
public class OrderService {
@Chain.Execute("mall.order.risk")
public Order createOrder(OrderCreateContext context) {
return doCreateOrder(context.getUserId(), context.getAmount());
}
}需要读取结果时用手动 Facade:
OrderCreateContext context =
new OrderCreateContext("trace-" + command.userId(), command.userId(), command.amount());
ChainExecutionResult<OrderCreateContext> result =
Chain.execute("mall.order.risk", context);
if (result.isInterrupted()) {
throw new IllegalStateException(result.getMessage());
}注册规则:
@Component
@Chain.Rule(value = "mall.order.risk", code = "amount-risk", order = 10)
public class OrderAmountRiskRule implements ChainHandler<OrderCreateContext> {
@Override
public void handle(OrderCreateContext context, ChainInvocation<OrderCreateContext> chain) {
if (context.getAmount() > 10_000L) {
context.terminate("high amount requires manual review");
return;
}
chain.proceed(context);
}
}一次性规则可直接传 lambda:
Chain.execute("mall.order.risk", context,
Chain.action("print", ctx -> log.info("chain {}", ctx.getTraceId())),
Chain.rule("amount-risk", (ctx, chain) -> {
if (ctx.getAmount() > 10_000L) {
ctx.terminate("high amount requires manual review");
return;
}
chain.proceed(ctx);
}));组合使用
组合链路建议用一个共享上下文承接 trace、临时属性和中止状态。Hook 包住业务方法;Pipeline 做固定步骤;Chain 做可中断规则判断;最终落库、扣费、外部副作用仍由 Service 明确控制。
@Service
public class OrderService {
@Hook.Execute("mall.order.create")
public Order createOrder(CreateOrderCommand command) {
OrderCreateContext context = new OrderCreateContext(
"order-" + command.userId(),
command.userId(),
command.amount());
PipelineExecutionResult<OrderCreateContext> prepare =
Pipeline.execute("mall.order.prepare", context);
if (prepare.isInterrupted()) {
throw new IllegalStateException(prepare.getMessage());
}
ChainExecutionResult<OrderCreateContext> risk =
Chain.execute("mall.order.risk", context);
if (risk.isInterrupted()) {
throw new IllegalStateException(risk.getMessage());
}
return doCreateOrder(command);
}
}组合时遵守三条规则:
| 规则 | 说明 |
|---|---|
| Service 控制最终状态 | Pipeline / Chain 只做前置编排和同步判断,不替代领域 Service |
| 中止状态统一 | PipelineStepResult.stop(...) 和 context.terminate(...) 都会反映到 context.isTerminated() |
| 不把异步和持久化流程塞进来 | 需要重试、延迟、跨进程恢复时转向 Queue、Scheduler、Event、Outbox 或业务状态机 |
生产试点:Forum、CMS 与 Hook 单点
Forum 主题发布和回复发布已经接入 Hook / Pipeline / Chain 组合入口:
| 动作 | Hook point | Pipeline | Chain |
|---|---|---|---|
| 主题发布 | forum.topic.publish | forum.topic.publish.precheck | forum.topic.publish.rules |
| 回复发布 | forum.reply.publish | forum.reply.publish.precheck | forum.reply.publish.rules |
| 主题举报 | forum.topic.report | forum.topic.report.precheck | forum.topic.report.rules |
| 回复举报 | forum.reply.report | forum.reply.report.precheck | forum.reply.report.rules |
| 举报状态处理 | forum.report.status.update | forum.report.status.update.precheck | forum.report.status.update.rules |
| CMS 提交审核 | cms.content.review.submit | cms.content.review.submit.precheck | cms.content.review.submit.rules |
| CMS 完成审核 | cms.content.review.finish | cms.content.review.finish.precheck | cms.content.review.finish.rules |
Forum 发布入口内置 forum-topic-rule-precheck / forum-reply-rule-precheck Pipeline Step 和 forum-topic-rule-check / forum-reply-rule-check Chain Rule,复用 spring.open.forum.rules 的标题 / 正文长度和禁用词校验;应用可通过 starter 配置启停或排序覆盖,再叠加自定义 Handler。
业务 Handler / Step / Rule 可读取 ForumPublishExecutionContext 中的作者、板块、主题、父回复和原始命令,也可读取 ForumReportExecutionContext 中的举报人、操作人、举报对象、举报 ID、状态流转和原始命令。Forum Service 仍负责事务、落库、内容安全、计数、处理记录、Outbox 和事件发布;Pipeline 或 Chain 返回中断时只是在落库或状态流转前阻断,不接管最终状态。
Forum 举报治理会把 Pipeline / Chain 执行摘要合并写入举报单和处理记录的 extra_json.executionTrace,Admin 举报详情和处理记录详情可直接查看并复制 traceId、执行名称、状态、中断来源和耗时。该落点是首个业务样板,不要求 Hook / Pipeline / Chain starter 相互依赖,也不把 trace 持久化表强塞进底座 starter。
CMS 业务 Handler / Step / Rule 可读取 CmsReviewExecutionContext 中的内容 ID、内容类型、客户端、编码、创建人、操作人、审核动作、操作动作和审核备注。CMS Service 仍负责审批工作流执行、审核状态字段、操作日志和通知事件;Pipeline 或 Chain 返回中断时只是在审核工作流执行前阻断,不替代最终审核状态。
Conversation 消息发送已接入 Hook 单点 conversation.message.send,业务 Handler 可读取 ConversationMessageSendExecutionContext 中的会话 ID / 类型、发送人、客户端消息 ID、内容类型、附件数量和原始发送命令。Conversation Service 仍负责内容安全、消息落库、附件绑定、未读 / 已读状态、WebSocket 推送和站内通知事件;Hook 只适合审计、埋点、轻量同步或可解释阻断。
相关文档
| 文档 | 内容 |
|---|---|
| Hook Starter | Hook Handler、Context、排序、启停、失败策略和监听器 |
| Pipeline Starter | Pipeline Step、Action、inline step、Saga 补偿、阶段监听和配置项 |
| Chain Starter | Chain Rule、Action、inline rule、显式 proceed 和中断语义 |
| Execution Starter | 共享 ExecutionContext、ExecutionKey<T>、trace 和中止状态 |