什么是责任链模式?应用场景有哪些?


面试考察点

  1. 设计思想理解:面试官不仅仅是想知道你能背出责任链的定义,更是想知道你是否理解 "把多个处理者串成一条链,请求沿着链传递,直到有人处理" 这个核心思想,以及它解决了什么问题。

  2. 实战编码能力:能否手写一个责任链,能否讲清楚链的构建方式和传递逻辑。有些面试官会让你现场写一个简单的责任链。

  3. 框架源码关联:能否把责任链和 Servlet Filter、Spring Interceptor、MyBatis Plugin 等框架实现联系起来,这体现了你的源码阅读深度。

核心答案

一句话定义:责任链模式将多个处理器串成一条链,请求沿着链依次传递,每个处理器决定是自己处理、传递给下一个处理器、还是两者都做。发送请求的一方不需要知道谁会最终处理,实现了请求发送者和接收者的解耦。

生活中的例子:公司请假审批流程——3 天以内组长批,3~7 天经理批,7 天以上总监批。你提交请假申请后,请求会沿着 "组长 → 经理 → 总监" 这条链自动流转,你不需要关心最终是谁批的。

上面的图展示了责任链的基本流转。核心思路就是:

  • 链式结构:每个处理器持有下一个处理器的引用,形成一条链
  • 自动传递:请求从链头开始,沿链依次传递
  • 自主决策:每个处理器自行决定是处理、传递还是终止

深度解析

一、手写责任链

先用一个完整的代码示例把责任链的核心结构搞明白。

// 抽象处理器
public abstract class Handler {

    protected Handler next; // 持有下一个处理器的引用

    public Handler setNext(Handler next) {
        this.next = next;
        return next; // 返回下一个处理器,方便链式调用
    }

    public abstract void handle(String request);
}

// 具体处理器 A:组长
public class GroupLeaderHandler extends Handler {
    @Override
    public void handle(String request) {
        System.out.println("组长收到请求:" + request);
        if (request.contains("3 天以内")) {
            System.out.println("→ 组长审批通过 ✓");
        } else {
            System.out.println("→ 组长权限不够,转交给上级...");
            if (next != null) {
                next.handle(request);
            }
        }
    }
}

// 具体处理器 B:经理
public class ManagerHandler extends Handler {
    @Override
    public void handle(String request) {
        System.out.println("经理收到请求:" + request);
        if (request.contains("7 天以内")) {
            System.out.println("→ 经理审批通过 ✓");
        } else {
            System.out.println("→ 经理权限不够,转交给上级...");
            if (next != null) {
                next.handle(request);
            }
        }
    }
}

// 具体处理器 C:总监
public class DirectorHandler extends Handler {
    @Override
    public void handle(String request) {
        System.out.println("总监收到请求:" + request);
        System.out.println("→ 总监审批通过 ✓");
    }
}

组装链条并使用:

// 构建责任链
Handler groupLeader = new GroupLeaderHandler();
Handler manager = new ManagerHandler();
Handler director = new DirectorHandler();

groupLeader.setNext(manager).setNext(director);

// 测试
groupLeader.handle("请假 2 天,回老家");
System.out.println("-----------");
groupLeader.handle("请假 5 天,去旅游");
System.out.println("-----------");
groupLeader.handle("请假 10 天,休年假");

输出:

组长收到请求:请假 2 天,回老家
→ 组长审批通过 ✓
-----------
组长收到请求:请假 5 天,去旅游
→ 组长权限不够,转交给上级...
经理收到请求:请假 5 天,去旅游
→ 经理审批通过 ✓
-----------
组长收到请求:请假 10 天,休年假
→ 组长权限不够,转交给上级...
经理收到请求:请假 10 天,休年假
→ 经理权限不够,转交给上级...
总监收到请求:请假 10 天,休年假
→ 总监审批通过 ✓

代码结构非常清晰:每个处理器只关心自己能不能处理,能就处理,不能就传给下一个。新增处理器只需要创建一个类,然后插入链中即可,完全不影响已有的处理器代码。这就是责任链模式的核心优势——开闭原则。

二、Servlet Filter 中的责任链

说完了基础写法,来看看真实框架怎么用的。Servlet 的 Filter 就是最经典的责任链实现。

public class MyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        System.out.println("请求到达,前置处理...");

        chain.doFilter(request, response); // 传给下一个 Filter

        System.out.println("响应返回,后置处理...");
    }
}

它的流转方式和纯责任链略有不同——FilterChain 不只是单向传递,而是像一个管道:

┌───────────────────────────────────────────────────────┐
│           Servlet Filter 责任链(管道模型)            │
├───────────────────────────────────────────────────────┤
│                                                       │
│   Request ─►                                          │
│       ┌──────────┐  ┌──────────┐  ┌──────────┐      │
│       │ Filter A  │─►│ Filter B  │─►│ Filter C  │──► Servlet │
│       └──────────┘  └──────────┘  └──────────┘      │
│           ◄─          ◄─            ◄─               │
│       ┌──────────┐  ┌──────────┐  ┌──────────┐      │
│       │ Filter A  │◄─│ Filter B  │◄─│ Filter C  │◄── Response  │
│       └──────────┘  └──────────┘  └──────────┘      │
│                                                       │
│   每个 Filter 的 chain.doFilter() 把请求传给下一个,  │
│   执行完后续逻辑后,响应按相反顺序依次返回             │
│                                                       │
└───────────────────────────────────────────────────────┘

Servlet Filter 采用了双向传递的管道模型:

  • 请求阶段Filter A → Filter B → Filter C → Servlet,每个 Filter 在 chain.doFilter() 之前的逻辑按顺序执行
  • 响应阶段Filter C → Filter B → Filter A,每个 Filter 在 chain.doFilter() 之后的逻辑按逆序执行

这种设计特别适合统一处理编码转换、登录校验、日志记录等横切关注点。

三、Spring Interceptor 中的责任链

Spring MVC 的 HandlerInterceptor 也是责任链,但接口设计更清晰:

public class AuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) {
        // 前置处理:返回 true 继续传递,返回 false 终止链
        String token = request.getHeader("Authorization");
        if (token == null) {
            response.setStatus(401);
            return false; // 终止传递
        }
        return true; // 继续传给下一个 Interceptor
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler, ModelAndView modelAndView) {
        // 后置处理
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex) {
        // 完成后处理(无论是否异常都会执行)
    }
}

preHandle() 返回 false 就能直接截断整条链,这个设计比 Servlet Filter 更直观。

四、责任链 vs 装饰器 vs 拦截器

面试时可能会被问到这几者的区别,简单对比一下:

对比项责任链模式装饰器模式拦截器
核心目的分离请求发送者和处理者动态增强对象功能在流程前后插入逻辑
链路特点可中断、可跳过不中断,逐层增强可中断
典型场景审批流程、过滤器链IO 流包装、BufferedInputStreamSpring Interceptor
关注点"谁该处理这个请求""如何增强这个对象""流程执行前后做什么"

其实责任链和装饰器在代码结构上很像(都是链式调用),区别在于意图——责任链关注的是 "谁能处理",装饰器关注的是 "怎么增强"。

五、实际应用场景

1. 权限校验链

多个权限规则串成一条链,请求进来后依次经过:登录校验 → 角色校验 → 权限校验,任何一环不通过直接拒绝。

2. 日志 / 审计链

请求进来后依次经过:操作日志记录 → 敏感数据脱敏 → 审计信息记录。

3. 参数校验链

表单提交的数据依次经过:非空校验 → 格式校验 → 业务规则校验 → 数据库唯一性校验。

4. 框架中的责任链

框架责任链实现作用
ServletFilter + FilterChain编码、登录、日志等
Spring MVCHandlerInterceptor权限、日志、性能监控
MyBatisInterceptor(Plugin)分页、SQL 打印、慢查询监控
DubboFilter监控、限流、负载均衡
NettyChannelPipeline + ChannelHandler编解码、业务处理

面试高频追问

  1. 责任链模式和装饰器模式有什么区别?

    • 责任链关注 "谁来处理",可以中断或跳过;装饰器关注 "怎么增强",不会中断链路。代码结构相似,设计意图不同。
  2. 如何动态增删责任链中的处理器?

    • 用一个 List<Handler> 维护处理器列表,运行时动态增删。很多框架(如 Spring 的 InterceptorRegistry)就是这么做的。
  3. 责任链会不会导致性能问题?

    • 链太长确实会影响性能,每个请求都要经过所有处理器。生产环境要注意控制链的长度,避免不必要的处理器。也可以用短路机制(处理完即停)来优化。
  4. Servlet Filter 和 Spring Interceptor 的区别?

    • Filter 是 Servlet 规范层面的,作用范围是所有请求(包括静态资源);Interceptor 是 Spring MVC 层面的,只拦截 Controller 方法。执行顺序是 Filter → Interceptor → Controller。

常见面试变体

  • "手写一个责任链模式"
  • "Servlet Filter 的底层原理是什么?"
  • "Spring 拦截器和过滤器的区别?"
  • "你项目中哪里用到了责任链模式?"

记忆口诀

核心思想:一链串多手,各管各的活,能干自己干,干不了往下传。

应用场景:审批流程、过滤器链、参数校验、权限校验。

与装饰器的区别:责任链问 "谁来处理",装饰器问 "怎么增强"。

总结

责任链模式的精髓就是把多个处理器串成链,请求沿链传递,每个处理器自主决定处理或转发。面试时先说概念,再手写一个简单实现,然后结合 Servlet Filter 和 Spring Interceptor 讲框架中的应用,最后聊一下和装饰器模式的区别。这条线下来,面试官对你的设计模式功底基本就心里有数了。