GoF设计模式——责任链模式

GoF设计模式——责任链模式

本文是【GoF设计模式】系列第21篇,更多内容欢迎关注公众号:咖啡八杯

image

前言

为什么需要责任链模式?

假设要实现一个请假审批功能:1 天以内组长批,3 天以内经理批,7 天以内总监批,7 天以上总经理批。最直接的写法是在审批方法里堆一串 if-else

public void approve(LeaveRequest request) {if (request.getDays() <= 1) {teamLeader.approve(request);} else if (request.getDays() <= 3) {manager.approve(request);} else if (request.getDays() <= 7) {director.approve(request);} else {generalManager.approve(request);}
}

看起来能跑,但问题藏在后面。第一,审批逻辑和审批人死死绑在客户端--新增一个"副总经理"层级,这段 if-else 必须改;调整天数阈值,也得改这里。第二,客户端被迫知道整条审批链的结构和每个人的权限范围,本该只管"提交申请",却被迫操心"谁先批、谁后批、每个人能批多少"。

问题症结在于把"请求的处理者"和"请求的发起者"焊在了一起--发送者必须清楚链上有谁、按什么顺序、各自管什么。责任链模式解决的就是这个问题:把多个处理者串成一条链,请求沿链传递,发送者只管把请求丢给链首,至于最终由谁处理,链自己决定。

概念

责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,核心思想是将请求的发送者和接收者解耦,让多个对象都有机会处理请求,将这些对象连成一条链,沿着链传递请求,直到有一个对象处理它为止

可以把它想象成公司里的报销审批:拿着报销单先找组长签字,组长权限不够就转给经理,经理不够再转给总监。申请人只管提交单子,不需要知道最终由谁签批;每个审批人也只管两件事--"这笔我能不能批"和"批不了转给谁"。

责任链模式涉及三个角色:

  • Handler(抽象处理者):定义处理请求的接口,持有下一个处理者的引用,提供设置后继者的方法
  • ConcreteHandler(具体处理者):实现处理逻辑,判断自己能否处理,能则处理,不能则转发给后继者
  • Client(客户端):创建并组装处理者链,将请求发送给链首
