责任链模式在 Java 和 Spring 中有哪些典型应用?
简化版
责任链模式在 Java 和 Spring 体系里非常常见,典型例子包括 Servlet FilterChain、Spring MVC HandlerInterceptor、Spring Security FilterChain、Netty ChannelPipeline、MyBatis 插件链等。它们都体现了“请求经过多个处理节点,节点可增强、放行或拦截”的思想。
详细版
常见应用可以这样回答:
- Servlet FilterChain:请求进入 Web 容器后,依次经过多个 Filter,Filter 可以处理请求、调用
chain.doFilter放行,也可以直接返回; - Spring MVC HandlerInterceptor:请求执行 Controller 前后经过拦截器,
preHandle可以决定是否继续; - Spring Security FilterChain:认证、授权、异常处理、会话管理等由一组安全过滤器协作完成;
- Netty ChannelPipeline:入站和出站事件沿 Pipeline 传播,每个 Handler 处理自己关心的事件;
- MyBatis 插件机制:多个 Interceptor 可以对执行器、语句处理器等对象进行增强。
这些例子不一定都完全等同于经典 GoF 责任链结构,但都体现了责任链的核心思想:调用方不直接依赖所有处理器,请求或事件沿处理器序列传递。
完整版教学
一、Servlet FilterChain:最经典的 Web 过滤器链
Servlet FilterChain 是责任链模式在 Java Web 中非常典型的应用。
Filter 的核心写法是:
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain) {
// 前置处理
chain.doFilter(request, response);
// 后置处理
}
如果 Filter 调用了 chain.doFilter,请求继续向后传递;如果没有调用,请求就在当前 Filter 被拦截。
这非常符合责任链模式:
- 每个 Filter 是一个处理器;
- FilterChain 是链对象;
- 请求沿 Filter 顺序传递;
- Filter 可以增强、放行或短路。
典型用途包括编码处理、日志记录、鉴权、跨域处理、限流等。
二、Spring MVC HandlerInterceptor:拦截 Controller 调用
Spring MVC 的拦截器也体现了责任链思想。
它通常包含三个方法:
boolean preHandle(...)
void postHandle(...)
void afterCompletion(...)
其中 preHandle 返回 true 表示继续执行后续拦截器或 Controller,返回 false 表示中断请求。
它和 Servlet Filter 的区别是:
- Filter 更靠近 Servlet 容器;
- Interceptor 更靠近 Spring MVC;
- Interceptor 能拿到 Handler 等 MVC 语义;
- Filter 适合通用 Web 处理,Interceptor 适合业务层拦截。
面试时可以用它说明责任链中的“短路”和“后置处理”。
三、Spring Security FilterChain:安全能力的责任链编排
Spring Security 中,一个请求会经过一组安全过滤器。
这些过滤器分别承担不同职责:
- 提取认证信息;
- 建立安全上下文;
- 判断登录状态;
- 处理异常;
- 执行授权判断;
- 处理登出逻辑。
安全逻辑如果全部写在业务 Controller 中,会导致业务代码严重耦合安全细节。FilterChain 把安全能力拆成多个处理节点,让请求在进入业务前完成统一安全处理。
这也是责任链模式很适合横切关注点的原因。
四、Netty ChannelPipeline:事件传播链
Netty 的 ChannelPipeline 也是常见面试点。
它把一组 ChannelHandler 串起来,网络事件在 Pipeline 中传播。入站事件从前往后传播,出站事件可能按相反方向传播。
比如一个请求可能经过:
解码器 -> 业务处理器 -> 编码器
每个 Handler 只处理自己关心的事件,并通过类似 ctx.fireChannelRead(msg) 的方式把事件继续传递。
它和普通责任链相比更复杂,因为它区分入站、出站、事件类型和上下文,但核心仍然是链式处理。
五、MyBatis 插件链:对核心组件做增强
MyBatis 插件机制允许多个 Interceptor 对 Executor、StatementHandler、ParameterHandler、ResultSetHandler 等组件进行拦截增强。
常见用途包括:
- 分页;
- SQL 审计;
- 数据权限;
- 慢 SQL 监控;
- 字段自动填充。
多个插件叠加时,会形成类似链式代理和责任链结合的结构。它不只是纯责任链,也混合了代理模式思想,但从“多个拦截器按顺序参与处理”的角度看,具备责任链特征。
六、回答框架应用题时要注意边界
面试里说框架例子时,不要把所有链式结构都武断说成“标准责任链”。更稳妥的表达是:这些框架体现了责任链模式的思想,具体实现可能同时结合模板方法、代理、装饰器、回调等机制。
这样回答更准确,也能体现你不是在背模式名。
七、不同框架链路的真实语义
Servlet 三个 Filter 的前置顺序为 1→2→3,调用 chain.doFilter 返回后的后置顺序为 3→2→1。
request -> F1 -> F2 -> F3 -> servlet -> F3 -> F2 -> F1
责任链是否正确,不能只看节点都被注册,还要看请求到底经过哪些节点、在哪里停止以及返回阶段如何展开。应把“继续、已处理、拒绝、系统失败”建成可区分的结果,并让日志能够还原一次请求的完整路径。
八、链路顺序、短路与可观测性
| 检查维度 | 应确认的内容 |
|---|---|
| 机制正确性 | MyBatis 插件更偏代理/装饰链,作为责任链例子时要说明多个拦截器依次增强而非典型职责转移。 |
| 适用边界 | Spring Security 可能先由 FilterChainProxy 选择一条 SecurityFilterChain;Netty 入站和出站传播方向也不同,不能只说“都是链”。 |
| 测试证据 | 从框架入口验证前置与后置顺序、短路行为、异常传播及容器定义的排序规则 |
| 工程代价 | 重点测量链长、各节点耗时、最坏路径延迟和短路率,并确认动态装配不会产生重复节点或排序漂移 |
易错点:MyBatis 插件更偏代理/装饰链,作为责任链例子时要说明多个拦截器依次增强而非典型职责转移。
九、常见误区与追问
- 误区:所有 Java 框架的拦截器执行顺序都相同。 Servlet、Spring MVC、Security、Netty 各有自己的排序和回调规则,必须看具体契约。
- 误区:责任链一定会让每个处理器都执行。 纯链可能命中一个节点就结束,不纯链也可能因拒绝、异常或短路提前终止,必须由链契约定义。
- 追问:Filter 不调用 chain.doFilter 会怎样? 后续 Filter 和目标资源都不会执行,这正是短路能力。
- 追问:责任链如何避免请求到链尾无人处理? 提供明确的默认处理器或链尾失败结果,并对未处理请求记录指标,不能静默成功。
- 追问:责任链上线后最少要记录哪些信息? 至少记录链版本、节点顺序、逐节点耗时与决策、短路位置、异常和最终处理结果。
十、加强记忆
责任链在 Java 和 Spring 中最常见的例子是 FilterChain、Interceptor、Security FilterChain、Netty Pipeline 和插件链。它们共同点是:请求或事件经过多个处理节点,节点可以增强、放行或拦截;差异在于每个框架会结合自己的生命周期、上下文和回调机制。