摘要:责任链模式(Chain of Responsibility Pattern)是 GoF 23 种设计模式中的行为型模式之一,它通过构建一条处理者链来解耦请求的发送者与接收者,让多个对象都有机会处理请求。本文系统性地介绍了责任链模式的核心概念、UML 类图、Java 实战示例(包含完整的异常校验)、Spring Boot 拦截器应用,并深入探讨了其在微服务网关、响应式编程等现代架构中的实践。无论你是初学者还是有一定经验的开发者,都能从中掌握责任链模式的原理、实现技巧及最佳实践,提升代码的灵活性与可维护性。

📖 目录

1. 什么是责任链模式?

责任链模式(Chain of Responsibility Pattern)是一种行为设计模式,它允许你将请求沿着处理者链进行传递。收到请求后,每个处理者均可对请求进行处理,或将其传递给链上的下一个处理者。

这种模式的核心思想是解耦请求的发送者和接收者,让多个对象都有机会处理请求,从而避免请求发送者与接收者之间的紧密耦合。

核心角色:

  • 抽象处理者(Handler):定义处理请求的接口,通常包含一个指向下一个处理者的引用。
  • 具体处理者(Concrete Handler):实现抽象处理者的接口,负责处理它能够处理的请求,否则将请求传递给下一个处理者。
  • 客户端(Client):创建处理链,并向链的头部提交请求。

2. 模式原理与 UML 类图

责任链模式通过构建一条处理者链来工作。每个处理者都持有对下一个处理者的引用,形成一条链。请求从链的一端进入,沿着链传递,直到被某个处理者处理或到达链的末端。

successor

创建并组装链

«abstract»

Handler

-successor: Handler

+setSuccessor(handler: Handler)

+handleRequest(request: Request)

ConcreteHandlerA

+handleRequest(request: Request)

ConcreteHandlerB

+handleRequest(request: Request)

Client

+main()

流程说明:

  1. 客户端创建具体处理者对象,并按需设置它们的后继者,形成一条链。
  2. 客户端将请求发送给链的第一个处理者。
  3. 请求在链上传递,每个处理者判断自己是否能处理该请求。
  4. 若能处理,则处理并结束传递;若不能,则传递给后继者。
  5. 如果请求到达链尾仍未被处理,则可能被忽略或进行默认处理。

3. 实战:审批流程示例

我们通过一个经典的“费用报销审批”场景来演示责任链模式的实现。假设报销金额不同,需要不同级别的领导审批:项目经理(≤1000元)、部门经理(≤5000元)、总经理(>5000元)。

3.1 定义抽象处理者和请求对象

// 请求对象:报销申请
public class ReimbursementRequest {
    private String employeeName;
    private double amount;
    private String purpose;

    // 构造函数
    public ReimbursementRequest() {
    }

    public ReimbursementRequest(String employeeName, double amount, String purpose) {
        this.employeeName = employeeName;
        this.amount = amount;
        this.purpose = purpose;
    }

    // Getter 和 Setter 方法
    public String getEmployeeName() {
        return employeeName;
    }

    public void setEmployeeName(String employeeName) {
        this.employeeName = employeeName;
    }

    public double getAmount() {
        return amount;
    }

    public void setAmount(double amount) {
        this.amount = amount;
    }

    public String getPurpose() {
        return purpose;
    }

    public void setPurpose(String purpose) {
        this.purpose = purpose;
    }

    @Override
    public String toString() {
        return "ReimbursementRequest{" +
                "employeeName='" + employeeName + '\'' +
                ", amount=" + amount +
                ", purpose='" + purpose + '\'' +
                '}';
    }
}

// 抽象处理者:审批人
public abstract class Approver {
    protected Approver successor; // 下一级审批人

    public void setSuccessor(Approver successor) {
        this.successor = successor;
    }

    // 处理审批请求(增加异常校验)
    public abstract void processRequest(ReimbursementRequest request);
    
    // 公共的请求校验方法
    protected boolean validateRequest(ReimbursementRequest request) {
        if (request == null) {
            System.out.println("错误:报销请求不能为null!");
            return false;
        }
        
        if (request.getAmount() < 0) {
            System.out.println("错误:报销金额不能为负数!员工:" + request.getEmployeeName() + ",金额:" + request.getAmount());
            return false;
        }
        
        if (request.getEmployeeName() == null || request.getEmployeeName().trim().isEmpty()) {
            System.out.println("错误:员工姓名不能为空!");
            return false;
        }
        
        if (request.getPurpose() == null || request.getPurpose().trim().isEmpty()) {
            System.out.println("警告:报销事由为空,请补充说明。");
        }
        
        return true;
    }
}

