找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

274

积分

0

好友

22

主题
发表于 前天 20:21 | 查看: 5| 回复: 0

当审批流需要支持动态配置,审批级别从3级扩展到10级时,重新设计变得必要。面对这个需求,我的第一反应是使用责任链模式——它似乎完美契合!然而,当我用递归方式实现责任链后,在审批级别调整到8级时,系统直接抛出了StackOverflowError。经过一天的折腾,我终于明白:递归实现责任链是一个美丽的陷阱,而使用List才是正道

一、场景:审批流从3级扩展到10级

先说说业务场景。我们的审批流最初有3级:

  1. 主管审批:金额<1000元
  2. 经理审批:金额<10000元
  3. 总监审批:金额>=10000元

最初的if-else实现

@Service
public class ApprovalService {
    @Autowired
    private SupervisorApprovalService supervisorService;

    @Autowired
    private ManagerApprovalService managerService;

    @Autowired
    private DirectorApprovalService directorService;

    public ApprovalResult approve(ApprovalRequest request) {
        BigDecimal amount = request.getAmount();

        if (amount.compareTo(new BigDecimal("1000")) < 0) {
            // 主管审批
            return supervisorService.approve(request);
        } else if (amount.compareTo(new BigDecimal("10000")) < 0) {
            // 经理审批
            return managerService.approve(request);
        } else {
            // 总监审批
            return directorService.approve(request);
        }
    }
}

新需求

  • 审批级别可配置,从3级扩展到10级
  • 每个级别的审批规则可动态调整
  • 审批流程可跳过某些级别

我当时的想法:if-else肯定不行了,要用责任链模式!

二、第一次尝试:递归实现责任链(美丽的陷阱)

责任链模式的核心思想

  • 每个处理者知道下一个处理者
  • 自己处理完后,交给下一个处理者
  • 形成一条链

实现

1. 审批处理器接口

public interface ApprovalHandler {
    /**
     * 处理审批请求
     * @param request 审批请求
     * @return 审批结果
     */
    ApprovalResult handle(ApprovalRequest request);

    /**
     * 设置下一个处理器
     * @param next 下一个处理器
     */
    void setNext(ApprovalHandler next);

    /**
     * 获取处理器级别
     */
    String getLevel();
}

2. 主管审批处理器

@Component
public class SupervisorApprovalHandler implements ApprovalHandler {

    private ApprovalHandler next;

    @Override
    public ApprovalResult handle(ApprovalRequest request) {
        // 1. 判断自己能否处理
        if (canHandle(request)) {
            // 2. 处理逻辑
            ApprovalResult result = doApprove(request);

            // 3. 如果通过,交给下一个处理器
            if (result.isApproved() && next != null) {
                return next.handle(request);  // 递归调用
            }

            return result;
        }

        // 4. 如果不能处理,交给下一个
        if (next != null) {
            return next.handle(request);  // 递归调用
        }

        return ApprovalResult.reject("无法处理");
    }

    private boolean canHandle(ApprovalRequest request) {
        return request.getAmount().compareTo(new BigDecimal("1000")) < 0;
    }

    private ApprovalResult doApprove(ApprovalRequest request) {
        // 主管审批逻辑
        return ApprovalResult.approve("主管审批通过");
    }

    @Override
    public void setNext(ApprovalHandler next) {
        this.next = next;
    }

    @Override
    public String getLevel() {
        return "SUPERVISOR";
    }
}

3. 经理审批处理器

@Component
public class ManagerApprovalHandler implements ApprovalHandler {

    private ApprovalHandler next;

    @Override
    public ApprovalResult handle(ApprovalRequest request) {
        if (canHandle(request)) {
            ApprovalResult result = doApprove(request);
            if (result.isApproved() && next != null) {
                return next.handle(request);  // 递归调用
            }
            return result;
        }

        if (next != null) {
            return next.handle(request);  // 递归调用
        }

        return ApprovalResult.reject("无法处理");
    }

    private boolean canHandle(ApprovalRequest request) {
        return request.getAmount().compareTo(new BigDecimal("10000")) < 0;
    }

    private ApprovalResult doApprove(ApprovalRequest request) {
        // 经理审批逻辑
        return ApprovalResult.approve("经理审批通过");
    }

    @Override
    public void setNext(ApprovalHandler next) {
        this.next = next;
    }

