跳到内容

钩子 / 管道 / 责任链 专题

受众:开发者 摘要:Hook、Pipeline、Chain 的快速选型、复制即用语法、推荐场景和组合使用指南。

钩子 / 管道 / 责任链都是进程内同步执行模型,依赖同一个 Execution Context 底座。开发者可以按业务需要选择注解语法糖、Facade 手动调用或实例 Bean 入口;推荐优先使用 Facade 和注解,复杂装配、测试替换或按名称注入时再使用实例 Bean。

一句话区分

能力一句话常见场景
Hook包住一个业务方法,在方法前后做同步增强日志、审计、指标、鉴权、幂等、轻量风控
Pipeline一个业务动作内部天然分成多个固定步骤发布前检查、下单前校验、内容预处理、AI 任务步骤
Chain当前规则自己决定是否继续传给下一个规则风控、审批、策略分流、内容审核、人工复核前置判断

快速选型

你要做什么推荐
方法入口统一记录审计、指标、Trace 或鉴权Hook
方法执行后统一记录结果、补充返回值或失败留痕Hook
发布、下单、结算、AI 任务等流程有固定步骤Pipeline
某一步可以 nextskipstop,并希望看到步骤 tracePipeline
每条规则都可能决定“不继续往后传”Chain
规则类似中间件,需要显式 proceedChain
业务完成后通知其他模块,不要求同步阻断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.ActionPipeline.step(...) / Pipeline.action(...)PipelineHandler Bean
Chain@Chain.Execute("name")Chain.execute(name, context)ChainFactory.flow(name).execute(context)
Chain 规则@Chain.Rule / @Chain.ActionChain.rule(...) / Chain.action(...)ChainHandler Bean

注解方式走 Spring AOP 代理,请从其他 Spring Bean 调用目标方法;同一个类内部 this.xxx(...) 自调用不会触发注解。需要读取执行结果或自己决定中断处理时,用手动 Facade 调用更清楚。

复制模板:共享 Context

Pipeline 要求 PipelineContext,Chain 接受通用 ExecutionContext。如果一个业务流程里会组合 Pipeline 和 Chain,可以直接继承 DefaultPipelineContext

java
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

java
@Service
public class OrderService {

    @Hook.Execute("mall.order.create")
    public Order createOrder(CreateOrderCommand command) {
        return doCreate(command);
    }
}

不方便走注解时,用手动 Facade:

java
Order order = Hook.execute(
        "mall.order.create",
        command,
        () -> doCreate(command));

注册外层扩展:

java
@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

固定前置步骤链可以直接标业务方法:

java
@Service
public class OrderService {

    @Pipeline.Execute("mall.order.create")
    public Order createOrder(OrderCreateContext context) {
        return doCreateOrder(context.getUserId(), context.getAmount());
    }
}

需要读取结果时用手动 Facade:

java
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());
}

注册固定步骤:

java
@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:

java
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

固定前置规则链可以直接标业务方法:

java
@Service
public class OrderService {

    @Chain.Execute("mall.order.risk")
    public Order createOrder(OrderCreateContext context) {
        return doCreateOrder(context.getUserId(), context.getAmount());
    }
}

需要读取结果时用手动 Facade:

java
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());
}

注册规则:

java
@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:

java
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 明确控制。

java
@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 pointPipelineChain
主题发布forum.topic.publishforum.topic.publish.precheckforum.topic.publish.rules
回复发布forum.reply.publishforum.reply.publish.precheckforum.reply.publish.rules
主题举报forum.topic.reportforum.topic.report.precheckforum.topic.report.rules
回复举报forum.reply.reportforum.reply.report.precheckforum.reply.report.rules
举报状态处理forum.report.status.updateforum.report.status.update.precheckforum.report.status.update.rules
CMS 提交审核cms.content.review.submitcms.content.review.submit.precheckcms.content.review.submit.rules
CMS 完成审核cms.content.review.finishcms.content.review.finish.precheckcms.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 StarterHook Handler、Context、排序、启停、失败策略和监听器
Pipeline StarterPipeline Step、Action、inline step、Saga 补偿、阶段监听和配置项
Chain StarterChain Rule、Action、inline rule、显式 proceed 和中断语义
Execution Starter共享 ExecutionContextExecutionKey<T>、trace 和中止状态

Released under the Apache License 2.0.