3.2 实现具体处理者

// 具体处理者:项目经理
public class ProjectManager extends Approver {
    private static final double LIMIT = 1000.0;

    @Override
    public void processRequest(ReimbursementRequest request) {
        // 校验请求的合法性
        if (!validateRequest(request)) {
            System.out.println("项目经理:请求校验失败,终止处理。");
            return;
        }
        
        if (request.getAmount() <= LIMIT) {
            System.out.println("项目经理审批通过:" + request.getEmployeeName() + "的报销,金额:" + request.getAmount() + "元,事由:" + request.getPurpose());
        } else if (successor != null) {
            System.out.println("项目经理无权审批,转交部门经理处理。");
            successor.processRequest(request);
        } else {
            System.out.println("无人能处理此报销申请。");
        }
    }
}

// 具体处理者:部门经理
public class DepartmentManager extends Approver {
    private static final double LIMIT = 5000.0;

    @Override
    public void processRequest(ReimbursementRequest request) {
        // 校验请求的合法性
        if (!validateRequest(request)) {
            System.out.println("部门经理:请求校验失败,终止处理。");
            return;
        }
        
        if (request.getAmount() <= LIMIT) {
            System.out.println("部门经理审批通过:" + request.getEmployeeName() + "的报销,金额:" + request.getAmount() + "元,事由:" + request.getPurpose());
        } else if (successor != null) {
            System.out.println("部门经理无权审批,转交总经理处理。");
            successor.processRequest(request);
        } else {
            System.out.println("无人能处理此报销申请。");
        }
    }
}

// 具体处理者:总经理
public class GeneralManager extends Approver {
    @Override
    public void processRequest(ReimbursementRequest request) {
        // 校验请求的合法性
        if (!validateRequest(request)) {
            System.out.println("总经理:请求校验失败,终止处理。");
            return;
        }
        
        // 总经理可以处理所有金额
        System.out.println("总经理审批通过:" + request.getEmployeeName() + "的报销,金额:" + request.getAmount() + "元,事由:" + request.getPurpose());
    }
}

3.3 客户端组装链并提交请求

public class Client {
    public static void main(String[] args) {
        // 1. 创建处理者
        Approver projectManager = new ProjectManager();
        Approver departmentManager = new DepartmentManager();
        Approver generalManager = new GeneralManager();

        // 2. 构建责任链:项目经理 -> 部门经理 -> 总经理
        projectManager.setSuccessor(departmentManager);
        departmentManager.setSuccessor(generalManager);

        // 3. 创建报销请求
        ReimbursementRequest request1 = new ReimbursementRequest("张三", 800, "办公用品采购");
        ReimbursementRequest request2 = new ReimbursementRequest("李四", 3500, "差旅费");
        ReimbursementRequest request3 = new ReimbursementRequest("王五", 12000, "项目设备购置");

        // 4. 提交请求,从链头开始
        System.out.println("=== 报销审批流程开始 ===");
        projectManager.processRequest(request1);
        System.out.println("---");
        projectManager.processRequest(request2);
        System.out.println("---");
        projectManager.processRequest(request3);
    }
}

运行结果:

=== 报销审批流程开始 ===
项目经理审批通过:张三的报销,金额:800.0元,事由:办公用品采购
---
项目经理无权审批,转交部门经理处理。
部门经理审批通过:李四的报销,金额:3500.0元,事由:差旅费
---
项目经理无权审批,转交部门经理处理。
部门经理无权审批,转交总经理处理。
总经理审批通过:王五的报销,金额:12000.0元,事由:项目设备购置

4. 责任链模式的变体与高级用法

4.1 纯与不纯的责任链

  • 纯责任链:一个请求必须被某个处理者处理,且只能被一个处理者处理(如审批流程,一个请求只被一个领导批准)。
  • 不纯责任链:一个请求可以被多个处理者部分处理,或者所有处理者都不处理(如过滤器链,每个过滤器都可能对请求进行修改)。

4.2 使用链表或集合管理处理者

