Chain Starter
受众:开发者 摘要:责任链 / 规则链 Facade。Handler 显式
proceed才继续,适合风控、审批、策略分流、内容审核等“当前规则决定是否传递”的同步链路。
spring-open-starter-chain 是钩子 / 管道 / 责任链三件套里的条件决策层。它依赖 Execution Starter 的 ExecutionContext,但不依赖 Hook 或 Pipeline。
何时使用
| 场景 | 建议 |
|---|---|
| 规则自己决定是否继续传递 | 用 Chain |
| 风控、审批、内容审核、策略分流等可中断规则链 | 用 Chain |
| 不确定该用 Hook、Pipeline 还是 Chain | 先看 钩子 / 管道 / 责任链 专题 |
钩子 / 管道 / 责任链的选型、推荐场景、注解糖和组合使用见 钩子 / 管道 / 责任链 专题。
快速开始
spring:
open:
chain:
enabled: true
trace-enabled: true复制模板:Context
简单场景直接使用 DefaultExecutionContext:
DefaultExecutionContext context = new DefaultExecutionContext("trace-001");
context.put("amount", 100L);
context.put("currency", "CNY");正式业务建议定义强类型上下文,把类名和字段改成你的业务名即可:
public class OrderRiskChainContext extends DefaultExecutionContext {
private final Long userId;
private final Long amount;
public OrderRiskChainContext(String traceId, Long userId, Long amount) {
super(traceId);
this.userId = userId;
this.amount = amount;
}
public Long getUserId() {
return userId;
}
public Long getAmount() {
return amount;
}
}复制模板:注解 Rule 类
推荐用 @Chain.Rule 标注 Handler 类。Handler 只有显式调用 chain.proceed(context) 才会继续传递;少写这行就表示当前规则停止链路。
@Component
@Chain.Rule(
value = "mall.order.risk",
code = "amount-risk",
order = 10,
description = "Check high order amount risk.")
public class OrderAmountRiskRule implements ChainHandler<OrderRiskChainContext> {
@Override
public void handle(OrderRiskChainContext context, ChainInvocation<OrderRiskChainContext> chain) {
if (context.getAmount() > 10_000L) {
context.terminate("high amount requires manual review");
return;
}
chain.proceed(context);
}
}@Chain.Rule 标在 Handler 类上后,可以省掉 getChain()、getCode()、getDescription() 和 order() 样板;配置里的 chains.<name>.handlers.<code>.order 仍然可以覆盖注解排序。
复制模板:普通 Bean 方法
简单规则也可以直接写在任意 Spring Bean 方法上,不需要单独创建 Handler 类:
@Component
public class OrderRiskRules {
@Chain.Action(value = "mall.order.risk", code = "write-log", order = 10)
public void writeLog(OrderRiskChainContext context) {
log.info("risk check traceId={}", context.getTraceId());
}
@Chain.Rule(value = "mall.order.risk", code = "amount-risk", order = 20)
public void amountRisk(OrderRiskChainContext context, ChainInvocation<OrderRiskChainContext> chain) {
if (context.getAmount() > 10_000L) {
context.terminate("high amount requires manual review");
return;
}
chain.proceed(context);
}
}| 注解 | 方法签名 | 语义 |
|---|---|---|
@Chain.Rule | void method(C context, ChainInvocation<C> chain) | 当前规则自己决定是否调用 chain.proceed(context) |
@Chain.Action | void method(C context) | 只执行副作用,完成后自动调用 chain.proceed(context) |
复制模板:Service 调用
显式 Facade 调用最清楚,适合主流程里需要读取执行结果、自行决定后续动作的场景:
public class OrderService {
public void createOrder(Long userId, Long amount) {
OrderRiskChainContext context =
new OrderRiskChainContext("trace-" + userId, userId, amount);
ChainExecutionResult<OrderRiskChainContext> result =
Chain.execute("mall.order.risk", context);
if (result.isInterrupted()) {
throw new IllegalStateException(result.getMessage());
}
// Chain 只做规则传递和中断决策;创建订单、扣库存、创建支付单仍由 Service 控制。
}
}复制模板:自动触发业务方法
如果某个业务方法固定需要先跑同一条 Chain,可以直接标 @Chain.Execute。方法参数里必须有一个 ExecutionContext;Chain 完整传递或被配置禁用后才会进入目标方法,中断会抛出 ChainInterruptedException 并阻断目标方法。
@Chain.Execute 走 Spring AOP 代理;请从其他 Spring Bean 调用该方法,同一个类内部 this.createOrder(...) 自调用不会触发注解。
@Service
public class OrderService {
@Chain.Execute("mall.order.risk")
public Order createOrder(OrderRiskChainContext context) {
// 能执行到这里,表示 mall.order.risk Chain 已经完整传递。
return doCreateOrder(context.getUserId(), context.getAmount());
}
}上下文参数不在第一个位置时,用 contextArg 指定下标:
@Service
public class OrderService {
@Chain.Execute(value = "mall.order.risk", contextArg = 1)
public Order createOrder(String requestSource, OrderRiskChainContext context) {
return doCreateOrder(context.getUserId(), context.getAmount());
}
}Inline Rule
一次性规则不需要单独创建 ChainHandler 类,可直接通过 Facade 传 lambda:
DefaultExecutionContext context = new DefaultExecutionContext("trace-001")
.put("amount", 100L);
Chain.execute("mall.order.risk", context,
Chain.action("print", ctx -> log.info("chain {}", ctx.getTraceId())),
Chain.rule("amount-risk", (ctx, chain) -> {
Long amount = ctx.get("amount");
if (amount != null && amount > 10_000L) {
ctx.terminate("high amount requires manual review");
return;
}
chain.proceed(ctx);
}));| API | 说明 |
|---|---|
@Chain.Execute | 标注业务方法,方法执行前自动跑指定 Chain;中断或失败会阻断目标方法 |
@Chain.Rule | 标注 Handler 类或普通 Bean 方法,注册正式可配置规则 |
@Chain.Action | 标注普通 Bean 方法,注册自动 proceed 的动作规则 |
Chain.action(code, action) | 执行 void lambda,执行完成后自动 proceed |
Chain.rule(code, callback) | 拥有完整传递控制权,可自行决定是否调用 chain.proceed(ctx) |
.when(predicate) | 当前上下文满足条件时才执行该 inline rule |
.named(name) / .described(description) | 覆盖 trace 中展示的名称和说明 |
Inline rule 默认按传参顺序执行;如果配置了 chains.<name>.handlers.<code>.order,仍可通过配置覆盖顺序。正式、可复用、需要独立测试或配置启停的规则仍建议沉淀为 ChainHandler Bean。
Starter 入口与源码骨架
Chain starter 采用 Facade -> Manager -> Registry / Executor -> Handler 的薄分层。业务代码默认使用 Chain.execute(...);固定前置规则链可用 @Chain.Execute 自动触发;正式规则可用 @Chain.Rule / @Chain.Action 注册;简单一次性规则可通过 Chain.rule(...) / Chain.action(...) 传入 inline lambda;复杂装配、测试替换或按名称注入时使用 ChainFactory 创建 ChainFlow,再调用 flow.execute(ctx)。
| 层 | 类 | 职责 |
|---|---|---|
| Facade | Chain | 业务调用入口:Chain.execute(name, context);支持 @Chain.Execute 自动触发、@Chain.Rule / @Chain.Action 注册规则、正式 Handler 和 Chain.rule(...) / Chain.action(...) inline lambda |
| Flow | ChainFactory / ChainFlow | 可注入实例入口:chainFactory.flow(name).execute(context) |
| Manager | DefaultChainManager | 校验 Chain 名称和上下文,处理 Chain 级启停,委托 Registry / Executor,并在最终结果处通知监听器 |
| Registry | DefaultChainHandlerRegistry | 按 Chain 名绑定 Handler,应用 Handler 启停配置,并按配置 order、注解 order、ChainHandler.order()、Spring Ordered / @Order 排序 |
| Executor | DefaultChainExecutor | 同步执行 Handler;supports=false 自动跳过;不调用 proceed 或上下文终止时返回中断结果;异常默认向调用方抛出 |
| Handler | ChainHandler<C> | 单个规则;需要继续时必须调用 chain.proceed(context) |
| Invocation | ChainInvocation<C> | Handler 内部继续传递契约,只在规则实现里使用 |
| Listener | ChainExecutionListener | 承接审计、指标、执行日志和运维观测;监听器异常只记录 warning,不影响主流程 |
| Context | ExecutionContext / DefaultExecutionContext / ExecutionKey<T> | 同一条链路内共享 trace、attributes、类型安全属性和中止状态 |
关键机制顺序:
Chain.execute(name, context)调用ChainManager。DefaultChainManager校验参数和启停配置,向 Registry 取当前 Chain 的 Handler。DefaultChainHandlerRegistry完成绑定、启停过滤和排序。DefaultChainExecutor按顺序进入 Handler。supports=false记为 skipped,并自动继续后续 Handler。- Handler 调用
chain.proceed(context)才继续;不调用即返回INTERRUPTED,并写入context.terminate(...)。 context.terminate(reason)会中断当前 Chain。- Handler 异常默认向调用方抛出,Manager 会先通知监听器收到
FAILED快照。
Handler 语义
开发者通常复制上文的 @Chain.Rule Handler 类或普通 Bean 方法模板即可。需要理解 Handler 契约时,看下面这些核心方法:
| 方法 | 说明 |
|---|---|
getChain() | 精确 Chain 名,例如 mall.order.risk;使用 @Chain.Rule 时可省略实现 |
getCode() | 稳定 handler code,用于配置、trace 和运维展示;使用 @Chain.Rule(code = "...") 时可省略实现 |
getName() | 展示名称 |
getDescription() | 职责说明 |
getIdentity() | 执行身份;中断和失败来源优先展示该值 |
order() | 默认排序,未设置时等同 Spring Ordered.LOWEST_PRECEDENCE |
supports(context) | 运行期条件判断;返回 false 时跳过并继续后续 Handler |
handle(context, chain) | 当前规则主体;需要继续时必须调用 chain.proceed(context);不调用会中断链路并写入上下文中止状态 |
排序与启停
排序来源按优先级生效:
| 来源 | 示例 | 优先级 |
|---|---|---|
| 配置覆盖 | chains.mall.order.risk.handlers.amount-risk.order: 10 | 最高 |
| 注解 / Handler 默认排序 | @Chain.Rule(order = 10) / ChainHandler.order() | 中 |
| Spring 排序 | Ordered / @Order | 低 |
| 默认值 | Ordered.LOWEST_PRECEDENCE | 最低 |
配置示例:
spring:
open:
chain:
chains:
mall.order.risk:
enabled: true
handlers:
amount-risk:
enabled: true
order: 10
inventory-risk:
enabled: true
order: 20实例入口
需要按名称注入、方便测试替换或与 Hook / Pipeline 组合时,使用 ChainFactory:
@RequiredArgsConstructor
public class OrderRiskService {
private final ChainFactory chainFactory;
public void check(OrderRiskChainContext context) {
ChainFlow<OrderRiskChainContext> flow = chainFactory.flow("mall.order.risk");
flow.execute(context);
}
}执行结果与监听器
ChainExecutionResult 包含 chain、traceId、status、interruptedBy、message、exception、elapsedMillis 和 ChainHandlerTrace 列表。
| 状态 | 说明 |
|---|---|
SUCCESS | 所有可执行 Handler 都继续传递并完成 |
INTERRUPTED | Handler 未调用 proceed,或上下文被 terminate |
FAILED | Handler 抛出异常;默认仍会向调用方抛出原始异常 |
DISABLED | Chain 被配置禁用 |
监听器示例:
@Component
public class ChainMetricsListener implements ChainExecutionListener {
@Override
public void onExecution(ChainExecutionResult<?> result) {
meterRegistry.counter("spring_open_chain_total",
"chain", result.getChain(),
"status", result.getStatus().name()).increment();
}
}阶段监听器全部为默认空方法,按需覆盖。监听器自身异常会被隔离并记录 warning,不影响 Chain 主流程。
边界
Chain 是进程内、同步、可中断的规则链。Handler 必须显式调用 chain.proceed(context) 才继续;不适合固定步骤流水线、异步任务、事件广播、持久化工作流或分布式事务。
命名约定
- Chain 名使用点分层:
mall.order.risk/forum.topic.publish.risk - Handler code 使用短横线:
amount-risk/content-safety/manual-review - Handler 类名以规则动作结尾:
OrderAmountRiskHandler/ContentSafetyChainHandler - Handler 内只做单条规则;复杂固定步骤拆到 Pipeline
验证命令
./mvnw -pl spring-open-starters/spring-open-starter-chain -am test -DskipITs -DskipFrontend
git diff --check