责任链模式:请求传递的艺术
责任链模式,说白了就是让多个对象都有机会处理同一个请求。它们排成一条链,请求沿着链传递,直到某个对象处理它为止。这就像公司里的审批流程——你的请假申请先到组长那,组长批不了就往上递,直到有人能拍板。
我最早接触这个模式,是在做Web框架的FilterChain时。那时候我还在想:为什么要把过滤器串起来?直接一个个调用不就行了?后来踩了坑才明白,责任链的精髓在于解耦——发送者不需要知道谁会处理请求,接收者也不需要知道请求从哪来。
模式结构
责任链模式有三个核心角色:
- Handler(处理者):定义处理请求的接口,并且持有下一个处理者的引用
- ConcreteHandler(具体处理者):实现处理逻辑,决定是自己处理还是传给下一个
- Client(客户端):构建责任链,并向链头发起请求
嗯,这里要注意:责任链可以是单向的,也可以是双向的。Netty的Pipeline就是双向链,请求从头部进来,经过所有处理器,再原路返回。这个设计非常巧妙,我后面会细说。
Java实现:从FilterChain说起
先看一个经典的FilterChain实现。我记得在写一个Web框架时,需要支持多个过滤器对请求进行预处理。
// 过滤器接口
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) {
System.out.println("认证过滤器:检查用户身份");
// 继续传递
chain.doFilter(request, response);
System.out.println("认证过滤器:响应后处理");
}
}
public class LogFilter implements Filter {
@Override
public void doFilter(Request request, Response response, FilterChain chain) {
System.out.println("日志过滤器:记录请求信息");
chain.doFilter(request, response);
System.out.println("日志过滤器:记录响应信息");
}
}
你看,每个过滤器只关心自己的事,处理完就传给下一个。这种设计让添加新过滤器变得非常简单——只需要实现Filter接口,然后加到链里就行。
关键点:FilterChain本身也实现了doFilter方法,但它不是真正的处理器,而是负责维护链的遍历。这种模式在Java Servlet规范中广泛使用。
C++实现:更贴近底层
C++的实现思路类似,但要注意内存管理。我个人习惯用智能指针来管理链上的节点。
#include <iostream>
#include <memory>
class Handler {
public:
virtual ~Handler() = default;
void setNext(std::shared_ptr<Handler> next) {
this->next = next;
}
virtual void handle(int request) {
if (next) {
next->handle(request);
}
}
protected:
std::shared_ptr<Handler> next;
};
class ConcreteHandler1 : public Handler {
public:
void handle(int request) override {
if (request >= 0 && request < 10) {
std::cout << "Handler1 处理请求: " << request << std::endl;
} else {
Handler::handle(request);
}
}
};
class ConcreteHandler2 : public Handler {
public:
void handle(int request) override {
if (request >= 10 && request < 20) {
std::cout << "Handler2 处理请求: " << request << std::endl;
} else {
Handler::handle(request);
}
}
};
// 使用
int main() {
auto h1 = std::make_shared<ConcreteHandler1>();
auto h2 = std::make_shared<ConcreteHandler2>();
h1->setNext(h2);
h1->handle(5); // Handler1处理
h1->handle(15); // Handler2处理
h1->handle(25); // 无人处理
return 0;
}
避坑指南:我曾经在C++项目中忘记处理链尾的情况,导致请求丢失。建议在基类Handler中提供一个默认的handle实现,要么传给下一个,要么记录日志表示无人处理。
Netty Pipeline:责任链的巅峰之作
说到责任链模式,不得不提Netty的Pipeline。它把责任链玩出了新高度——双向链、事件驱动、动态增删处理器。
Netty的Pipeline结构是这样的:
// Netty中的ChannelHandler
public class SimpleServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println("服务端收到消息: " + msg);
// 处理完后继续传递
ctx.fireChannelRead(msg);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
// 添加处理器到Pipeline
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("handler", new SimpleServerHandler());
为什么Netty要设计成双向链?因为网络通信中,请求进来和响应出去是两个方向。入站处理器处理请求,出站处理器处理响应。这种设计让代码非常清晰——你只需要关注当前方向的处理逻辑。
实际应用场景
责任链模式在真实项目中随处可见。我总结了几种典型场景:
| 场景 | 说明 | 示例 |
|---|---|---|
| 请求预处理 | 对请求进行认证、授权、日志、限流等 | Spring Security FilterChain |
| 事件处理 | 事件沿着链传递,每个处理器决定是否消费 | GUI事件处理、Netty Pipeline |
| 审批流程 | 不同级别的审批人处理不同范围的请求 | OA系统、报销审批 |
| 异常处理 | 异常从底层向上层传递,直到被捕获 | Java异常机制、try-catch链 |
注意事项:责任链模式虽然灵活,但也不是万能的。如果链太长,性能会受影响。我曾经在一个项目中看到过20多个过滤器串在一起,每次请求都要遍历一遍,响应时间直接翻倍。建议控制链的长度,或者考虑用树形结构替代。
优缺点总结
责任链模式的好处很明显:
- 解耦:请求发送者和接收者完全解耦
- 灵活:可以动态调整链的顺序和成员
- 可扩展:新增处理器不影响现有代码
但也有它的短板:
- 性能:链越长,性能损耗越大
- 调试困难:请求在链中传递,很难定位问题
- 可能被忽略:如果链尾没有兜底处理,请求可能丢失
我个人建议,在需要多个处理器按顺序处理同一请求时,优先考虑责任链模式。但如果你只需要一个处理器来处理请求,那就别用——杀鸡焉用牛刀。
避坑指南:我曾经在一个项目中,把责任链和观察者模式搞混了。责任链是串行传递,每个处理器决定是否处理;观察者模式是广播通知,所有观察者都会收到。这两个模式的应用场景完全不同,千万别用错。
好了,责任链模式就聊到这。记住它的核心思想:让多个对象都有机会处理请求,直到有人接手。下次你写过滤器、拦截器或者Pipeline时,不妨想想这个模式,说不定能帮你写出更优雅的代码。
公众号:蓝海资料掘金营,微信deep3321