    @Override
    public String getLevel() {
        return "MANAGER";
    }
}

4. 总监审批处理器

@Component
public class DirectorApprovalHandler implements ApprovalHandler {

    private ApprovalHandler next;

    @Override
    public ApprovalResult handle(ApprovalRequest request) {
        // 总监是最后一级,不需要判断next
        return doApprove(request);
    }

    private ApprovalResult doApprove(ApprovalRequest request) {
        // 总监审批逻辑
        return ApprovalResult.approve("总监审批通过");
    }

    @Override
    public void setNext(ApprovalHandler next) {
        // 总监是最后一级,不需要next
    }

    @Override
    public String getLevel() {
        return "DIRECTOR";
    }
}

5. 构建责任链

@Component
public class ApprovalChainBuilder {

    @Autowired
    private SupervisorApprovalHandler supervisorHandler;

    @Autowired
    private ManagerApprovalHandler managerHandler;

    @Autowired
    private DirectorApprovalHandler directorHandler;

    public ApprovalHandler buildChain() {
        // 构建链:主管 -> 经理 -> 总监
        supervisorHandler.setNext(managerHandler);
        managerHandler.setNext(directorHandler);
        // directorHandler不需要setNext

        return supervisorHandler;
    }
}

6. 使用

@Service
public class ApprovalService {

    @Autowired
    private ApprovalChainBuilder chainBuilder;

    public ApprovalResult approve(ApprovalRequest request) {
        ApprovalHandler chain = chainBuilder.buildChain();
        return chain.handle(request);
    }
}

优点

  • 解耦成功:每个处理器只关心自己的逻辑
  • 可扩展:新增处理器,只需要实现接口,加入链中
  • 灵活:可以动态调整链的顺序

三、踩坑:递归调用导致栈溢出

事故现象

审批级别扩展到8级后,测试时报错:

java.lang.StackOverflowError
at com.example.approval.SupervisorApprovalHandler.handle(SupervisorApprovalHandler.java:25)
at com.example.approval.SupervisorApprovalHandler.handle(SupervisorApprovalHandler.java:30)
at com.example.approval.SupervisorApprovalHandler.handle(SupervisorApprovalHandler.java:30)
...重复1000次

原因分析

// 审批级别:主管 -> 经理 -> 总监 -> 副总 -> 总经理 -> 董事会 -> 董事长 -> 集团
// 共8级
// 每个handle()方法调用next.handle(),是递归调用
// 递归深度 = 审批级别数 = 8
// JVM默认栈深度是1000左右,8级不会溢出
// 但如果在handle()里再调用其他方法,或者审批级别增加到20级,就会溢出

问题代码

@Override
public ApprovalResult handle(ApprovalRequest request) {
    if (canHandle(request)) {
        ApprovalResult result = doApprove(request);
        if (result.isApproved() && next != null) {
            return next.handle(request);  // 递归调用
        }
        return result;
    }

    if (next != null) {
        return next.handle(request);  // 递归调用
    }

    return ApprovalResult.reject("无法处理");
}

递归调用的问题

  • 每调用一次方法,栈帧增加一层
  • 审批级别越多,栈帧越多
  • 如果每个handle()里再调用其他方法,栈帧更多
  • 最终可能导致StackOverflowError

四、第二次尝试:用List代替递归(正道)

解决方案:放弃递归,用循环遍历List

@Component
public class ApprovalService {

    // Spring会自动注入所有ApprovalHandler
    @Autowired
    private List<ApprovalHandler> handlers;

    public ApprovalResult approve(ApprovalRequest request) {
        // 1. 根据审批级别排序
        List<ApprovalHandler> sortedHandlers = handlers.stream()
            .sorted(Comparator.comparing(ApprovalHandler::getOrder))
            .collect(Collectors.toList());

        // 2. 循环遍历,不是递归
        ApprovalResult result = null;
        for (ApprovalHandler handler : sortedHandlers) {
            if (handler.canHandle(request)) {
                result = handler.handle(request);
                if (!result.isApproved()) {
                    // 如果某个环节拒绝,直接返回
                    return result;
                }
            }
        }

        return result != null ? result : ApprovalResult.reject("无法处理");
    }
}

改造ApprovalHandler接口

