21、责任链模式:请求传递、FilterChain与拦截器
责任链模式,说白了就是让多个对象都有机会处理同一个请求。
把这些对象串成一条链,请求沿着链传递,直到某个对象处理它为止。
我刚开始接触这个模式时,觉得它不就是个链表嘛。后来在项目中用多了,才发现它的设计思想其实很巧妙。
什么是责任链模式
责任链模式是一种行为型设计模式。它允许你将请求的发送者和接收者解耦。
具体做法是:让多个接收者对象组成一条链,请求沿着这条链传递,直到被处理。
核心角色有三个:
- Handler(抽象处理者):定义处理请求的接口,并持有下一个处理者的引用
- ConcreteHandler(具体处理者):实现处理逻辑,决定是否处理或转发
- Client(客户端):构建链并发送请求
关键点:每个处理者只关心自己能不能处理。处理不了,就丢给下一个。这种「各扫门前雪」的设计,让代码变得非常干净。
一个简单的例子:请假审批
我记得刚带团队时,有个同事问我:「请假流程怎么设计比较灵活?」
我直接说:「用责任链啊。」
你看,请假审批天然就是一条链:组长批3天以内,经理批7天以内,总监批15天以内,超过15天得老板批。
// 抽象处理者
public abstract class Approver {
protected Approver next;
protected String name;
public Approver(String name) {
this.name = name;
}
public void setNext(Approver next) {
this.next = next;
}
public abstract void approve(int days);
}
// 具体处理者:组长
public class GroupLeader extends Approver {
public GroupLeader(String name) {
super(name);
}
@Override
public void approve(int days) {
if (days <= 3) {
System.out.println(name + " 审批通过,请假 " + days + " 天");
} else if (next != null) {
next.approve(days);
}
}
}
// 具体处理者:经理
public class Manager extends Approver {
public Manager(String name) {
super(name);
}
@Override
public void approve(int days) {
if (days <= 7) {
System.out.println(name + " 审批通过,请假 " + days + " 天");
} else if (next != null) {
next.approve(days);
}
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Approver groupLeader = new GroupLeader("张三");
Approver manager = new Manager("李四");
Approver director = new Director("王五");
groupLeader.setNext(manager);
manager.setNext(director);
groupLeader.approve(2); // 组长批
groupLeader.approve(5); // 经理批
groupLeader.approve(10); // 总监批
}
}
你看,代码结构非常清晰。每个处理者只判断自己能不能处理,不能就丢给下一个。
我曾经在一个项目中,把审批流程从硬编码的if-else改成了责任链,代码量直接减少了一半。
责任链在框架中的应用
其实你每天都在用责任链模式,只是没意识到。
1. FilterChain(过滤器链)
Java Web中的Filter就是典型的责任链。每个Filter处理请求,然后调用chain.doFilter()把请求传给下一个。
public class LogFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("记录日志:" + request.getRemoteAddr());
chain.doFilter(request, response); // 传给下一个
}
}
我做过一个电商项目,用Filter链实现了:鉴权→日志→参数校验→业务处理。每个Filter只做一件事,维护起来特别爽。
2. 拦截器(Interceptor)
Spring MVC的拦截器也是责任链。HandlerInterceptor有三个方法:preHandle、postHandle、afterCompletion。
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String token = request.getHeader("Authorization");
if (token == null || !token.startsWith("Bearer ")) {
response.setStatus(401);
return false; // 中断链
}
return true; // 继续传递
}
}
嗯,这里要注意:拦截器的preHandle返回false会中断链,后面的拦截器和处理器都不会执行。这个细节我踩过坑。
责任链的两种实现方式
| 实现方式 | 特点 | 适用场景 |
|---|---|---|
| 链表式 | 每个节点持有下一个节点的引用 | 处理者数量固定,顺序明确 |
| 集合式 | 用List或数组存储所有处理者 | 处理者动态增减,顺序可配置 |
我个人习惯用集合式。为什么呢?因为在实际项目中,处理者经常需要动态调整。用集合式,加一个处理者就是list.add(),删一个就是list.remove(),非常灵活。
// 集合式责任链
public class FilterChain {
private List<Filter> filters = new ArrayList<>();
private int index = 0;
public void addFilter(Filter filter) {
filters.add(filter);
}
public void doFilter(Request request, Response response) {
if (index < filters.size()) {
Filter filter = filters.get(index++);
filter.doFilter(request, response, this);
}
}
}
避坑指南
我曾经犯过的错:
- 链太长导致性能问题:有一次我设计了20多个处理者,请求处理时间从5ms飙升到200ms。后来发现大部分请求在前3个处理者就被处理了,后面的根本用不上。解决方案:把高频处理者往前放。
- 忘记调用next:在Filter中忘记调用chain.doFilter(),请求就断了。这个bug排查了我一下午。
- 处理者顺序搞错:鉴权应该在日志之前,否则日志里会记录未鉴权的请求。顺序错了,逻辑就乱了。
我的建议:
- 处理者数量控制在5-8个以内,超过考虑合并或重构
- 每个处理者只做一件事,遵循单一职责原则
- 用配置驱动处理者的顺序,不要硬编码
- 考虑使用责任链的变体:拦截器模式、管道模式
责任链模式的核心流程图
下面这张图展示了责任链模式的完整流程。请求从客户端发出,沿着链依次经过每个处理者,直到被处理或到达链尾。
总结
责任链模式的核心思想就一句话:让多个对象都有机会处理请求,避免请求发送者和接收者之间的耦合。
你想想看,如果没有责任链,我们得写多少if-else?
if (组长能处理) { 组长处理 } else if (经理能处理) { 经理处理 } else if ...
这种代码,改一次需求就得动整个方法,维护成本极高。
而责任链模式,每个处理者独立,加一个处理者就是加一个类,改一个处理者不影响其他。这才是面向对象设计的精髓。
一句话记住责任链:请求沿着链传递,直到有人接盘。