Java 23 种设计模式:从踩坑到精通 | 责任链模式 —— 请求流转,审批流程的本质

Java 23 种设计模式:从踩坑到精通 | 责任链模式 —— 请求流转,审批流程的本质

摘要:当请求需要经过多个处理者,且处理链在运行时才能确定时,写死 if-else 链会让代码严重耦合、难以扩展。责任链模式通过将处理者串联成一条链,让请求沿着链自动传递,直到被某个处理者处理。本文从 OA 审批流程出发,完整讲解纯责任链与不纯责任链的实现,结合 Servlet Filter、Spring Interceptor 等框架应用,并给出 Spring 自动装配链的实战写法,帮你彻底掌握“请求传递,各司其职”的设计精髓。

🗺️ 本文阅读地图(3 分钟速览)

  • 为什么 if-else 审批链一定会炸?
  • 纯责任链 vs 不纯责任链(99% 的人搞混)
  • 手写 OA 审批链(可直接抄)
  • Spring 自动装配责任链(生产级写法)
  • Servlet Filter / Spring Interceptor 源码级映射
  • 面试必问 + 面试官追问连环炮

📖 《Java 23 种设计模式:从踩坑到精通》
开篇:系列介绍与目录 | 上一篇:代理模式 | 当前:责任链模式 | 下一篇:命令模式
🔗 返回系列总目录



1. 从“审批流程”的层层传递说起

假设你在开发 OA 系统的请假审批功能:员工提交请假申请后,需要依次经过直属领导、部门经理、HR 三个角色审批,每个角色可能批准、驳回或转交下一级。如果硬编码:

if (leader.approve(request)) {
    if (manager.approve(request)) {
        if (hr.approve(request)) {
            // 通过
        }
    }
}

这种写法将审批顺序死死绑定在代码中,当审批流程变化(如增加财务审批环节)时,必须修改核心代码,极易引入 Bug。更糟糕的是,如果需要在运行时动态调整审批顺序(比如根据请假天数跳过某些审批节点),这种嵌套结构完全无法应对。

责任链模式(Chain of Responsibility Pattern)通过将处理者组织成一条链,让请求沿着链自动传递,直到某个处理者处理它。处理者和请求发送者都只关心自己的职责,链的结构可以灵活调整。

1.1 你的场景该不该用责任链?

判断标准是 → 用责任链否 → 用其他方式
有多个对象可以处理同一请求,且处理器动态确定
需要向多个处理器提交请求,但不明确最终由谁处理
处理器顺序需要灵活调整,甚至运行时动态改变
处理逻辑非常简单,且处理器固定不变直接调用即可

滥用责任链的典型症状:链长度超过 5 个节点、每个节点只做简单日志、Debug 像走迷宫。此时应优先考虑策略模式(规则固定)或编排器(强顺序强事务)。


2. 模式定义与 UML 结构

责任链模式 使多个对象都有机会处理请求,从而避免请求的发送者与接收者之间的耦合。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。它属于 行为型设计模式
责任链模式

