责任链模式:请求传递、链式处理、实战:过滤器链

责任链模式,说白了就是让多个对象都有机会处理同一个请求。我把这些对象串成一条链,请求沿着链传递,直到有某个对象处理它为止。嗯,这有点像公司里的审批流程——你提交一个报销单,组长先看,金额大了转经理,再大了转总监。

我个人习惯把责任链模式看作是「解耦请求发送者和接收者」的利器。发送者不需要知道谁会处理,接收者也不需要知道请求从哪来。每个节点只关心两件事:我能不能处理?不能的话,丢给下一个。

模式结构:三个核心角色

责任链模式的结构其实很简单,就三个角色:

角色 职责 我见过的坑
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 流程图:责任链模式的核心逻辑

责任链模式:请求传递与链式处理 客户端 (Client) Handler 1 能处理?→ 处理并返回 不能处理,传递 Handler 2 能处理?→ 处理并返回 不能处理,传递 Handler 3 能处理?→ 处理并返回 仍不能处理,返回默认结果 关键点 • 每个 Handler 持有下一个引用 • 请求沿着链单向传递 • 处理者决定是否处理或转发 • 客户端只需调用第一个节点 • 新增处理者不影响现有代码 • 顺序由组装时决定 → 开闭原则的完美体现

什么时候用责任链模式?

我总结了几条经验,当你遇到以下情况时,可以考虑用责任链模式:

  • 有多个对象可以处理同一个请求,但具体哪个处理者在运行时才能确定
  • 你想在不修改客户端代码的情况下,动态新增或调整处理者
  • 处理逻辑可以拆成多个独立的步骤,每个步骤只做一件事
  • 请求的处理顺序很重要,而且可能会变化

一句话总结:责任链模式把「谁处理」这个决策从编译时推迟到了运行时。每个节点只关心自己的职责,不关心上下游。这就是它能让代码变得优雅的原因。

好了,关于责任链模式就聊这么多。过滤器链的实战代码我已经贴出来了,你可以直接拿去用。记住,写代码的时候多想想「这个逻辑能不能拆成一个链」,你会发现很多地方都能用上这个模式。

公众号:蓝海资料掘金营,微信 deep3321