classDiagramdirection BTclass Handler {<<abstract>>#next: Handler+setNext(Handler) Handler+handleRequest(Request)}class ConcreteHandlerA {+handleRequest(Request)}class ConcreteHandlerB {+handleRequest(Request)}class Client {+main()}ConcreteHandlerA --|> Handler : 继承ConcreteHandlerB --|> Handler : 继承Handler o--> Handler : next 持有后继Client --> Handler : 发送请求

图中各类之间的关系:Handler 是抽象处理者,用一个 next 字段持有后继者的引用,并通过 setNext 把处理者串起来。ConcreteHandlerAConcreteHandlerB 都继承 Handler,各自实现 handleRequest 的判断逻辑--能处理就处理,不能就调用 next.handleRequest 把请求往后传。Client 只认识 Handler,把请求发给链首,不关心链上有几个处理者、各自是谁。请求就这样沿 next 引用一路传递,直到被某个处理者接收或到达链尾。

实现

责任链模式的核心是"请求与处理者解耦 + 链式传递"--抽象处理者统一维护后继引用和转发逻辑,子类只负责"我能不能处理"。根据请求被处理后是否继续传递,分纯责任链和不纯责任链两种。

标准实现

用抽象类作为 Handler 基类,统一维护 next 引用和转发逻辑;子类实现 handleRequest,判断自己能否处理,能则处理,不能则转发给 next。请求被一个处理者接收后不再继续传递--这是"纯责任链"。

// 请求类
class Request {private int level;private String data;public Request(int level, String data) {this.level = level;this.data = data;}public int getLevel() { return level; }public String getData() { return data; }
}// 抽象处理者
abstract class Handler {protected Handler next;public Handler setNext(Handler next) {this.next = next;return next;                   // 返回 next 支持链式调用}public abstract void handleRequest(Request request);
}// 具体处理者A:处理 level <= 1 的请求
class ConcreteHandlerA extends Handler {public void handleRequest(Request request) {if (request.getLevel() <= 1) {System.out.println("ConcreteHandlerA 处理: " + request.getData());} else if (next != null) {next.handleRequest(request);} else {System.out.println("请求未被处理: " + request.getData());}}
}// 具体处理者B:处理 level <= 2 的请求
class ConcreteHandlerB extends Handler {public void handleRequest(Request request) {if (request.getLevel() <= 2) {System.out.println("ConcreteHandlerB 处理: " + request.getData());} else if (next != null) {next.handleRequest(request);} else {System.out.println("请求未被处理: " + request.getData());}}
}// 客户端:构建链并发送请求
class Client {public static void main(String[] args) {Handler handlerA = new ConcreteHandlerA();Handler handlerB = new ConcreteHandlerB();handlerA.setNext(handlerB);                          // 构建链:A -> BhandlerA.handleRequest(new Request(1, "低级别请求")); // A 处理handlerA.handleRequest(new Request(2, "高级别请求")); // B 处理handlerA.handleRequest(new Request(3, "超限请求"));    // 无人处理}
}

角色对照

  • Handler(抽象处理者)Handler,持有 next 并提供 setNext
  • ConcreteHandler(具体处理者)ConcreteHandlerAConcreteHandlerB,各自判断 level 范围
  • Client(客户端)Client,组装链并发送请求

关键点setNext 返回 next 本身,因此 handlerA.setNext(handlerB).setNext(handlerC) 能一路链式串下去。抽象类统一维护 next 引用和转发逻辑,子类只写"我处理什么 + 处理不了转给 next"两行判断,不重复转发代码。链尾的 else 分支兜底处理无人接收的情况,避免请求"悬空"--这是纯责任链必须处理的问题。

💡 链的构建方式:上例是手动 setNext 逐个拼接,实际项目里链往往不是手拼的。常见三种方式:手动 setNext(简单直观,适合链短且固定)、配置驱动(如 Spring 的 @Order 注解给处理者标序,容器自动按序收集成链)、枚举定义(用一个枚举类集中声明链顺序,改顺序只动枚举)。链越动态越长,越该用配置或枚举集中管理,避免散落各处的 setNext 难维护。

引入一个具体场景:医院的分诊台。病人挂号后,分诊台按病情轻重分流--普通感冒分给全科,需要手术的分给外科,危重病人分给急诊。每个科室只判断"这病我能不能看",看不了就往下一个科室转。这正是纯责任链"沿链寻找唯一处理者"的思路。

// 请求:患者
class Patient {private String name;private int severity;                  // 病情严重程度:1轻 2中 3重public Patient(String name, int severity) {this.name = name;this.severity = severity;}public String getName() { return name; }public int getSeverity() { return severity; }
}// 抽象处理者:科室
abstract class Department {protected Department next;public Department setNext(Department next) {this.next = next;return next;}public abstract void treat(Patient patient);
}// 全科:处理 severity == 1
class GeneralPractice extends Department {public void treat(Patient patient) {if (patient.getSeverity() == 1) {System.out.println("全科接诊: " + patient.getName());} else if (next != null) {next.treat(patient);} else {System.out.println("无人接诊: " + patient.getName());}}
}// 外科:处理 severity == 2
class Surgery extends Department {public void treat(Patient patient) {if (patient.getSeverity() == 2) {System.out.println("外科接诊: " + patient.getName());} else if (next != null) {next.treat(patient);}}
}// 急诊:处理 severity == 3
class Emergency extends Department {public void treat(Patient patient) {if (patient.getSeverity() == 3) {System.out.println("急诊接诊: " + patient.getName());} else if (next != null) {next.treat(patient);}}
}// 客户端:分诊台
class TriageDesk {public static void main(String[] args) {Department gp = new GeneralPractice();Department surgery = new Surgery();Department emergency = new Emergency();gp.setNext(surgery).setNext(emergency);              // 全科 -> 外科 -> 急诊gp.treat(new Patient("张三", 1));                    // 全科接诊gp.treat(new Patient("李四", 2));                    // 外科接诊gp.treat(new Patient("王五", 3));                    // 急诊接诊}
}

角色对照

  • Handler(抽象处理者)Department,持有 next 并提供 setNext
  • ConcreteHandler(具体处理者)GeneralPracticeSurgeryEmergency,各自按 severity 接诊
  • Client(客户端)TriageDesk,组装链并发送患者

关键点gp.setNext(surgery).setNext(emergency) 一行就把整条分诊链串好,新增一个"儿科"只需再 setNext 一次,老科室代码一行不改--这就是开闭原则的体现。客户端只和链首 gp 打交道,完全不感知链的长度和顺序。

不纯责任链

GoF 标准实现是纯责任链--请求被一个处理者接收后不再传递。但实际项目里更常见的是"不纯责任链"--每个处理者都对请求做一部分处理,然后继续传递,如 Servlet Filter、Spring Interceptor。它的写法借用模板方法模式的思路:handleRequest 固定"先处理再传递"的流程,子类只实现 doHandle 负责自己的那部分逻辑。

// 抽象处理者(不纯责任链)
abstract class Handler {protected Handler next;public Handler setNext(Handler next) {this.next = next;return next;}public void handleRequest(Request request) {doHandle(request);                 // 先处理自己的部分if (next != null) {next.handleRequest(request);   // 再继续传递}}protected abstract void doHandle(Request request);
}// 具体处理者:记录日志
class LogHandler extends Handler {protected void doHandle(Request request) {System.out.println("记录日志: " + request.getData());}
}// 具体处理者:权限校验
class AuthHandler extends Handler {protected void doHandle(Request request) {System.out.println("权限校验通过: " + request.getData());}
}// 具体处理者:业务处理
class BusinessHandler extends Handler {protected void doHandle(Request request) {System.out.println("执行业务逻辑: " + request.getData());}
}// 客户端
class Client {public static void main(String[] args) {Handler log = new LogHandler();Handler auth = new AuthHandler();Handler biz = new BusinessHandler();log.setNext(auth).setNext(biz);log.handleRequest(new Request(1, "用户请求"));// 输出:记录日志 -> 权限校验通过 -> 执行业务逻辑}
}

关键点:每个处理者都参与处理,请求一路传到链尾。如果某个处理者需要中断链(如权限校验失败),最直接的方式是在 doHandle 中抛异常,由调用方 catch 处理:

// 不纯责任链中断示例:权限校验失败抛异常
class AuthHandler extends Handler {protected void doHandle(Request request) {if (request.getLevel() < 0) {throw new RuntimeException("权限不足,请求被拒绝: " + request.getData());}System.out.println("权限校验通过: " + request.getData());}
}

抛异常是最常见的中断方式--不纯责任链通常运行在框架容器中(如 Servlet Filter),异常会被容器统一捕获并转换为错误响应。

函数式实现

Java 8 之后,处理者可以是一个 Function,靠 andThen 组合成链,不需要抽象类。这是不纯责任链的现代写法,每个处理者都对请求做一点处理再往下传,andThen 天然就是"先做自己的、再交给下一个"。

import java.util.function.Function;class FunctionalChain {public static void main(String[] args) {// 每个处理者是一个 Function:处理输入,返回输出Function<String, String> log = req -> {System.out.println("记录日志: " + req);return req;};Function<String, String> auth = req -> {System.out.println("权限校验: " + req);return req;};Function<String, String> biz = req -> {System.out.println("业务处理: " + req);return req;};// andThen 组合成链:log -> auth -> bizFunction<String, String> chain = log.andThen(auth).andThen(biz);chain.apply("用户请求");// 输出:记录日志 -> 权限校验 -> 业务处理}
}

关键点Function.andThen 把多个处理者串成"先做自己的、再传下一个",等价于不纯责任链的"先 doHandlenext"。无需抽象类、无需 setNext,链就是函数的组合,写法极简。但中断不便:andThen 链中某个处理者想中断,只能抛异常或返回特殊标记,没有 chain.doFilter 那样"不调用即中断"的语义。

引入一个场景:快递流水线。打包、贴标、质检三道工序依次过,每道工序给包裹做一点加工就传给下一道,这正是 andThen 的"接力加工"。如果某道工序发现包裹破损,抛异常即可让整条流水线停下。

💡 何时用函数式:处理者逻辑简单、无状态、且是"都参与处理"的不纯链时,优先用 andThen;一旦需要"按条件中断""共享上下文""运行时增删处理者",就回到抽象类方案,函数式胜在简洁,抽象类胜在控制力。

如何选择

维度 纯责任链 不纯责任链
处理方式 只有一个处理者处理请求 每个处理者都参与处理
传递时机 处理不了才传递 处理完自己的部分后继续传递
链中断 处理后自动停止 需要显式中断(异常/标志位)
典型场景 审批流程、异常处理 过滤器链、拦截器链

简单记忆审批用纯链,过滤用不纯链。纯责任链关注"谁来处理",不纯责任链关注"每个环节做什么"。

推荐:业务审批场景用纯责任链(一个请求只该一个人批);框架级的请求预处理用不纯责任链(每个切面都要过一遍)。大多数 Web 框架(Filter、Interceptor)都是不纯责任链。

总结

本质:把请求的发送者和接收者解耦,多个处理者串成链,请求沿链传递直到被处理。

什么时候用

  • 请求需要经过多个对象处理,但具体由谁处理、按什么顺序在编译时不确定
  • 想动态增删处理者或调整链的顺序,不想改动已有代码
  • 处理同一请求的方式可按优先级排列,逐个尝试

什么时候不用

  • 处理者之间有严格的固定顺序且不会变化,直接硬编码更简单
  • 只有一个处理者,没必要引入链
  • 处理者之间需要共享大量上下文,链式传递信息不方便

⚠️ 缺点与调试技巧

  • 性能损耗:链越长,请求要经过的节点越多,调用栈越深,长链在高频场景不可忽视
  • 调试困难:请求被默默转发,出错时不易确定是哪个处理者接收了请求,链越深越难追踪
  • 循环引用风险setNext 拼接错误可能形成环,请求在环里无限递归直至栈溢出
  • 调试建议:在每个处理者入口加一行日志(处理者名加请求关键字),请求流向一目了然;链构建完成后遍历校验,确保无环(如用 Set 记录已访问节点,遇重复即抛异常)

简单记忆:发送者只管丢请求,处理者排队接力传;批不了就往后送,纯链一个办、不纯链一起干。

相似模式区分

总览

模式 接口关系 核心意图 典型场景
责任链 处理者持有下一个处理者的引用 沿链寻找处理者,请求被一个或多个处理者接收 审批流程、过滤器链
装饰 装饰者持有被装饰对象,实现相同接口 逐层增强对象功能 I/O 流包装、UI 组件增强
策略 策略之间独立,由 Context 持有和切换 一个上下文选择一种策略执行 支付方式、排序算法
命令 命令封装请求,调用者持有命令 把请求对象化,支持撤销、排队、记录 任务队列、宏命令、撤销重做

简单记忆:责任链找"谁来办",装饰模式层层"加 buff",策略模式"一次选一个",命令模式把请求"打包发"。

责任链 vs 装饰模式

两者都用链式结构,但意图完全不同:

维度 责任链模式 装饰模式
核心意图 沿链寻找处理者,请求被一个处理者接收 逐层增强对象功能,每个装饰者都参与处理
结构差异 处理者持有下一个处理者的引用 装饰者持有被装饰对象的引用,实现相同接口
关注点 请求的路由和分发 对象功能的动态扩展
典型场景 审批流程、过滤器链 I/O 流包装、UI 组件增强

逐步区分法

  • 如果请求只需要一个处理者接收 -> 选责任链
  • 如果每个环节都需要参与处理且增强同一个对象 -> 选装饰模式

简单记忆口诀:"责任链找谁来办,装饰模式层层加"。

推荐:两者都能用但意图不同--需要"选一个处理者"用责任链,需要"层层增强"用装饰模式。Servlet Filter 表面像装饰模式(每个都处理),但本质是责任链(各管各的切面,不是增强同一个对象)。

责任链 vs 策略模式

两者都用于选择处理方式,但选择机制不同:

维度 责任链模式 策略模式
核心意图 多个处理者沿链传递,动态寻找处理者 一个上下文选择一种策略执行
结构差异 处理者之间有链式连接(next 引用) 策略之间独立,由 Context 持有和切换
关注点 处理者的动态组合和传递 算法的可替换性
典型场景 审批流程、过滤器链、异常处理 支付方式选择、排序算法切换、折扣策略

逐步区分法

  • 如果处理者需要按顺序逐个判断能否处理 -> 选责任链
  • 如果可以直接确定用哪种策略 -> 选策略模式

简单记忆口诀:"责任链逐个试,策略模式一次选"。

推荐:能直接确定用哪个处理者就用策略模式(更简单高效),只有处理者不确定或需要逐个尝试时才用责任链。策略是"知道选谁",责任链是"不知道谁行,挨个试"。

责任链 vs 命令模式

两者都能把请求和处理分开,但关注点不同:命令模式把请求封装成对象,关注"请求的对象化、可撤销、可排队";责任链关注"多个处理者如何串联传递"。

维度 责任链模式 命令模式
核心意图 沿链寻找处理者,请求被一个或多个处理者接收 把请求封装成对象,使请求的发送者和执行者解耦
结构差异 处理者持有下一个处理者的引用 命令对象持有接收者,调用者持有命令
关注点 处理者的串联与传递 请求的对象化、撤销、排队、日志
典型场景 审批流程、过滤器链 任务队列、宏命令、撤销重做

逐步区分法

  • 如果目的是让多个处理者接力处理 -> 选责任链
  • 如果目的是把请求打包成对象以便撤销、排队、记录 -> 选命令模式

简单记忆口诀:"责任链接力传,命令模式打包发"。

推荐:两者常组合使用,把每个命令对象当作责任链上的一个处理者,就能拼出可配置的审批流:命令模式负责"把审批动作对象化",责任链负责"按权限层级串起来传"。单独用时,要"传给谁不确定"用责任链,要"请求可撤销可排队"用命令模式。

练习题目

跨部门采购审批

题目描述:某公司不同部门的采购审批链不同(例如 IT 部门没有经理层,销售部门没有组长层)。请使用责任链模式,根据输入动态构建审批链,处理采购申请。审批人及金额上限:

  • TeamLeader(组长):≤ 1000 元
  • Manager(经理):≤ 5000 元
  • Director(总监):≤ 20000 元

如果金额超过审批链末端处理人的上限,则由末端处理人否决。

输入描述:第一行是审批链配置,由审批人名称组成,用空格分隔,按顺序执行。名称为 TeamLeader、Manager、Director,至少包含一个,顺序任意,可缺少。第二行是一个整数 N(1 ≤ N ≤ 100),表示采购申请数量。接下来的 N 行,每行格式为 申请人 金额

输出描述:对于每个采购请求,输出一行:批准输出 申请人 Approved by 审批人.,否决输出 申请人 Denied by 审批人.(审批人为链中最后一个处理者)。

输入示例

TeamLeader Manager Director
4
Alice 500
Bob 3000
Charlie 15000
David 25000

输出示例

Alice Approved by TeamLeader.
Bob Approved by Manager.
Charlie Approved by Director.
David Denied by Director.

解题思路:题目是纯责任链的典型应用--审批链可由输入动态拼接,正是责任链"运行时增删处理者、调整顺序"的强项。每种审批人是一个 ConcreteHandler,持有金额上限判断逻辑;客户端根据输入动态构建链,将采购请求发给链首。请求沿链传递--金额在当前审批人上限内则批准,否则传给下一个;到达链尾仍超限则否决。去掉责任链,客户端就得为每种审批链组合写一份硬编码的 if-else,部门一多组合爆炸。

import java.util.*;public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);String line = sc.nextLine();String[] strs = line.split(" ");Handler[] handlers = new Handler[strs.length];for (int i = 0; i < strs.length; i++) {handlers[i] = createHandler(strs[i]);}// 动态构建责任链for (int i = 0; i < handlers.length - 1; i++) {handlers[i].setNextHandler(handlers[i + 1]);}int n = sc.nextInt();while (n-- > 0) {String name = sc.next();int amount = sc.nextInt();handlers[0].handle(new Request(name, amount));}}static Handler createHandler(String type) {switch (type) {case "TeamLeader": return new TeamLeader();case "Manager":    return new Manager();default:           return new Director();}}
}interface Handler {public void handle(Request request);public void setNextHandler(Handler handler);
}class Request {private String name;private int amount;public Request(String name, int amount) {this.name = name;this.amount = amount;}public String getName() { return name; }public int getAmount() { return amount; }
}class TeamLeader implements Handler {private Handler next;public void setNextHandler(Handler handler) { this.next = handler; }public void handle(Request request) {if (request.getAmount() <= 1000) {System.out.println(request.getName() + " Approved by TeamLeader.");} else if (next != null) {next.handle(request);} else {System.out.println(request.getName() + " Denied by TeamLeader.");}}
}class Manager implements Handler {private Handler next;public void setNextHandler(Handler handler) { this.next = handler; }public void handle(Request request) {if (request.getAmount() <= 5000) {System.out.println(request.getName() + " Approved by Manager.");} else if (next != null) {next.handle(request);} else {System.out.println(request.getName() + " Denied by Manager.");}}
}class Director implements Handler {private Handler next;public void setNextHandler(Handler handler) { this.next = handler; }public void handle(Request request) {if (request.getAmount() <= 20000) {System.out.println(request.getName() + " Approved by Director.");} else if (next != null) {next.handle(request);} else {System.out.println(request.getName() + " Denied by Director.");}}
}

扩展:实际项目中的责任链模式

Servlet Filter Chain

Java Servlet 的过滤器链是责任链最经典的应用。每个 Filter 在请求到达 Servlet 之前和响应返回之后各执行一段逻辑,如编码转换、权限校验、日志记录。FilterChain.doFilter() 是责任链的核心--每个 Filter 调用它把请求传给下一个 Filter,不调用就中断链。Filter 的执行顺序由 web.xml 里的 <filter-mapping> 顺序决定,这是典型的不纯责任链,每个 Filter 都参与处理。

public class AuthFilter implements Filter {public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {HttpServletRequest req = (HttpServletRequest) request;String token = req.getHeader("Authorization");if (token == null || token.isEmpty()) {((HttpServletResponse) response).sendError(401, "未授权");return;                          // 不调用 chain.doFilter,中断链}chain.doFilter(request, response);   // 继续传递}
}

Spring Cloud Gateway Filter

Spring Cloud Gateway 的过滤器链是云原生网关里责任链的典型应用,与 Servlet Filter 一脉相承但更现代化。请求进网关后依次穿过一串 GatewayFilterGatewayFilterChain.filter(exchange) 把请求传给下一个,不调用就中断链。Gateway 区分 GlobalFilter(全局生效,如鉴权、限流)和 GatewayFilter(通过路由配置绑定,如改写路径),这是典型的不纯责任链,每个过滤器都参与处理。

@Component
public class AuthFilter implements GlobalFilter {public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {String token = exchange.getRequest().getHeaders().getFirst("Authorization");if (token == null) {exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);return exchange.getResponse().setComplete();   // 不调 chain.filter,中断链}return chain.filter(exchange);                     // 继续传递}
}

Spring HandlerInterceptor

Spring MVC 的拦截器链是责任链在框架层的应用,在 Controller 处理请求前后执行通用逻辑。preHandle 返回 true 继续传递,返回 false 中断链。拦截器有三个钩子:preHandle(Controller 前)、postHandle(Controller 后视图渲染前)、afterCompletion(请求完成后清理资源)。与 Filter 不同,Interceptor 基于 Spring MVC,能拿到 Handler 信息,更贴近业务层。

@Component
public class LoginInterceptor implements HandlerInterceptor {public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler) throws Exception {String token = request.getHeader("Authorization");if (token == null) {response.setStatus(401);return false;                    // 中断链}return true;                         // 继续传递}
}

OkHttp Interceptor

OkHttp 的拦截器机制是责任链在网络请求层的应用,每个拦截器能查看、修改请求和响应。chain.proceed(request) 把请求传给下一个拦截器,拦截器可以在 proceed 前改请求、在 proceed 后改响应--形成"请求从外到内、响应从内到外"的双向处理。OkHttp 区分 addInterceptor(应用拦截器,只调一次)和 addNetworkInterceptor(网络拦截器,重定向时多次调用)。

public class HeaderInterceptor implements Interceptor {public Response intercept(Chain chain) throws IOException {Request modified = chain.request().newBuilder().addHeader("Authorization", "Bearer token").build();return chain.proceed(modified);      // 传递给下一个拦截器}
}

Spring Security Filter Chain

Spring Security 通过一串过滤器实现安全控制,每个 Filter 专管一个安全切面--CSRF、认证、授权、异常翻译各司其职。SecurityFilterChain 默认包含十几个 Filter,自定义 Filter 通过 addFilterBefore/addFilterAfter 插到指定位置,不用关心其他 Filter 的存在。遇到 JWT 认证时,自定义一个解析 token、塞入 SecurityContext 的 Filter 挂进链里即可。

public class JwtAuthFilter extends OncePerRequestFilter {protected void doFilterInternal(HttpServletRequest request,HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {String token = resolveToken(request);if (token != null && tokenProvider.validateToken(token)) {Authentication auth = tokenProvider.getAuthentication(token);SecurityContextHolder.getContext().setAuthentication(auth);}filterChain.doFilter(request, response);  // 继续传递}
}

MyBatis Plugin

MyBatis 的插件机制本质是一条由动态代理层层包裹出的责任链。InterceptorChain.pluginAll() 遍历所有插件,逐个用 Plugin.wrap() 生成代理包裹目标对象,四大核心对象(ExecutorStatementHandlerParameterHandlerResultSetHandler)都能被拦截。插件用 @Intercepts 注解声明要拦截哪个接口的哪个方法,Interceptor.intercept() 拦截后调 invocation.proceed() 把调用传给下一层代理。

@Intercepts({@Signature(type = Executor.class, method = "query",args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class PageInterceptor implements Interceptor {public Object intercept(Invocation invocation) throws Throwable {// 拦截查询,改写 SQL 加分页return invocation.proceed();   // 传给下一层代理(最内层是真实 Executor)}
}

⚠️ MyBatis 插件结构上更像装饰模式:层层代理增强同一个目标对象,每个插件都参与。但它又具备责任链特征,多个插件按注册顺序串联,可中途拦截、可 proceed 放行。因此多数资料把它归为责任链,实际是责任链与装饰模式的混合体。

Netty ChannelPipeline

Netty 的 ChannelPipeline 是责任链在网络框架中的高级应用,每个 ChannelHandler 处理入站或出站事件。它的特殊之处是双向链--入站事件(读数据)从 head 向 tail 传播,出站事件(写数据)从 tail 向 head 传播。ChannelInboundHandler 管入站,ChannelOutboundHandler 管出站,每个 Handler 调用 ctx.fireChannelRead(msg) 把事件传给下一个。编解码、心跳、业务处理全都挂在同一条 pipeline 上。

ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("decoder", new StringDecoder());   // 入站:解码
pipeline.addLast("encoder", new StringEncoder());   // 出站:编码
pipeline.addLast("handler", businessHandler);       // 入站:业务处理

异常处理链

异常沿调用栈传播,本质是一条隐式的责任链--每层 catch 决定处理还是继续抛出。能处理的层就地消化,处理不了的就 throw 给上层,直到遇到能处理的层或到达顶层导致程序中止。这也是纯责任链的特征:请求(异常)被一个处理者接收后不再传递。

try {doBusiness();                              // 业务调用
} catch (BusinessException e) {log.warn("业务异常,已处理", e);             // 本层能处理,消化掉
} catch (SQLException e) {throw new SystemException(e);               // 本层处理不了,转换后抛给上层
}

技术交流 & 更多原创内容,关注公众号:咖啡八杯