责任链模式:请求传递、链式处理、实战:过滤器链
责任链模式,说白了就是让多个对象都有机会处理同一个请求。我把这些对象串成一条链,请求沿着链传递,直到有某个对象处理它为止。嗯,这有点像公司里的审批流程——你提交一个报销单,组长先看,金额大了转经理,再大了转总监。
我个人习惯把责任链模式看作是「解耦请求发送者和接收者」的利器。发送者不需要知道谁会处理,接收者也不需要知道请求从哪来。每个节点只关心两件事:我能不能处理?不能的话,丢给下一个。
模式结构:三个核心角色
责任链模式的结构其实很简单,就三个角色:
| 角色 | 职责 | 我见过的坑 |
|---|---|---|
| Handler(抽象处理者) | 定义处理请求的接口,持有下一个处理者的引用 | 很多人忘了设置 successor,链断了 |
| ConcreteHandler(具体处理者) | 判断能否处理,能则处理,不能则转发 | 处理逻辑里直接 return,忘了调用 next |
| Client(客户端) | 组装链,发起请求 | 链的顺序搞反了,小请求被大领导处理了 |
核心要点:每个处理者只做自己的判断,不关心上下游是谁。这就是「单一职责」在模式层面的体现。
代码示例:审批流程
先来个简单的例子。假设我们有三级审批:组长能批 1000 以下,经理能批 5000 以下,总监能批 10000 以下。
// 抽象处理者
public abstract class Approver {
protected Approver next;
public void setNext(Approver next) {
this.next = next;
}
public abstract void handleRequest(int amount);
}
// 组长
public class TeamLeader extends Approver {
@Override
public void handleRequest(int amount) {
if (amount <= 1000) {
System.out.println("组长审批通过:" + amount);
} else if (next != null) {
next.handleRequest(amount);
}
}
}
// 经理
public class Manager extends Approver {
@Override
public void handleRequest(int amount) {
if (amount <= 5000) {
System.out.println("经理审批通过:" + amount);
} else if (next != null) {
next.handleRequest(amount);
}
}
}
// 总监
public class Director extends Approver {
@Override
public void handleRequest(int amount) {
if (amount <= 10000) {
System.out.println("总监审批通过:" + amount);
} else {
System.out.println("金额过大,需要董事会审批");
}
}
}
// 客户端组装链
public class Client {
public static void main(String[] args) {
Approver teamLeader = new TeamLeader();
Approver manager = new Manager();
Approver director = new Director();
teamLeader.setNext(manager);
manager.setNext(director);
teamLeader.handleRequest(800); // 组长处理
teamLeader.handleRequest(3000); // 经理处理
teamLeader.handleRequest(8000); // 总监处理
}
}
我的习惯:在 setNext 方法里加个判空,防止链中间断了没人处理。我曾经在一个支付系统中遇到过链断掉,结果请求石沉大海,用户以为支付成功了,实际上钱根本没扣。
实战:过滤器链
说到责任链模式,最经典的实战场景就是过滤器链。Java Web 中的 Filter、Spring 中的 Interceptor、Netty 中的 ChannelPipeline,本质上都是责任链模式。
你想想看,一个 HTTP 请求进来,要经过多少个环节?鉴权、日志、参数校验、XSS 过滤、性能监控……如果把这些逻辑全塞到一个 Servlet 里,那代码就成了一锅粥。过滤器链就是把这些逻辑拆成独立的 Filter,串成一条链。
代码示例:自定义过滤器链
// 过滤器接口
public interface Filter {
void doFilter(Request request, Response response, FilterChain chain);
}
// 过滤器链
public class FilterChain {
private List<Filter> filters = new ArrayList<>();
private int index = 0;
public FilterChain addFilter(Filter filter) {
filters.add(filter);
return this;
}
public void doFilter(Request request, Response response) {
if (index < filters.size()) {
Filter filter = filters.get(index++);
filter.doFilter(request, response, this);
}
}
}
// 具体过滤器:鉴权
public class AuthFilter implements Filter {
@Override
public void doFilter(Request request, Response response, FilterChain chain) {
if (request.getToken() == null) {
response.setStatus(401);
return;
}
System.out.println("鉴权通过");
chain.doFilter(request, response);
}
}
// 具体过滤器:日志
public class LogFilter implements Filter {
@Override
public void doFilter(Request request, Response response, FilterChain chain) {
System.out.println("记录日志:" + request.getUri());
chain.doFilter(request, response);
System.out.println("响应状态:" + response.getStatus());
}
}
// 使用
public class Application {
public static void main(String[] args) {
FilterChain chain = new FilterChain();
chain.addFilter(new AuthFilter())
.addFilter(new LogFilter())
.addFilter(new XssFilter());
chain.doFilter(new Request(), new Response());
}
}
注意:过滤器链的执行顺序很重要。鉴权过滤器一定要放在最前面,否则未登录的用户也能走到业务逻辑里。我曾经见过一个项目,把日志过滤器放在鉴权前面,结果日志里全是未登录用户的请求记录,排查问题的时候特别痛苦。
责任链模式的两种变体
在实际项目中,责任链模式有两种常见的变体:
- 纯责任链模式:请求被一个处理者处理后,链就终止了。比如上面的审批流程,组长批了就结束了,不会继续往经理传。
- 不纯责任链模式:请求经过每个处理者,每个处理者都可以对请求做点手脚。比如过滤器链,每个 Filter 都可以修改 Request 和 Response,然后继续往下传。
我个人更常用不纯责任链模式,尤其是在中间件开发中。每个 Filter 只做一件事,组合起来就能完成复杂的处理流程。这其实就是「管道-过滤器」架构模式。
避坑指南
我曾经在写一个消息推送系统时,用责任链模式处理消息的过滤和转换。结果线上出了个 bug,消息重复推送了两次。排查了半天,发现是链中某个 Filter 调用了两次 chain.doFilter()。嗯,这里要注意:每个 Filter 只能调用一次 chain.doFilter(),否则请求会在链中循环或者重复执行。
另一个常见的坑是:链的顺序依赖。如果 Filter A 需要在 Filter B 之前执行,但代码里写反了,那整个逻辑就乱了。我建议在组装链的时候,用配置文件或者注解来声明顺序,而不是硬编码在代码里。
SVG 流程图:责任链模式的核心逻辑
什么时候用责任链模式?
我总结了几条经验,当你遇到以下情况时,可以考虑用责任链模式:
- 有多个对象可以处理同一个请求,但具体哪个处理者在运行时才能确定
- 你想在不修改客户端代码的情况下,动态新增或调整处理者
- 处理逻辑可以拆成多个独立的步骤,每个步骤只做一件事
- 请求的处理顺序很重要,而且可能会变化
一句话总结:责任链模式把「谁处理」这个决策从编译时推迟到了运行时。每个节点只关心自己的职责,不关心上下游。这就是它能让代码变得优雅的原因。
好了,关于责任链模式就聊这么多。过滤器链的实战代码我已经贴出来了,你可以直接拿去用。记住,写代码的时候多想想「这个逻辑能不能拆成一个链」,你会发现很多地方都能用上这个模式。