欢迎光临
我们一直在努力

走进 Gang of Four 设计模式:责任链模式

走进 Gang of Four 设计模式:责任链模式

说明:不仅告诉你"怎么用",更告诉你"为什么这样设计"

前置知识:Java 面向对象基础(抽象类、多态、链表结构)


📊 设计模式分类总览

GoF 23 种设计模式可按两个维度交叉分类:类/对象(处理方式) × 创建型/结构型/行为型(目的)。

维度创建型(Creational)结构型(Structural)行为型(Behavioral)
类(Class)(通过继承复用) Factory Method工厂方法 Adapter(类适配器) Interpreter(解释器)Template Method(模板方法)
对象(Object)(通过组合/聚合复用) Abstract Factory(抽象工厂)Builder(建造者)Prototype(原型)Singleton(单例) Adapter(对象适配器)Bridge(桥接)Composite(组合)Decorator(装饰)Facade(外观)Flyweight(享元)Proxy(代理) Chain of Resp.(责任链)Command(命令)Iterator(迭代器)Mediator(中介者)Memento(备忘录)Observer(观察者)State(状态)Strategy(策略)Visitor(访问者)

📑 目录

  • 模式概述 · 15 问深度分析
  • 框架源码实战分析
    • Servlet Filter 责任链源码拆解
    • MyBatis InterceptorChain 源码拆解
    • Spring Security FilterChain 源码拆解
  • 深度追问:Why / How / Trade-off / Evolution / Modern Practice
  • 总结

  • 1. 模式概述 · 15 问深度分析

    Q1: 为什么需要这个模式?它解决了什么问题?

    在软件开发中,经常会出现一个请求需要经过多个环节处理的场景(例如:权限校验 → 数据脱敏 → 日志记录 → 业务核心处理)。

    如果让调用者直接知道所有处理节点,调用者就必须耦合所有的处理逻辑和流转规则。责任链模式正是为了解决请求发送者与多个请求处理者之间的紧耦合问题。它将处理者连成一条链,让请求沿着这条链传递,直到有对象处理它为止,或者每个节点都参与处理。

    核心矛盾:"确定性的调用流"与"不确定的处理者"之间的矛盾。

    Q2: 如果不用这个模式,会有什么缺陷?

    不用责任链模式时,通常会采用硬编码的 if-else 或 switch-case 逻辑。

    // ❌ 硬编码——新增处理者需要改源码
    public void handle(Request request) {
    if (request.hasPermission()) {
    // 权限校验通过
    if (request.needsLogging()) {
    log.info("处理请求: " + request);
    }
    // 核心业务处理
    process(request);
    // 发送通知
    notifyUser(request);
    }
    }

    缺陷:

    • 违反开闭原则:新增或删除一个处理环节时,必须修改核心类的代码。
    • 硬编码流程:处理顺序和逻辑在编译期就固定了,无法在运行时动态调整。
    • 代码臃肿:一个方法中充斥着多个处理阶段,难以维护和测试。

    Q3: 核心思想是什么?一句话如何概括?

    • 核心思想:将请求沿着处理者链传递,每个处理者决定是否处理或传递给下一个。
    • 一句话概括:为请求创建一条接收者对象的链,沿着这条链传递请求,直到有一个处理者处理它。

    Q4: 包含哪些角色?每个角色的职责是什么?

    责任链模式包含以下 3 个核心角色:

  • Handler(抽象处理者):定义处理请求的接口,通常包含一个处理方法和一个指向下一个处理者的引用。
  • ConcreteHandler(具体处理者):实现 Handler 接口,判断是否能处理当前请求。如果能则自己处理,如果不能则调用 nextHandler.handle() 传递给下一个。
  • Client(客户端):组装责任链,向链头发送请求。
  • Q5: 它们之间如何协作?调用流程是怎样的?

    标准调用流程:

  • 组装阶段:客户端将处理者 A、B、C 串联成链:A → B → C。
  • 请求发起:客户端向链头(A)发送请求。
  • 流转处理:
    • A 处理或传递给 B
    • B 处理或传递给 C
    • C 处理或终止
  • 结果返回:请求被某节点处理后返回结果,或到达链尾未被处理。
  • Q6: 为什么要这样设计?每个角色存在的意义是什么?

    • Handler 的意义:定义了统一的处理接口,让客户端无需关心链中的具体处理者。
    • ConcreteHandler 的意义:每个处理者只关心自己能处理的那部分逻辑,实现了关注点分离。
    • Client 的意义:负责组装链——可以在运行时动态决定链的组成和顺序。

    Q7: 为什么使用接口、抽象类、组合,而不是其他方式?

    • 为什么用抽象类? 因为 Handler 需要持有下一个处理者的引用,并且通常有一个默认的转发逻辑(if (next != null) next.handle()),抽象类可以统一实现这个逻辑。
    • 为什么用组合(链表结构)? 链表可以在运行时动态添加、删除或调整处理者的顺序。如果用数组或列表硬编码,灵活性大打折扣。

    Q8: 这种设计符合哪些面向对象原则?

    • 开闭原则(OCP):极佳。新增处理者只需新建一个 ConcreteHandler 并接入链中,无需修改任何现有代码。
    • 单一职责原则(SRP):每个具体处理者只负责一种处理逻辑(如权限校验、日志记录),职责非常单一。
    • 依赖倒置原则(DIP):客户端和具体处理者都依赖抽象的 Handler 接口。

    Q9: 它有哪些优点和局限性?会带来哪些额外成本?

    优点:

    • 降低耦合:请求发送者和接收者之间完全解耦。
    • 动态灵活:可以在运行时动态新增、删除或调整处理者顺序。
    • 符合单一职责:每个处理者只关心自己的逻辑。

    局限性/额外成本:

    • 请求可能不被处理:如果链的末端没有兜底处理者,请求可能被丢弃。
    • 调试困难:运行时调用链不直观,需要跟踪链的流转。
    • 性能影响:请求可能经过多个处理者才被处理,链太长影响性能。

    Q10: 它最适合哪些场景?哪些场景不应该使用?

    最适合的场景:

    • 有多个对象可以处理请求,具体由谁处理由运行时决定。
    • 需要动态指定或调整处理者链的顺序(如按配置决定 Filter 顺序)。
    • 向多个对象中的一个提交请求,不想明确指定接收者。

    不应该使用的场景:

    • 处理者只有 1-2 个且固定不变时,用简单的 if-else 更轻量。
    • 请求的处理路径需要在编译期就严格确定时。

    Q11: 它与容易混淆的其他设计模式有什么区别和联系?

    与装饰器模式的区别:

    对比维度责任链模式装饰器模式
    请求流向 链式传递,可能由链中某个节点终止 层层包装,每个装饰器都执行
    处理方式 请求最多被一个处理者处理(或全部处理) 每个装饰器都增强功能
    结构 链表结构 嵌套结构
    典型应用 FilterChain、MyBatis Plugin Java IO Stream

    与观察者模式的区别:

    • 观察者模式是 Subject 主动广播给所有 Observer(一对多)。
    • 责任链模式是请求沿着链传递,直到有 Handler 处理它(一对一)。

    Q12: 框架中有哪些典型应用?

    • Tomcat / Servlet 中的 FilterChain:在请求到达 Servlet 之前,经过一系列 Filter,层层过滤 Request/Response。
    • MyBatis 插件机制(InterceptorChain):多个 MyBatis 插件构成责任链,依次拦截 SQL 执行。
    • Spring Security 中的 SecurityFilterChain:一组安全过滤器链,按顺序对 HTTP 请求进行安全处理。
    • Netty 的 ChannelPipeline:出站/入站处理器的责任链。

    Q13: 如何从零实现一个最小可运行版本?

    以日志级别过滤器为例:

    // 1. Handler 抽象处理者
    public abstract class AbstractLogger {
    public static int INFO = 1;
    public static int DEBUG = 2;
    public static int ERROR = 3;

    protected int level;
    protected AbstractLogger nextLogger; // 指向下一个处理者

    public void setNextLogger(AbstractLogger nextLogger) {
    this.nextLogger = nextLogger;
    }

    public void logMessage(int level, String message) {
    if (this.level <= level) {
    write(message);
    }
    if (nextLogger != null) {
    nextLogger.logMessage(level, message); // 传递给下一个
    }
    }

    abstract protected void write(String message);
    }

    // 2. ConcreteHandler 具体处理者
    public class ConsoleLogger extends AbstractLogger {
    public ConsoleLogger(int level) { this.level = level; }

    @Override
    protected void write(String message) {
    System.out.println("Standard Console::Logger: " + message);
    }
    }

    public class FileLogger extends AbstractLogger {
    public FileLogger(int level) { this.level = level; }

    @Override
    protected void write(String message) {
    System.out.println("File::Logger: " + message);
    }
    }

    public class ErrorLogger extends AbstractLogger {
    public ErrorLogger(int level) { this.level = level; }

    @Override
    protected void write(String message) {
    System.out.println("Error Console::Logger: " + message);
    }
    }

    // 3. Client 组装链
    public class ChainPatternDemo {
    private static AbstractLogger getChainOfLoggers() {
    AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
    AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
    AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);

    errorLogger.setNextLogger(fileLogger);
    fileLogger.setNextLogger(consoleLogger);

    return errorLogger; // 返回链头
    }

    public static void main(String[] args) {
    AbstractLogger loggerChain = getChainOfLoggers();

    loggerChain.logMessage(AbstractLogger.INFO, "This is an information.");
    loggerChain.logMessage(AbstractLogger.DEBUG, "This is a debug level information.");
    loggerChain.logMessage(AbstractLogger.ERROR, "This is an error information.");
    }
    }

    Q14: 如何在现有项目中识别可以使用该模式的地方?

    如果出现以下信号,可以考虑重构为责任链模式:

  • 代码中频繁出现链式的 if-else if-else if 或 switch,且每个分支的处理逻辑不同。
  • 一个方法中包含了多个阶段的处理(权限校验 → 日志 → 核心业务 → 通知),且阶段可能经常增减。
  • 需要根据配置或运行时条件动态决定请求的处理路径。
  • Q15: 如果重新设计这个模式,在今天会有哪些改进?

    在现代 Java(Java 8+)中,函数式接口(Function、Consumer)可以替代部分责任链节点,消除处理者类的数量:

    // 函数式责任链
    public class FunctionalChain {
    private final List<Consumer<Request>> handlers = new ArrayList<>();

    public FunctionalChain addHandler(Consumer<Request> handler) {
    handlers.add(handler);
    return this;
    }

    public void execute(Request request) {
    for (Consumer<Request> handler : handlers) {
    handler.accept(request);
    }
    }
    }

    // 使用时无需创建 Handler 子类
    new FunctionalChain()
    .addHandler(req -> System.out.println("权限校验"))
    .addHandler(req -> System.out.println("日志记录"))
    .addHandler(req -> System.out.println("核心业务"))
    .execute(request);


    2. 框架源码实战分析

    2.1 Servlet FilterChain 源码拆解

    Servlet 的 FilterChain 是责任链模式最典型的工业级应用(结合了模板方法模式)。

    场景背景:在 HTTP 请求到达 Servlet 之前,需要经过一系列 Filter(如编码过滤、登录校验、XSS 防护)。这些 Filter 的顺序通常由 web.xml 或注解配置决定,可以在运行时动态调整。

    核心源码:

    // Tomcat 中 FilterChain 的核心实现
    public class ApplicationFilterChain implements FilterChain {
    // 内部维护过滤器数组
    private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
    private int pos = 0; // 当前执行位置(指针)
    private Servlet servlet; // 目标 Servlet

    @Override
    public void doFilter(ServletRequest request, ServletResponse response) {
    if (pos < filters.length) {
    // 取出当前过滤器(按顺序)
    ApplicationFilterConfig filterConfig = filters[pos++];
    Filter filter = filterConfig.getFilter();

    // 将自身(this)作为 FilterChain 传入
    // 这样在 Filter 中可以调用 chain.doFilter() 继续传递
    filter.doFilter(request, response, this);
    } else {
    // 所有过滤器执行完毕,调用目标 Servlet
    servlet.service(request, response);
    }
    }
    }

    // 开发者自定义 Filter
    @WebFilter("/*")
    public class LoggingFilter implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response,
    FilterChain chain) throws IOException, ServletException {
    // 前置处理
    System.out.println("请求前 – " + System.currentTimeMillis());

    // 调用下一个 Filter(关键!)
    chain.doFilter(request, response);

    // 后置处理
    System.out.println("请求后 – " + System.currentTimeMillis());
    }
    }

    为什么这样设计? 每个 Filter 通过 chain.doFilter() 将控制权交回给 ApplicationFilterChain,chain 再调用下一个 Filter 或最终的 Servlet。这种设计实现了"过滤器链"的职责链模式,同时每个 Filter 可以在调用前后添加逻辑,又体现了模板方法模式。

    2.2 MyBatis InterceptorChain 源码拆解

    MyBatis 的插件机制本质上是责任链模式的应用。

    场景背景:MyBatis 允许开发者编写插件来拦截四大核心对象的方法调用(Executor、StatementHandler、ParameterHandler、ResultSetHandler)。多个插件构成一条责任链,依次执行拦截逻辑。

    核心源码:

    // MyBatis 中的拦截器链
    public class InterceptorChain {
    private final List<Interceptor> interceptors = new ArrayList<>();

    // 注册拦截器
    public Object pluginAll(Object target) {
    // 遍历所有拦截器,依次包装目标对象
    // 注意:这是责任链模式的一种变体——先注册的在内层
    for (Interceptor interceptor : interceptors) {
    target = interceptor.plugin(target);
    }
    return target;
    }

    public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
    }
    }

    // 自定义 MyBatis 插件
    @Intercepts({
    @Signature(type = Executor.class, method = "query",
    args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
    })
    public class ExamplePlugin implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
    // 前置处理
    System.out.println("SQL 执行前");

    // 调用下一个拦截器或目标方法
    Object result = invocation.proceed();

    // 后置处理
    System.out.println("SQL 执行后");
    return result;
    }

    @Override
    public Object plugin(Object target) {
    // 用 Plugin.wrap 来生成代理
    return Plugin.wrap(target, this);
    }
    }

    2.3 Spring Security FilterChain 源码拆解

    Spring Security 的安全过滤机制是责任链模式在企业级安全框架中的顶级应用。

    场景背景:Spring Security 通过一组 SecurityFilterChain 来保护 Web 应用。每个 FilterChain 包含多个安全过滤器(如 UsernamePasswordAuthenticationFilter、ExceptionTranslationFilter、FilterSecurityInterceptor),按照特定顺序执行。

    核心源码:

    // Spring Security 的过滤器链管理器
    public class FilterChainProxy extends GenericFilterBean {
    // 持有多个 SecurityFilterChain(责任链数组)
    private List<SecurityFilterChain> filterChains;

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
    FilterChain chain) throws IOException, ServletException {
    // 遍历所有 SecurityFilterChain,找到匹配当前请求的第一个
    for (SecurityFilterChain filterChain : filterChains) {
    if (filterChain.matches(request)) {
    // 执行匹配的 SecurityFilterChain 中的所有 Filter
    filterChain.getFilters().doFilter(request, response, chain);
    return;
    }
    }
    // 没有匹配的 chain,直接放行
    chain.doFilter(request, response);
    }
    }

    // 配置多个安全过滤链
    @Configuration
    @EnableWebSecurity
    public class SecurityConfig {
    @Bean
    public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
    http
    .securityMatcher("/api/**") // 只匹配 /api/ 路径
    .authorizeHttpRequests(auth -> auth
    .anyRequest().authenticated()
    )
    .httpBasic(Customizer.withDefaults());
    return http.build();
    }

    @Bean
    public SecurityFilterChain webFilterChain(HttpSecurity http) throws Exception {
    http
    .securityMatcher("/web/**") // 匹配 /web/ 路径
    .authorizeHttpRequests(auth -> auth
    .requestMatchers("/login").permitAll()
    .anyRequest().authenticated()
    )
    .formLogin(Customizer.withDefaults());
    return http.build();
    }
    }

    三个框架对比:

    框架组装方式传递机制终止条件
    Servlet Filter web.xml/注解顺序 chain.doFilter() 手动传递 全部执行完 → Servlet
    MyBatis Plugin interceptors.add() 注册 invocation.proceed() 内层外层的包装 全部执行完 → 目标方法
    Spring Security 代码配置 chain 顺序 自动匹配第一个 chain 后执行 匹配到的 chain 执行完或放行

    3. 深度追问

    3.1 Why(为什么):解决哪个根本矛盾?

    责任链模式解决的根本矛盾是:"确定性的调用流"与"不确定的处理者"之间的矛盾。

    在复杂的业务系统中,请求需要经过多个处理环节,但哪些处理环节是必需的、它们的顺序如何,这些信息在编译期往往无法确定。如果使用硬编码的 if-else,每次调整处理环节都需要修改核心代码。责任链模式通过将处理者串联成链,将"谁处理"的决定权从编译期延后到运行期。

    3.2 How(怎么做):对象关系与交互机制

    责任链模式的核心机制可以总结为:隐式配置、多态传递、控制权让渡。

    • 隐式配置:链的组装在客户端完成,Handler 不知道链的全貌。
    • 多态传递:每个 Handler 持有抽象 Handler 的引用,调用 next.handle() 时利用多态。
    • 控制权让渡:每个 Handler 执行完毕后,可以决定是终止处理还是继续传递。

    典型的链式调用伪代码:

    abstract class Handler {
    protected Handler next;
    public void handle(Request req) {
    if (canHandle(req)) {
    doHandle(req);
    } else if (next != null) {
    next.handle(req); // 控制权让渡
    }
    }
    }

    3.3 Trade-off(代价):牺牲了什么换取什么?

    它牺牲了(付出代价):

    • 请求处理的确定性:请求可能经过整条链都没有处理者能处理(如果没有兜底处理者)。
    • 调试的直观性:运行时调用链不直观,需要跟踪 next.handle() 的传递过程。
    • 性能损耗:链越长,请求经过的处理者越多,性能损耗越大。

    它换取了(获得收益):

    • 处理者之间的完全解耦:每个处理者只关心自己的逻辑,不知道其他处理者的存在。
    • 运行时动态调整:可以随意新增、删除或重排处理者。
    • 单一职责:每个处理者的职责非常单一清晰。

    3.4 Evolution(演化):它是如何从简单设计逐步演化而来的?

  • 硬编码 if-else(起点):所有处理逻辑写在一个方法里,最直接但也最混乱。
  • 策略模式:动态选择单个处理者,但只能选一个,不能链式传递。
  • 责任链模式(经典):链式传递,多个处理者依次尝试直到有人处理。
  • 责任链 + 模板方法(现代变体):如 Servlet Filter,在链式传递的基础上,每个 Filter 可以通过 chain.doFilter() 在前后添加逻辑,结合了模板方法模式。
  • 3.5 Modern Practice(现代实践):它在今天被取代了吗?

    结论:责任链模式不仅没有被取代,反而在现代框架中成为了核心基础设施。

    • Servlet FilterChain 仍然是 Java Web 应用的基础。
    • Spring Security 的 SecurityFilterChain 是安全框架的基石。
    • MyBatis Plugin 基于责任链实现插件机制。
    • Netty 的 ChannelPipeline 是高性能网络框架的核心。

    在函数式编程中,可以使用 Function 链式组合实现轻量级责任链:

    Function<Request, Response> handler = request
    .andThen(this::validate)
    .andThen(this::process)
    .andThen(this::respond);

    但这种方式的灵活性不如经典责任链模式(不能中断传递),说明经典责任链在处理"有条件的传递"时仍有不可替代的优势。


    4. 总结

    核心要点

    • 责任链模式将请求沿着处理者链传递,直到有处理者处理它
    • 核心结构是链表:每个处理者持有下一个处理者的引用
    • 与装饰器的区别:责任链是链式传递(可能终止),装饰器是层层嵌套(全部执行)
    • 典型工业应用:Servlet Filter、MyBatis Interceptor、Spring Security FilterChain、Netty ChannelPipeline

    一句话

    责任链模式就是"击鼓传花"——请求沿着链传递,直到有人能处理它。


    5. 参考文献

    [1] GAMMA E, HELM R, JOHNSON R, et al. Design Patterns: Elements of Reusable Object-Oriented Software[M]. Boston: Addison-Wesley, 1994. [2] Apache Tomcat. Tomcat FilterChain Source[EB/OL]. https://tomcat.apache.org/, 2024. [3] MyBatis. MyBatis 3 Plugin[EB/OL]. https://mybatis.org/mybatis-3/configuration.html#plugins, 2024. [4] SPRING. Spring Security Architecture[EB/OL]. https://docs.spring.io/spring-security/, 2024.

    赞(0)
    未经允许不得转载:171主机测评 » 走进 Gang of Four 设计模式:责任链模式
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址