除了每个处理者持有后继者的引用,也可以使用一个中央控制器(如 HandlerChain 类)来维护处理者列表,并按顺序调用它们。

// 使用列表管理的责任链
public class HandlerChain {
    private List<Approver> handlers = new ArrayList<>();

    public void addHandler(Approver handler) {
        handlers.add(handler);
    }

    public void process(ReimbursementRequest request) {
        for (Approver handler : handlers) {
            handler.processRequest(request);
            // 可根据需要决定是否中断链
            // if (request.isProcessed()) break;
        }
    }
}

4.3 结合模板方法模式

可以在抽象处理者中定义处理流程的骨架,将是否处理的判断逻辑留给子类实现。

public abstract class ApproverTemplate {
    protected ApproverTemplate successor;

    public void setSuccessor(ApproverTemplate successor) {
        this.successor = successor;
    }

    // 模板方法:定义处理流程
    public final void handle(ReimbursementRequest request) {
        if (canHandle(request)) {
            doHandle(request);
        } else if (successor != null) {
            successor.handle(request);
        } else {
            defaultHandle(request);
        }
    }

    protected abstract boolean canHandle(ReimbursementRequest request);
    protected abstract void doHandle(ReimbursementRequest request);
    protected void defaultHandle(ReimbursementRequest request) {
        System.out.println("请求未被处理: " + request);
    }
}

5. 应用场景与优缺点

5.1 典型应用场景

  1. 多级审批/权限检查:如报销、请假、采购审批流程。
  2. 事件处理/消息过滤:GUI 中的事件冒泡、Servlet 中的 Filter 链、Netty 中的 ChannelHandler 链。
  3. 日志记录器:不同级别的日志(DEBUG、INFO、WARN、ERROR)被不同处理器处理。
  4. 异常处理:尝试多种异常处理策略,直到有一个能处理。
  5. 中间件管道:Web 框架的中间件(如 Express.js、Koa、ASP.NET Core)。

5.2 优点

  • 降低耦合度:请求发送者无需知道具体由哪个对象处理。
  • 增强灵活性:可以动态地增加或修改处理链。
  • 符合单一职责原则:每个处理者只负责处理自己相关的请求。
  • 符合开闭原则:可以新增处理者而不影响现有代码。

5.3 缺点

  • 请求可能未被处理:如果链配置不当,请求可能到达链尾都未被处理。
  • 性能问题:链过长时,请求传递可能带来性能开销。
  • 调试困难:请求的传递路径可能不直观,调试时较难跟踪。

6. 在 Spring 框架中的应用

Spring MVC 中的拦截器(Interceptor)链是责任链模式的典型应用。多个拦截器按顺序组成一条链,每个拦截器都可以对请求进行预处理和后处理。

// 模拟 Spring 拦截器责任链
public interface HandlerInterceptor {
    boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler);
    void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView);
    void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
}

public class HandlerExecutionChain {
    private List<HandlerInterceptor> interceptors = new ArrayList<>();
    private int interceptorIndex = -1;

    public void addInterceptor(HandlerInterceptor interceptor) {
        interceptors.add(interceptor);
    }

    // 执行预处理(责任链正向传递)
    public boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        for (int i = 0; i < interceptors.size(); i++) {
            HandlerInterceptor interceptor = interceptors.get(i);
            if (!interceptor.preHandle(request, response, handler)) {
                // 某个拦截器返回 false,中断链
                triggerAfterCompletion(request, response, handler, null);
                return false;
            }
            interceptorIndex = i;
        }
        return true;
    }

    // 执行后处理(责任链反向传递)
    public void applyPostHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        for (int i = interceptors.size() - 1; i >= 0; i--) {
            HandlerInterceptor interceptor = interceptors.get(i);
            interceptor.postHandle(request, response, handler, modelAndView);
        }
    }
}

6.1 Spring Boot 拦截器实战示例

下面通过一个具体的 Spring Boot 示例,展示如何定义两个自定义拦截器(日志拦截器和权限拦截器)并将其注册到责任链中:

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;

// 1. 日志拦截器:记录请求开始和结束时间
@Component
public class LoggingInterceptor implements HandlerInterceptor {
    
