责任链模式:从基础概念到实战应用完整指南
摘要:责任链模式(Chain of Responsibility Pattern)是 GoF 23 种设计模式中的行为型模式之一,它通过构建一条处理者链来解耦请求的发送者与接收者,让多个对象都有机会处理请求。本文系统性地介绍了责任链模式的核心概念、UML 类图、Java 实战示例(包含完整的异常校验)、Spring Boot 拦截器应用,并深入探讨了其在微服务网关、响应式编程等现代架构中的实践。无论你是初学者还是有一定经验的开发者,都能从中掌握责任链模式的原理、实现技巧及最佳实践,提升代码的灵活性与可维护性。
📖 目录
- 1. 什么是责任链模式?
- 2. 模式原理与 UML 类图
- 3. 实战:审批流程示例
- 4. 责任链模式的变体与高级用法
- 5. 应用场景与优缺点
- 6. 在 Spring 框架中的应用
- 7. 总结
- 8. 扩展阅读与未来展望
1. 什么是责任链模式?
责任链模式(Chain of Responsibility Pattern)是一种行为设计模式,它允许你将请求沿着处理者链进行传递。收到请求后,每个处理者均可对请求进行处理,或将其传递给链上的下一个处理者。
这种模式的核心思想是解耦请求的发送者和接收者,让多个对象都有机会处理请求,从而避免请求发送者与接收者之间的紧密耦合。
核心角色:
- 抽象处理者(Handler):定义处理请求的接口,通常包含一个指向下一个处理者的引用。
- 具体处理者(Concrete Handler):实现抽象处理者的接口,负责处理它能够处理的请求,否则将请求传递给下一个处理者。
- 客户端(Client):创建处理链,并向链的头部提交请求。
2. 模式原理与 UML 类图
责任链模式通过构建一条处理者链来工作。每个处理者都持有对下一个处理者的引用,形成一条链。请求从链的一端进入,沿着链传递,直到被某个处理者处理或到达链的末端。
流程说明:
- 客户端创建具体处理者对象,并按需设置它们的后继者,形成一条链。
- 客户端将请求发送给链的第一个处理者。
- 请求在链上传递,每个处理者判断自己是否能处理该请求。
- 若能处理,则处理并结束传递;若不能,则传递给后继者。
- 如果请求到达链尾仍未被处理,则可能被忽略或进行默认处理。
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 典型应用场景
- 多级审批/权限检查:如报销、请假、采购审批流程。
- 事件处理/消息过滤:GUI 中的事件冒泡、Servlet 中的 Filter 链、Netty 中的 ChannelHandler 链。
- 日志记录器:不同级别的日志(DEBUG、INFO、WARN、ERROR)被不同处理器处理。
- 异常处理:尝试多种异常处理策略,直到有一个能处理。
- 中间件管道: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 权限)";
}
}
执行顺序说明:
-
拦截器注册顺序决定执行顺序:在
InterceptorConfig中,先注册LoggingInterceptor,后注册AuthInterceptor,因此执行顺序为:LoggingInterceptor.preHandle()AuthInterceptor.preHandle()- 控制器方法
AuthInterceptor.postHandle()LoggingInterceptor.postHandle()AuthInterceptor.afterCompletion()LoggingInterceptor.afterCompletion()
-
责任链传递机制:
- 每个拦截器的
preHandle()方法返回true时,请求继续传递给下一个拦截器 - 如果某个拦截器的
preHandle()返回false,则链被中断,后续拦截器和控制器都不会执行 postHandle()方法按注册的相反顺序执行(后进先出)afterCompletion()方法同样按注册的相反顺序执行
- 每个拦截器的
-
实际运行效果:
[日志拦截器] 请求开始: /api/hello | 时间: 2023-10-01T10:30:00 [权限拦截器] 权限验证通过: USER [日志拦截器] 请求处理完成: /api/hello [日志拦截器] 请求结束: /api/hello | 耗时: 15ms
这个示例展示了 Spring Boot 中责任链模式的实际应用:多个拦截器组成处理链,每个拦截器专注于单一职责(日志记录、权限验证),通过配置类动态组装链,实现了请求处理的解耦和灵活扩展。
7. 总结
责任链模式通过构建一条处理者链,将请求的发送与处理解耦,让多个对象都有机会处理请求。它在需要多级处理、动态指定处理顺序的场景中非常有用。
使用建议:
- 明确链的结束条件,避免请求无人处理。
- 控制链的长度,避免性能问题。
- 考虑使用“短路”机制,当某个处理者处理请求后,可以终止链的传递。
掌握责任链模式,能让你在设计和实现复杂的处理流程时,写出更灵活、更易维护的代码。
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)本质上也是责任链模式的应用:
- Reactor:
Flux和Mono的操作符(如map、filter、flatMap)形成处理链 - 响应式流处理:数据流经过多个处理阶段,每个阶段都可以对数据进行转换、过滤或聚合
- 背压传播:责任链模式帮助在异步流中传播背压信号
这种模式在响应式系统中特别有用,因为它允许将复杂的异步处理流程分解为一系列简单的处理单元。
8.3 推荐阅读资源
-
Spring Cloud Gateway 官方文档 - 深入了解网关过滤器链的实现
- 链接:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories
- 重点阅读 GatewayFilter 的链式执行机制
-
Reactor 核心概念指南 - 理解响应式编程中的操作符链
- 链接:https://projectreactor.io/docs/core/release/reference/#core-features
- 重点关注 “Operators” 和 “Assembly time” 章节
-
设计模式:可复用面向对象软件的基础(GoF 原著)
- 第 5 章行为型模式中的责任链模式详解
- 了解模式的原始定义和经典实现
-
Martin Fowler 的企业应用架构模式
- 中介者模式与责任链模式的对比
- 在实际企业应用中的取舍建议
8.4 未来发展趋势
随着云原生和 Serverless 架构的普及,责任链模式也在不断演进:
- 函数链(Function Chaining):在 FaaS(函数即服务)中,多个函数可以串联形成处理管道
- 边车模式(Sidecar Pattern):服务网格(如 Istio)中的边车代理使用责任链处理流量
- 可观测性管道:日志、指标、追踪数据的收集和处理链
- AI 推理管道:机器学习模型的服务化部署中,预处理、推理、后处理形成责任链
8.5 学习建议
要深入掌握责任链模式,建议:
- 动手实践:在 Spring Cloud Gateway 或类似网关框架中实现自定义过滤器
- 源码阅读:阅读 Reactor、Netty 等框架中责任链的实现源码
- 模式对比:与装饰器模式、管道过滤器模式进行对比分析
- 性能考量:在实际项目中注意链的长度和性能影响,合理使用短路机制
责任链模式的核心价值在于解耦和可扩展性,这些特性使其在现代分布式系统中依然保持着强大的生命力。随着架构的演进,责任链模式的应用场景会更加丰富,理解其本质将帮助你在复杂系统中设计出更优雅的解决方案。
更多推荐
所有评论(0)