责任链模式实现流程动态编排

如何使用责任链设计模式 + 事务 + 回滚 + 异步 + 监控 + 可中断等实现业务流程动态编排

适用场景:

  • 电商订单流程(校验→锁库存→扣减→支付→物流)
  • 审批流程(申请人→部门经理→HR→财务)
  • 风控/审核流程
  • 可动态调整步骤顺序、增删节点

一、核心思想

  1. 每个业务步骤 = 一个独立处理器(Handler)
  2. 处理器之间形成链式调用
  3. 流程顺序不写死在代码,可配置(数据库/JSON/YAML)
  4. 支持中断、跳过、异常回滚
  5. 可热插拔:新增步骤只需新增类,无需修改主流程

示例整体结构

  1. FlowContext ------ 流程上下文(携带数据、状态、标记)
  2. FlowHandler ------ 标准处理器接口(handle + rollback + getName)
  3. AbstractFlowHandler ------ 抽象链(自动管理 next、统一异常、统一监控)
  4. FlowEngine ------ 流程引擎(动态构建责任链)
  5. 业务处理器 ------ 校验/锁库存/支付/发货(可插拔)
  6. 事务 + 回滚 ------ 某一步失败,前面步骤自动回滚
  7. 异步 + 监控 ------ 计时、日志、告警

1. 流程上下文(贯穿全链)

java 复制代码
@Data
public class FlowContext {

    // 业务标识
    private String orderId;
    private Long userId;

    // 流程控制
    private boolean breakFlow = false;
    private String errorMsg;

    // 事务/回滚使用:记录已执行节点
    private List<String> executedHandlers = new ArrayList<>();

    // 扩展数据
    private Map<String, Object> data = new HashMap<>();
}

2. 标准处理器接口(含回滚)

java 复制代码
public interface FlowHandler {

    /**
     * 执行节点
     */
    void handle(FlowContext context);

    /**
     * 回滚节点(事务失败时)
     */
    void rollback(FlowContext context);

    /**
     * 节点唯一名称
     */
    String getName();
}

3. 抽象链实现(核心:异常、监控、next、中断)

java 复制代码
public abstract class AbstractFlowHandler implements FlowHandler {

    protected FlowHandler next;

    public void setNext(FlowHandler next) {
        this.next = next;
    }

    @Override
    public final void handle(FlowContext context) {
        if (context.isBreakFlow()) {
            fireNext(context);
            return;
        }

        String handlerName = getName();
        Stopwatch stopwatch = Stopwatch.createStarted();

        try {
            // 执行业务逻辑
            doHandle(context);

            // 记录已执行,用于回滚
            context.getExecutedHandlers().add(handlerName);

        } catch (Exception e) {
            log.error("节点[{}]执行异常: {}", handlerName, e.getMessage());
            context.setBreakFlow(true);
            context.setErrorMsg(e.getMessage());
        } finally {
            log.info("节点[{}] 耗时: {}ms", handlerName, stopwatch.elapsed(TimeUnit.MILLISECONDS));
        }

        // 执行下一个
        fireNext(context);
    }

    /**
     * 子类实现真正业务
     */
    protected abstract void doHandle(FlowContext context);

    /**
     * 驱动下一个节点
     */
    protected void fireNext(FlowContext context) {
        if (next != null && !context.isBreakFlow()) {
            next.handle(context);
        }
    }
}

4. 业务节点示例(3 个可插拔节点)

4.1 订单校验

java 复制代码
@Component
public class OrderCheckHandler extends AbstractFlowHandler {

    @Override
    public String getName() {
        return "orderCheck";
    }

    @Override
    protected void doHandle(FlowContext context) {
        log.info("执行订单校验 orderId: {}", context.getOrderId());
        // 模拟失败
        // if (true) throw new RuntimeException("订单参数非法");
    }

    @Override
    public void rollback(FlowContext context) {
        log.info("回滚订单校验 orderId: {}", context.getOrderId());
    }
}

4.2 锁定库存

java 复制代码
@Component
public class StockLockHandler extends AbstractFlowHandler {

    @Override
    public String getName() {
        return "stockLock";
    }

    @Override
    protected void doHandle(FlowContext context) {
        log.info("执行锁定库存 orderId: {}", context.getOrderId());
    }

    @Override
    public void rollback(FlowContext context) {
        log.info("解锁库存 orderId: {}", context.getOrderId());
    }
}

4.3 支付扣款

java 复制代码
@Component
public class PayHandler extends AbstractFlowHandler {

    @Override
    public String getName() {
        return "pay";
    }

    @Override
    protected void doHandle(FlowContext context) {
        log.info("执行支付扣款 orderId: {}", context.getOrderId());
    }

    @Override
    public void rollback(FlowContext context) {
        log.info("支付退款 orderId: {}", context.getOrderId());
    }
}