从上方的 UML 类图中可以看出,责任链模式主要由以下角色构成:

  • 抽象处理者(Approver:它是链式调用的核心模板。Approver 不仅定义了 handleRequest 处理规范,最关键的是内部持有一个 nextApprover 引用指向自己,从而构成了“自己调用自己”的链式结构。
  • 具体处理者(Leader / Manager / HR:它们各自独立封装了审批规则,职责清晰:
    • Leader(直属领导):只看 ≤3 天的假条,超过就甩给下一节点。
    • Manager(部门经理):只看 ≤7 天的假条,超过继续往下传。
    • HR:作为链条的兜底节点,审批 ≤30 天的假条并做最终把关。

核心机制:请求从 Leader 进入链中,遵循“各司其职”的原则传递。这种结构的好处是,如果某天需要增加一个“财务审批”节点,你只需新增一个 Finance 类,并把它像乐高一样拼进链中,完全不用修改 LeaderManager 的内部代码。


3. 纯责任链 vs 不纯责任链

对比维度✅ 纯责任链:非黑即白🔄 不纯责任链:人人有份
处理者行为要么处理请求,要么转发给下一个处理者,二者互斥每个处理者都可以加工请求,然后继续传递
请求终止一定被某个处理者处理,或到达链尾仍未被处理可能被多个处理者依次处理
典型应用审批流程(一个节点通过才往下)Servlet Filter 链、Spring Interceptor

Servlet Filter 对比表(纯 vs 不纯)

维度审批链(纯责任链)Servlet Filter(不纯责任链)
是否中断传递✅ 是(通过则停止)❌ 否(通常每个 Filter 都执行)
是否处理响应❌ 只处理请求✅ 请求和响应都走一遍链
典型用途业务规则审批横切逻辑(日志、编码、鉴权)

实际开发中,不纯责任链更常见,如 Filter 链、拦截器链。


4. 代码实现:OA 审批责任链(纯责任链)

4.1 请求类

public class LeaveRequest {
    private String name;
    private int days;
    private String reason;

    public LeaveRequest(String name, int days, String reason) {
        this.name = name;
        this.days = days;
        this.reason = reason;
    }

    public String getName() { return name; }
    public int getDays() { return days; }
    public String getReason() { return reason; }
}

💬 白话:把“请假天数”封装进请求对象,而不是散落在 if 里。
📌 设计点:请求对象携带所有上下文,处理者只依赖请求本身,不依赖外部环境。

4.2 抽象处理者

public abstract class Approver {
    protected Approver nextApprover;

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

    public abstract void handleRequest(LeaveRequest request);
}

4.3 具体处理者

public class Leader extends Approver {
    @Override
    public void handleRequest(LeaveRequest request) {
        if (request.getDays() <= 3) {
            System.out.println("直属领导批准 " + request.getName() + " 请假 " + request.getDays() + " 天");
        } else if (nextApprover != null) {
            System.out.println("直属领导无权审批,转交下一级...");
            nextApprover.handleRequest(request);
        }
    }
}

public class Manager extends Approver {
    @Override
    public void handleRequest(LeaveRequest request) {
        if (request.getDays() <= 7) {
            System.out.println("部门经理批准 " + request.getName() + " 请假 " + request.getDays() + " 天");
        } else if (nextApprover != null) {
            System.out.println("部门经理无权审批,转交下一级...");
            nextApprover.handleRequest(request);
        }
    }
}

public class HR extends Approver {
    @Override
    public void handleRequest(LeaveRequest request) {
        if (request.getDays() <= 30) {
            System.out.println("HR 批准 " + request.getName() + " 请假 " + request.getDays() + " 天");
        } else {
            System.out.println("请假天数过长,不予批准");
        }
    }
}

💬 白话:每个角色只看自己能批多少天,超过就甩给下一个,最后 HR 兜底。

4.4 客户端(手动组装链)

Approver leader = new Leader();
Approver manager = new Manager();
Approver hr = new HR();
leader.setNext(manager);
manager.setNext(hr);

leader.handleRequest(new LeaveRequest("张三", 2, "事假"));
// 直属领导批准
leader.handleRequest(new LeaveRequest("李四", 5, "病假"));
// 直属领导 → 部门经理批准
leader.handleRequest(new LeaveRequest("王五", 10, "年假"));
// 直属领导 → 部门经理 → HR 批准

5. 代码实现:请求处理链(不纯责任链,类似 Filter)

5.1 抽象处理者

public abstract class FilterChainHandler {
    protected FilterChainHandler next;

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

    public void doFilter(String request) {
        handle(request);
        if (next != null) {
            next.doFilter(request);
        }
    }

    protected abstract void handle(String request);
}

💬 白话:每个 Filter 执行完自己的逻辑后,自动把请求传给下一个。

5.2 具体处理者

public class LogFilter extends FilterChainHandler {
    @Override
    protected void handle(String request) {
        System.out.println("[日志] 记录请求:" + request);
    }
}

public class AuthFilter extends FilterChainHandler {
    @Override
    protected void handle(String request) {
        System.out.println("[权限] 校验请求权限...");
    }
}

5.3 客户端

FilterChainHandler log = new LogFilter();
FilterChainHandler auth = new AuthFilter();
log.setNext(auth);

log.doFilter("/api/user");
// [日志] 记录请求:/api/user
// [权限] 校验请求权限...

6. 生产级写法:Spring 自动装配责任链

手动 setNext 太“教学化”。在实际项目中,我们可以利用 Spring 的依赖注入自动组装链。

@Component
public class ApprovalChain {
    private final List<Approver> approvers;

    // Spring 自动注入所有 Approver 实现类,按 @Order 排序
    public ApprovalChain(List<Approver> approvers) {
        this.approvers = approvers;
        link();
    }

    private void link() {
        for (int i = 0; i < approvers.size() - 1; i++) {
            approvers.get(i).setNext(approvers.get(i + 1));
        }
    }

    public void handleRequest(LeaveRequest request) {
        if (!approvers.isEmpty()) {
            approvers.get(0).handleRequest(request);
        }
    }
}

💡 亮点:新增审批节点只需加一个 @Component 并实现 Approver 接口,链自动组装,完全符合开闭原则。


7. 优缺点一览

优点缺点
解耦:请求发送者与接收者解耦,各自独立变化性能:链太长时请求遍历会影响性能
灵活:可动态增加或删除处理者,修改链的顺序调试不便:链式调用递归或循环,排查问题需要顺藤摸瓜
符合单一职责:每个处理者只处理自己的职责可能不被处理:纯责任链下若未正确配置,请求可能“丢失”
可扩展:新增处理者只需加新类,无需修改现有逻辑

⚠️ 链 > 5 就要警惕:考虑拆分为多个子链,或引入规则引擎。


8. 框架与实践中的应用

8.1 Servlet Filter 链

javax.servlet.Filter 是不纯责任链的经典实现。每个 Filter 可以选择放行或拦截,请求和响应都会经过整条链。

public class EncodingFilter implements Filter {
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
        // 前置处理
        chain.doFilter(req, res);  // 交给下一个 Filter
        // 后置处理
    }
}

8.2 Spring Interceptor 链

Spring MVC 的 HandlerInterceptor 通过 preHandle()postHandle()afterCompletion() 控制请求的预处理和后处理,也是责任链的变体。

8.3 Dubbo Filter 链

Dubbo 的服务调用过滤链通过 SPI 机制链式加载 MonitorFilterExceptionFilter 等,是责任链在 RPC 框架中的典型应用。


9. 面试必问 + 面试官追问连环炮

基础必问

  • 责任链模式的优点? → 解耦请求与处理,灵活扩展,符合单一职责。
  • Servlet Filter 是什么模式? → 不纯责任链模式,FilterChain 就是链。
  • 纯责任链与不纯责任链的区别? → 纯责任链处理者要么处理要么转发;不纯责任链每个处理者都可以部分处理并继续传递。

🎤 面试官追问

  • “责任链会不会有循环引用?”
    👉 会,需在 setNext 时检测 next == this,或使用 List 结构避免环形依赖。
  • “如何保证责任链一定被处理?”
    👉 设置兜底处理者(Tail Handler),例如 HR 节点作为最终审批人。
  • “Spring 的 Interceptor 是责任链吗?”
    👉 是,但 preHandle 可以短路终止链的传递。
  • “如何避免链太长影响性能?”
    👉 设置超时、熔断,或限制链长度 ≤ 5。
  • “责任链和策略模式如何选择?”
    👉 责任链关注“谁来处理”,策略关注“怎么处理”。二者可组合使用:链负责路由,策略负责执行。

🎉 恭喜:如果你能立刻说出 Servlet Filter 是责任链模式,Spring Interceptor 也基于责任链思想,并能区分纯与不纯责任链的差异,你已经掌握了行为型模式中最常见的“请求传递”设计。


10. 六大设计原则在责任链模式中的体现

设计原则在责任链模式中的体现
单一职责原则(SRP)每个处理者只负责一种审批规则或处理逻辑
开闭原则(OCP)新增处理者无需修改原有代码,只需调整链的装配
里氏替换原则(LSP)所有具体处理者都可替换抽象 Handler
依赖倒置原则(DIP)客户端依赖抽象 Handler,不依赖具体处理者
接口隔离原则(ISP)抽象处理者接口方法精简,只定义处理与设置下一个
迪米特法则(LoD)请求只与第一个处理者交互,无需知道链的结构

《Java 23 种设计模式:从踩坑到精通》快速导航

🔔 关注《Java 23 种设计模式:从踩坑到精通》,用 25 篇文章彻底吃透设计模式。
📦 福利预告:全系列代码及 UML 源码将在完结时统一打包开放,点击「关注」「收藏」第一时间获取。
🚀 下一篇:命令模式:为什么 Ctrl+Z 能撤销?——把操作封装成对象,实现事务回滚与任务队列!🚧 即将发布,敬请关注!

📌 除了设计模式,我也在深挖智能物流实战(WMS、托盘调度、机器学习落地)。欢迎点击头像,看看专栏 《出版社物流WMS智能调度实战》。技术相通,思路可鉴。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值