public interface ApprovalHandler {
    /**
     * 判断是否能处理
     */
    boolean canHandle(ApprovalRequest request);

    /**
     * 处理审批
     */
    ApprovalResult handle(ApprovalRequest request);

    /**
     * 获取执行顺序
     */
    int getOrder();
}

具体实现

@Component
public class SupervisorApprovalHandler implements ApprovalHandler {

    @Override
    public boolean canHandle(ApprovalRequest request) {
        return request.getAmount().compareTo(new BigDecimal("1000")) < 0;
    }

    @Override
    public ApprovalResult handle(ApprovalRequest request) {
        // 主管审批逻辑
        return ApprovalResult.approve("主管审批通过");
    }

    @Override
    public int getOrder() {
        return 1;  // 第一个执行
    }
}

@Component
public class ManagerApprovalHandler implements ApprovalHandler {

    @Override
    public boolean canHandle(ApprovalRequest request) {
        return request.getAmount().compareTo(new BigDecimal("10000")) < 0;
    }

    @Override
    public ApprovalResult handle(ApprovalRequest request) {
        // 经理审批逻辑
        return ApprovalResult.approve("经理审批通过");
    }

    @Override
    public int getOrder() {
        return 2;  // 第二个执行
    }
}

@Component
public class DirectorApprovalHandler implements ApprovalHandler {

    @Override
    public boolean canHandle(ApprovalRequest request) {
        return true;  // 总监处理所有
    }

    @Override
    public ApprovalResult handle(ApprovalRequest request) {
        // 总监审批逻辑
        return ApprovalResult.approve("总监审批通过");
    }

    @Override
    public int getOrder() {
        return 3;  // 第三个执行
    }
}

优点

  • 没有递归,不会栈溢出
  • 审批级别可以无限扩展
  • 性能更好(循环比递归快)
  • 代码更简洁

五、Spring的@Order注解:更优雅的控制顺序

Spring框架提供了@Order注解,可以更优雅地控制执行顺序,这是Spring框架中常用的功能之一。

@Component
@Order(1)  // 第一个执行
public class SupervisorApprovalHandler implements ApprovalHandler {
    // ...
}

@Component
@Order(2)  // 第二个执行
public class ManagerApprovalHandler implements ApprovalHandler {
    // ...
}

@Component
@Order(3)  // 第三个执行
public class DirectorApprovalHandler implements ApprovalHandler {
    // ...
}

使用

@Component
public class ApprovalService {

    @Autowired
    private List<ApprovalHandler> handlers;  // Spring会自动按@Order排序

    public ApprovalResult approve(ApprovalRequest request) {
        ApprovalResult result = null;
        for (ApprovalHandler handler : handlers) {
            if (handler.canHandle(request)) {
                result = handler.handle(request);
                if (!result.isApproved()) {
                    return result;
                }
            }
        }
        return result != null ? result : ApprovalResult.reject("无法处理");
    }
}

六、责任链模式的适用场景

适合用责任链模式的场景

  1. 审批流:多级审批,每级逻辑不同
  2. 过滤器:Servlet Filter、Spring Interceptor
  3. 权限校验:多个校验规则,按顺序执行
  4. 日志处理:多个日志处理器,按级别处理
  5. 异常处理:多个异常处理器,按类型处理

不适合用责任链模式的场景

  1. 逻辑简单:只有1-2级,用if-else更直观
  2. 性能要求高:责任链有遍历开销
  3. 需要立即返回:责任链是顺序执行,不能并行

总结:责任链模式的使用原则

经过这次审批流改造,我对责任链模式的理解:

  1. 核心作用是解耦和顺序执行:每个处理器只关心自己的逻辑,不需要知道其他处理器。
  2. 避免递归实现:递归实现责任链是美丽的陷阱,容易栈溢出。用List + 循环更稳健。
  3. 控制执行顺序:用@Order注解或getOrder()方法控制顺序,别硬编码。
  4. 及时中断:如果某个处理器拒绝,应该立即中断,不要继续执行。
  5. 中小厂务实选择
  6. 简单场景:if-else
  7. 中等复杂:List + 循环
  8. 复杂场景:责任链模式 + Spring @Order

一句话总结:责任链模式很优雅,但递归是陷阱,List才是正道。

您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-9 00:47 , Processed in 0.063767 second(s), 38 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表