5. 流程引擎(动态编排核心)

java 复制代码
@Component
public class FlowEngine {

    @Autowired
    private Map<String, FlowHandler> handlerMap;

    /**
     * 根据步骤列表构建责任链
     */
    public FlowHandler buildChain(List<String> handlerNames) {
        AbstractFlowHandler head = null;
        AbstractFlowHandler prev = null;

        for (String name : handlerNames) {
            FlowHandler handler = handlerMap.get(name);
            if (handler == null) {
                throw new RuntimeException("节点不存在: " + name);
            }

            AbstractFlowHandler current = (AbstractFlowHandler) handler;
            if (head == null) {
                head = current;
            } else {
                prev.setNext(current);
            }
            prev = current;
        }
        return head;
    }

    /**
     * 统一回滚已执行节点
     */
    public void rollback(FlowContext context) {
        List<String> executed = context.getExecutedHandlers();
        Collections.reverse(executed);

        for (String name : executed) {
            FlowHandler handler = handlerMap.get(name);
            if (handler != null) {
                try {
                    handler.rollback(context);
                } catch (Exception e) {
                    log.error("回滚节点[{}]异常", name, e);
                }
            }
        }
    }
}

6. 统一执行入口(带事务 + 异步 + 回滚)

java 复制代码
@Service
public class FlowService {

    @Autowired
    private FlowEngine flowEngine;

    @Autowired
    private ThreadPoolTaskExecutor asyncExecutor;

    /**
     * 同步执行流程
     */
    @Transactional(rollbackFor = Exception.class)
    public void executeFlow(List<String> handlerNames, FlowContext context) {
        FlowHandler chain = flowEngine.buildChain(handlerNames);
        chain.handle(context);

        // 失败自动回滚
        if (context.isBreakFlow()) {
            flowEngine.rollback(context);
            throw new RuntimeException(context.getErrorMsg());
        }
    }

    /**
     * 异步执行(生产推荐)
     */
    public CompletableFuture<Void> executeAsync(List<String> handlerNames, FlowContext context) {
        return CompletableFuture.runAsync(() -> {
            executeFlow(handlerNames, context);
        }, asyncExecutor);
    }
}

7. 控制器测试(动态编排任意流程)

java 复制代码
@RestController
@RequestMapping("/flow")
public class FlowController {

    @Autowired
    private FlowService flowService;

    @GetMapping("/start")
    public String start() {
        // 1. 构建上下文
        FlowContext context = new FlowContext();
        context.setOrderId("ORDER_" + System.currentTimeMillis());
        context.setUserId(1001L);

        // 2. 动态编排流程(可来自数据库)
        List<String> flow = Arrays.asList(
            "orderCheck",
            "stockLock",
            "pay"
        );

        // 3. 执行
        flowService.executeFlow(flow, context);
        return "执行完成";
    }
}

8. 总结

动态编排 :步骤顺序由配置/数据库决定,不写死

可插拔 :新增节点只加类,不改主流程

自动回滚 :任意节点失败,前面节点自动反向回滚

统一异常 :不抛裸异常,流程安全中断

统一监控 :每个节点自动计时、日志埋点

异步支持 :高并发不阻塞,不占用请求线程

事务保证 :Spring 事务控制,数据一致

可中断、可跳过、可重试

相关推荐
JAVA面经实录9171 天前
Java企业级工程化·终极完整版背诵手册(无遗漏、全覆盖、面试+落地通用)
java·开发语言·面试
许彰午1 天前
CacheSQL(二):主从复制——OpLog 环形缓冲区与故障自动恢复
java·数据库·缓存
Bat U1 天前
JavaEE|多线程初阶(七)
java·开发语言
掌心向暖RPA自动化1 天前
如何获取网页某个元素在屏幕可见部分的中心坐标影刀RPA懒加载坐标定位技巧
java·javascript·自动化·rpa·影刀rpa
日取其半万世不竭1 天前
Minecraft Java版社区服务器搭建教程(Linux,适合新手)
java·linux·服务器
TeamDev1 天前
JxBrowser 9.0.0 版本发布啦!
java·前端·混合应用·jxbrowser·浏览器控件·跨平台渲染·原声输入
AI人工智能+电脑小能手1 天前
【大白话说Java面试题】【Java基础篇】第24题:Java面向对象有哪些特征
java·开发语言·后端·面试
AI人工智能+电脑小能手1 天前
【大白话说Java面试题】【Java基础篇】第25题:JDK1.8的新特性有哪些
java·开发语言·后端·面试
likerhood1 天前
SLF4J: Failed to load class “StaticLoggerBinder“ 解决
java·log4j·maven
早日退休!!!1 天前
大模型推理瓶颈七层分析模型
java·服务器·数据库