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个以内,超过考虑合并或重构
  • 每个处理者只做一件事,遵循单一职责原则
  • 用配置驱动处理者的顺序,不要硬编码
  • 考虑使用责任链的变体:拦截器模式、管道模式

责任链模式的核心流程图

下面这张图展示了责任链模式的完整流程。请求从客户端发出,沿着链依次经过每个处理者,直到被处理或到达链尾。

责任链模式请求传递流程 客户端 处理者1 能处理?→是 处理者2 能处理?→是 处理者3 能处理?→是 处理成功 响应返回 图例 处理者:判断是否能处理当前请求 响应:处理成功后返回结果 请求传递方向

总结

责任链模式的核心思想就一句话:让多个对象都有机会处理请求,避免请求发送者和接收者之间的耦合

你想想看,如果没有责任链,我们得写多少if-else?

if (组长能处理) { 组长处理 } else if (经理能处理) { 经理处理 } else if ...

这种代码,改一次需求就得动整个方法,维护成本极高。

而责任链模式,每个处理者独立,加一个处理者就是加一个类,改一个处理者不影响其他。这才是面向对象设计的精髓。

一句话记住责任链:请求沿着链传递,直到有人接盘。

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