Spring Boot + 执行管道:让业务流程清晰可控

不知道你有没有遇到过这种情况:一个订单创建的接口,刚上线的时候只有几十行代码,逻辑也很简单。但随着业务迭代,三个月后变成了这样:

java 复制代码
public void createOrder(OrderRequest request) {
    // 参数校验
    if (request.getUserId() == null) {
        throw new IllegalArgumentException("用户ID不能为空");
    }
    // ... 省略 20 行校验代码

    // 权限校验
    if (!checkPermission(request.getUserId())) {
        throw new ForbiddenException("无权限");
    }

    // 业务校验
    if (getOrderCount(request.getUserId()) >= 10) {
        throw new BusinessException("订单数量超限");
    }
    // ... 省略 15 行业务校验

    // 创建订单
    Order order = buildOrder(request);
    orderMapper.insert(order);

    // 写操作日志
    try {
        operateLogService.log(order.getId(), "CREATE");
    } catch (Exception e) {
        // 日志失败不影响主流程
        log.error("写日志失败", e);
    }

    // 发送通知
    try {
        notificationService.send(order);
    } catch (Exception e) {
        log.error("发送通知失败", e);
    }

    // 异步风控检查
    asyncRiskCheckService.check(order);
}

几百行代码,iftry-catch 到处都是。更可怕的是:

  • 日志写失败要不要抛异常?
  • 通知发送失败要不要回滚订单?
  • 新增一个"优惠券核销"逻辑加在哪里?
  • 测试环境想关闭风控检查怎么搞?

问题出在哪?

仔细看看上面的代码,其实它想做的事情可以拆成这么几个步骤:

markdown 复制代码
1. 校验参数
2. 校验权限
3. 校验业务规则
4. 创建订单(核心)
5. 记录日志
6. 发送通知
7. 风控检查

这些步骤有几个特点:

有的必须成功 :比如参数校验、订单创建 有的可以失败 :比如日志记录、通知发送 有的同步执行 :比如各种校验 有的异步执行 :比如风控检查 有的可能要关闭:比如测试环境不需要风控

但现在的写法,所有这些逻辑都混在一起,顺序、失败策略、是否可选 ,全靠代码里的 iftry-catch 来控制,能不乱吗?

有没有更好的写法?

如果把这些步骤抽象成一个个"节点",然后用一个"管道"把它们串起来,会怎样?

java 复制代码
public interface PipelineNode<T> {
    void execute(PipelineContext<T> context);
}

每个节点只做一件事,不关心前后是谁,不关心自己是第几个执行。

然后定义一个管道来执行这些节点:

java 复制代码
public class ExecutionPipeline<T> {

    private final List<PipelineNode<T>> nodes;

    public void execute(T data) {
        PipelineContext<T> ctx = new PipelineContext<>(data);
        for (PipelineNode<T> node : nodes) {
            if (ctx.isInterrupted()) {
                break;
            }
            node.execute(ctx);
        }
    }
}

现在再看看订单创建怎么写:

java 复制代码
Pipeline pipeline = Pipeline.builder()
    .add(new ParamValidateNode())       // 参数校验
    .add(new PermissionCheckNode())     // 权限校验
    .add(new BusinessValidateNode())    // 业务校验
    .add(new CreateOrderNode())         // 创建订单
    .add(new OperateLogNode())          // 写日志
    .add(new NotificationNode())        // 发通知
    .add(new AsyncRiskCheckNode())      // 风控检查
    .build();

pipeline.execute(orderRequest);

主流程只剩下 10 行代码,每个节点的逻辑独立封装。

这样做有什么好处?

1. 节点可插拔

想加一个"优惠券核销"逻辑?加个节点就行了:

java 复制代码
.add(new CouponDeductNode())

想关闭某个功能?去掉对应节点,或者通过配置控制:

java 复制代码
.add(env.isProd() ? new RiskCheckNode() : new SkipNode())

2. 失败策略清晰

PipelineContext 里定义好失败策略:

java 复制代码
public enum FailureStrategy {
    CONTINUE,    // 失败后继续执行
    STOP,        // 失败后中断管道
    ROLLBACK     // 失败后回滚(需要配合事务)
}

日志节点的失败策略是 CONTINUE,订单创建节点的失败策略是 STOP,一目了然。

3. 节点可复用

参数校验节点、权限校验节点、日志节点,都可以在其他业务里复用。

4. 执行顺序可控

节点的执行顺序由管道定义,不用在每个节点里写代码控制。

执行管道:一种工程化方案

上面介绍的方案既执行管道(Execution Pipeline)

它不是某个框架,而是一种工程设计模式:把一次业务执行拆成多个可插拔、有顺序、可控制的执行阶段,用统一机制串起来。

它跟 AOP、责任链有什么区别?

你可能想到了 AOP、责任链、工作流引擎这些概念,它们确实有相似之处:

方案 适用场景 局限性
AOP 通用的横切逻辑(日志、事务) 顺序不直观,难以做流程控制
Filter/Interceptor HTTP 请求级别的处理 只能用于请求处理
@Async 异步执行 只解决并发,不解决流程编排
责任链 动态组合处理逻辑 缺少工程化封装
工作流引擎 复杂的业务流程 太重,单机应用用不上

执行管道可以说是工程化的责任链模式,专门用于解决单机应用内的业务流程编排问题。

什么时候用?

不是所有场景都需要执行管道。如果你的代码:

  • 步骤固定、逻辑简单,直接写就行
  • 流程复杂但不会变化,也没必要过度设计

但当出现这些信号时,可以考虑:

  • 一个方法过长,不利于阅读维护
  • 有多个"可选"或"异步"的执行步骤
  • 经常需要插入新的执行逻辑
  • 不同环境需要不同的执行流程

执行管道特别适合单机应用内的复杂业务流程,比如订单创建、用户注册、数据导入等场景。

写在最后

好的代码不是一次写成的,而是在不断重构中进化出来的。

如果你的项目里已经有了那种"巨无霸"方法,不用急着全部推倒重来。先试着把其中一个独立的逻辑抽成节点,然后逐步迁移,代码会越来越清晰。

ruby 复制代码
https://github.com/yuboon/java-examples/tree/master/springboot-pipeline
相关推荐
while(1){yan}10 小时前
Spring事务
java·数据库·spring boot·后端·java-ee·mybatis
*.✧屠苏隐遥(ノ◕ヮ◕)ノ*.✧11 小时前
《苍穹外卖》- day01 开发环境搭建
spring boot·后端·spring·maven·intellij-idea·mybatis
_OP_CHEN12 小时前
【Linux系统编程】(二十)揭秘 Linux 文件描述符:从底层原理到实战应用,一篇吃透 fd 本质!
linux·后端·操作系统·c/c++·重定向·文件描述符·linux文件
老神在在00112 小时前
Token身份验证完整流程
java·前端·后端·学习·java-ee
源码获取_wx:Fegn089513 小时前
计算机毕业设计|基于springboot + vue景区管理系统(源码+数据库+文档)
java·vue.js·spring boot·后端·课程设计
星辰徐哥13 小时前
Rust函数与流程控制——构建逻辑清晰的系统级程序
开发语言·后端·rust
源代码•宸14 小时前
Leetcode—404. 左叶子之和【简单】
经验分享·后端·算法·leetcode·职场和发展·golang·dfs
你这个代码我看不懂15 小时前
Spring Boot拦截Http请求设置请求头
spring boot·后端·http
TechPioneer_lp15 小时前
小红书后端实习一面|1小时高强度技术追问实录
java·后端·面试·个人开发