    private static final String START_TIME_ATTR = "startTime";
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // 记录请求开始时间
        request.setAttribute(START_TIME_ATTR, System.currentTimeMillis());
        System.out.println("[日志拦截器] 请求开始: " + request.getRequestURI() + 
                          " | 时间: " + LocalDateTime.now());
        return true; // 继续执行下一个拦截器
    }
    
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, 
                          Object handler, ModelAndView modelAndView) {
        // 请求处理完成后执行
        System.out.println("[日志拦截器] 请求处理完成: " + request.getRequestURI());
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                               Object handler, Exception ex) {
        // 计算请求耗时
        Long startTime = (Long) request.getAttribute(START_TIME_ATTR);
        if (startTime != null) {
            long duration = System.currentTimeMillis() - startTime;
            System.out.println("[日志拦截器] 请求结束: " + request.getRequestURI() + 
                              " | 耗时: " + duration + "ms");
        }
        
        if (ex != null) {
            System.out.println("[日志拦截器] 请求异常: " + ex.getMessage());
        }
    }
}

// 2. 权限拦截器:检查用户权限
@Component
public class AuthInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String token = request.getHeader("Authorization");
        
        // 模拟权限检查
        if (token == null || !token.startsWith("Bearer ")) {
            System.out.println("[权限拦截器] 未授权访问: " + request.getRequestURI());
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false; // 中断链,不再继续执行后续拦截器和控制器
        }
        
        // 模拟验证 token
        String userRole = validateToken(token.substring(7));
        if (!hasPermission(userRole, request.getRequestURI())) {
            System.out.println("[权限拦截器] 权限不足: " + userRole + " 访问 " + request.getRequestURI());
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            return false; // 中断链
        }
        
        System.out.println("[权限拦截器] 权限验证通过: " + userRole);
        request.setAttribute("userRole", userRole);
        return true; // 继续执行下一个拦截器
    }
    
    private String validateToken(String token) {
        // 模拟 token 验证逻辑
        return token.equals("admin123") ? "ADMIN" : "USER";
    }
    
    private boolean hasPermission(String role, String uri) {
        // 模拟权限检查逻辑
        if (uri.startsWith("/admin") && !"ADMIN".equals(role)) {
            return false;
        }
        return true;
    }
    
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, 
                          Object handler, ModelAndView modelAndView) {
        // 可在此处添加权限相关的后处理逻辑
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, 
                               Object handler, Exception ex) {
        // 清理资源
        request.removeAttribute("userRole");
    }
}

// 3. 配置类:注册拦截器到责任链
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    
    @Autowired
    private LoggingInterceptor loggingInterceptor;
    
    @Autowired
    private AuthInterceptor authInterceptor;
    
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册拦截器到责任链,按添加顺序执行
        registry.addInterceptor(loggingInterceptor)
                .addPathPatterns("/api/**"); // 匹配所有 /api 开头的请求
        
        registry.addInterceptor(authInterceptor)
                .addPathPatterns("/api/**")
                .excludePathPatterns("/api/public/**"); // 排除公开接口
        
        // 可以继续添加更多拦截器...
        
        System.out.println("拦截器注册完成,执行顺序:LoggingInterceptor → AuthInterceptor");
    }
}

// 4. 控制器示例
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api")
public class DemoController {
    
    @GetMapping("/hello")
    public String hello() {
        return "Hello, Spring Boot Interceptor Chain!";
    }
    
    @GetMapping("/admin/data")
    public String adminData() {
        return "Admin Data (需要 ADMIN 权限)";
    }
}

执行顺序说明:

  1. 拦截器注册顺序决定执行顺序:在 InterceptorConfig 中,先注册 LoggingInterceptor,后注册 AuthInterceptor,因此执行顺序为:

    • LoggingInterceptor.preHandle()
    • AuthInterceptor.preHandle()
    • 控制器方法
    • AuthInterceptor.postHandle()
    • LoggingInterceptor.postHandle()
    • AuthInterceptor.afterCompletion()
    • LoggingInterceptor.afterCompletion()
  2. 责任链传递机制

    • 每个拦截器的 preHandle() 方法返回 true 时,请求继续传递给下一个拦截器
    • 如果某个拦截器的 preHandle() 返回 false,则链被中断,后续拦截器和控制器都不会执行
    • postHandle() 方法按注册的相反顺序执行(后进先出)
    • afterCompletion() 方法同样按注册的相反顺序执行
  3. 实际运行效果

    [日志拦截器] 请求开始: /api/hello | 时间: 2023-10-01T10:30:00
    [权限拦截器] 权限验证通过: USER
    [日志拦截器] 请求处理完成: /api/hello
    [日志拦截器] 请求结束: /api/hello | 耗时: 15ms
    

这个示例展示了 Spring Boot 中责任链模式的实际应用:多个拦截器组成处理链,每个拦截器专注于单一职责(日志记录、权限验证),通过配置类动态组装链,实现了请求处理的解耦和灵活扩展。

7. 总结

责任链模式通过构建一条处理者链,将请求的发送与处理解耦,让多个对象都有机会处理请求。它在需要多级处理、动态指定处理顺序的场景中非常有用。

使用建议:

  1. 明确链的结束条件,避免请求无人处理。
  2. 控制链的长度,避免性能问题。
  3. 考虑使用“短路”机制,当某个处理者处理请求后,可以终止链的传递。

掌握责任链模式,能让你在设计和实现复杂的处理流程时,写出更灵活、更易维护的代码。

8. 扩展阅读与未来展望

责任链模式作为经典的设计模式,在现代软件开发中依然有着广泛的应用,特别是在微服务架构和响应式编程等新兴领域。

8.1 微服务网关中的责任链模式

在微服务架构中,API 网关(如 Spring Cloud Gateway、Zuul)大量使用了责任链模式来处理请求。网关中的过滤器链(Filter Chain)就是责任链的典型应用:

  • Spring Cloud Gateway:通过 GatewayFilter 链处理请求,每个过滤器可以执行路由、认证、限流、日志等操作
  • Zuul:使用 ZuulFilter 链,支持 pre、route、post、error 四种类型的过滤器
  • Kong:基于插件机制构建的网关,插件执行顺序形成责任链

这些网关框架允许开发者自定义过滤器,并按需组合成处理链,实现了请求处理的高度可扩展性。

8.2 响应式编程中的责任链

在响应式编程框架(如 Reactor、RxJava)中,操作符链(Operator Chain)本质上也是责任链模式的应用:

  • ReactorFluxMono 的操作符(如 mapfilterflatMap)形成处理链
  • 响应式流处理:数据流经过多个处理阶段,每个阶段都可以对数据进行转换、过滤或聚合
  • 背压传播:责任链模式帮助在异步流中传播背压信号

这种模式在响应式系统中特别有用,因为它允许将复杂的异步处理流程分解为一系列简单的处理单元。

8.3 推荐阅读资源

  1. Spring Cloud Gateway 官方文档 - 深入了解网关过滤器链的实现

    • 链接:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories
    • 重点阅读 GatewayFilter 的链式执行机制
  2. Reactor 核心概念指南 - 理解响应式编程中的操作符链

    • 链接:https://projectreactor.io/docs/core/release/reference/#core-features
    • 重点关注 “Operators” 和 “Assembly time” 章节
  3. 设计模式:可复用面向对象软件的基础(GoF 原著)

    • 第 5 章行为型模式中的责任链模式详解
    • 了解模式的原始定义和经典实现
  4. Martin Fowler 的企业应用架构模式

    • 中介者模式与责任链模式的对比
    • 在实际企业应用中的取舍建议

8.4 未来发展趋势

随着云原生和 Serverless 架构的普及,责任链模式也在不断演进:

  • 函数链(Function Chaining):在 FaaS(函数即服务)中,多个函数可以串联形成处理管道
  • 边车模式(Sidecar Pattern):服务网格(如 Istio)中的边车代理使用责任链处理流量
  • 可观测性管道:日志、指标、追踪数据的收集和处理链
  • AI 推理管道:机器学习模型的服务化部署中,预处理、推理、后处理形成责任链

8.5 学习建议

要深入掌握责任链模式,建议:

  1. 动手实践:在 Spring Cloud Gateway 或类似网关框架中实现自定义过滤器
  2. 源码阅读:阅读 Reactor、Netty 等框架中责任链的实现源码
  3. 模式对比:与装饰器模式、管道过滤器模式进行对比分析
  4. 性能考量:在实际项目中注意链的长度和性能影响,合理使用短路机制

责任链模式的核心价值在于解耦可扩展性,这些特性使其在现代分布式系统中依然保持着强大的生命力。随着架构的演进,责任链模式的应用场景会更加丰富,理解其本质将帮助你在复杂系统中设计出更优雅的解决方案。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