Appearance
责任链模式 (Chain of Responsibility)
一、定义
一句话概括:使多个对象都有机会处理请求,将这些对象连成一条链,请求沿着这条链传递,直到有一个对象处理它为止。
官方定义:Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.
二、解决的问题
2.1 问题场景
在实际开发中,经常遇到一个请求需要经过多个处理环节的场景。例如:
- 请假审批流程:员工请假,需要组长 → 经理 → 总监 → 总经理逐级审批
- HTTP 请求处理:请求经过多个 Filter / Interceptor 进行处理
- 敏感词过滤:一段文本需要经过多个过滤器逐层过滤
2.2 不用责任链模式会怎样?
如果不用责任链模式,通常会在一个方法中写大量的 if-else 判断:
java
public void processRequest(Request request) {
if (request.getDays() <= 1) {
// 组长审批
leaderApprove(request);
} else if (request.getDays() <= 3) {
// 经理审批
managerApprove(request);
} else if (request.getDays() <= 7) {
// 总监审批
directorApprove(request);
} else {
// 总经理审批
ceoApprove(request);
}
}问题:
- 违反开闭原则:新增处理者需要修改原代码
- 职责不单一:一个方法承担了所有判断逻辑
- 难以扩展:处理顺序固定,无法灵活组合
- 耦合度高:请求发送者需要知道所有处理者
三、结构
3.1 角色组成
| 角色 | 说明 |
|---|---|
| Handler(抽象处理者) | 定义处理请求的接口,维护一个后继处理者的引用 |
| ConcreteHandler(具体处理者) | 实现处理请求的具体逻辑,如果能处理则处理,否则转发给后继者 |
| Client(客户端) | 创建处理链,向链首提交请求 |
3.2 类图(ASCII)
┌─────────────────┐
│ Handler │◄──────────────────┐
├─────────────────┤ │
│ - next: Handler │ │
├─────────────────┤ │
│ + setNext() │ │
│ + handle() │ │
└────────┬────────┘ │
│ │
┌────┴────┬──────────┐ │
│ │ │ │
▼ ▼ ▼ │
┌────────┐┌────────┐┌────────┐ │
│Concrete││Concrete││Concrete│ │
│HandlerA││HandlerB││HandlerC│ │
└────────┘└────────┘└────────┘ │
│
┌──────────┐ │
│ Client │──────────────────────────┘
└──────────┘四、代码实现
4.1 基础实现
java
// ==================== 请求类 ====================
class LeaveRequest {
private String name;
private int days;
private String reason;
public LeaveRequest(String name, int days, String reason) {
this.name = name;
this.days = days;
this.reason = reason;
}
public String getName() { return name; }
public int getDays() { return days; }
public String getReason() { return reason; }
}
// ==================== 抽象处理者 ====================
abstract class Approver {
protected Approver next; // 后继处理者
public void setNext(Approver next) {
this.next = next;
}
// 模板方法:先尝试自己处理,处理不了则交给下一个
public final void handleRequest(LeaveRequest request) {
if (canHandle(request)) {
doHandle(request);
} else if (next != null) {
next.handleRequest(request);
} else {
System.out.println("无人能处理该请求:" + request.getName());
}
}
protected abstract boolean canHandle(LeaveRequest request);
protected abstract void doHandle(LeaveRequest request);
}
// ==================== 具体处理者 ====================
class LeaderApprover extends Approver {
@Override
protected boolean canHandle(LeaveRequest request) {
return request.getDays() <= 1;
}
@Override
protected void doHandle(LeaveRequest request) {
System.out.println("组长审批通过:" + request.getName()
+ ",请假" + request.getDays() + "天");
}
}
class ManagerApprover extends Approver {
@Override
protected boolean canHandle(LeaveRequest request) {
return request.getDays() <= 3;
}
@Override
protected void doHandle(LeaveRequest request) {
System.out.println("经理审批通过:" + request.getName()
+ ",请假" + request.getDays() + "天");
}
}
class DirectorApprover extends Approver {
@Override
protected boolean canHandle(LeaveRequest request) {
return request.getDays() <= 7;
}
@Override
protected void doHandle(LeaveRequest request) {
System.out.println("总监审批通过:" + request.getName()
+ ",请假" + request.getDays() + "天");
}
}
class CEOApprover extends Approver {
@Override
protected boolean canHandle(LeaveRequest request) {
return true; // CEO 可以处理所有请求
}
@Override
protected void doHandle(LeaveRequest request) {
System.out.println("总经理审批通过:" + request.getName()
+ ",请假" + request.getDays() + "天");
}
}
// ==================== 客户端 ====================
public class ChainOfResponsibilityDemo {
public static void main(String[] args) {
// 构建责任链
Approver leader = new LeaderApprover();
Approver manager = new ManagerApprover();
Approver director = new DirectorApprover();
Approver ceo = new CEOApprover();
leader.setNext(manager);
manager.setNext(director);
director.setNext(ceo);
// 发送请求
leader.handleRequest(new LeaveRequest("张三", 1, "看病"));
leader.handleRequest(new LeaveRequest("李四", 5, "旅游"));
leader.handleRequest(new LeaveRequest("王五", 30, "婚假"));
}
}4.2 进阶实现
4.2.1 纯责任链 vs 不纯责任链
纯责任链:每个处理者要么处理请求,要么转发给下一个,不能部分处理后再转发。
不纯责任链:处理者可以处理请求的一部分,然后继续转发。例如 Servlet Filter 链:
java
// 不纯责任链示例:日志 Filter
class LogFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) {
System.out.println("请求到达:" + request.getRemoteAddr());
chain.doFilter(request, response); // 转发给下一个
System.out.println("响应返回");
}
}4.2.2 Servlet Filter 实现
java
// 自定义 Filter 实现责任链
interface Filter {
void doFilter(Request request, Response response, FilterChain chain);
}
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);
}
// 所有 Filter 执行完毕
}
}
// 具体 Filter:日志记录
class LogFilter implements Filter {
public void doFilter(Request request, Response response, FilterChain chain) {
System.out.println("[LogFilter] 请求前记录日志");
chain.doFilter(request, response);
System.out.println("[LogFilter] 请求后记录日志");
}
}
// 具体 Filter:权限校验
class AuthFilter implements Filter {
public void doFilter(Request request, Response response, FilterChain chain) {
if (request.getToken() != null) {
System.out.println("[AuthFilter] 认证通过");
chain.doFilter(request, response);
} else {
System.out.println("[AuthFilter] 认证失败,拒绝请求");
}
}
}
// 使用
public class FilterDemo {
public static void main(String[] args) {
FilterChain chain = new FilterChain();
chain.addFilter(new LogFilter());
chain.addFilter(new AuthFilter());
Request request = new Request("token123");
chain.doFilter(request, new Response());
}
}4.2.3 Spring Interceptor 实现
java
// Spring MVC 拦截器链
@Component
class LogInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) {
System.out.println("请求前:" + request.getRequestURI());
return true; // true 继续执行后续拦截器,false 中断
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) {
System.out.println("请求处理后,视图渲染前");
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler, Exception ex) {
System.out.println("视图渲染完成后");
}
}4.3 生产级实现
Spring Boot 审批流程实战
java
// ==================== 审批上下文 ====================
@Data
@Builder
class ApprovalContext {
private String applicant; // 申请人
private String content; // 审批内容
private BigDecimal amount; // 涉及金额
private int level; // 当前审批层级
private boolean approved; // 是否通过
private String rejectReason; // 拒绝原因
}
// ==================== 抽象审批处理器 ====================
abstract class AbstractApprovalHandler {
private AbstractApprovalHandler next;
public AbstractApprovalHandler setNext(AbstractApprovalHandler next) {
this.next = next;
return next;
}
public void handle(ApprovalContext context) {
if (!context.isApproved()) {
return; // 已被拒绝,停止传递
}
if (match(context)) {
doApprove(context);
}
if (next != null) {
next.handle(context);
}
}
protected abstract boolean match(ApprovalContext context);
protected abstract void doApprove(ApprovalContext context);
}
// ==================== 具体处理器 ====================
@Component
class DepartmentManagerHandler extends AbstractApprovalHandler {
@Override
protected boolean match(ApprovalContext context) {
return context.getAmount().compareTo(new BigDecimal("1000")) <= 0;
}
@Override
protected void doApprove(ApprovalContext context) {
System.out.println("部门经理审批通过:" + context.getApplicant());
}
}
@Component
class DirectorHandler extends AbstractApprovalHandler {
@Override
protected boolean match(ApprovalContext context) {
return context.getAmount().compareTo(new BigDecimal("10000")) <= 0;
}
@Override
protected void doApprove(ApprovalContext context) {
System.out.println("总监审批通过:" + context.getApplicant());
}
}
@Component
class VPHandler extends AbstractApprovalHandler {
@Override
protected boolean match(ApprovalContext context) {
return context.getAmount().compareTo(new BigDecimal("100000")) <= 0;
}
@Override
protected void doApprove(ApprovalContext context) {
System.out.println("VP审批通过:" + context.getApplicant());
}
}
// ==================== Spring Boot 配置 ====================
@Configuration
class ApprovalChainConfig {
@Bean
public AbstractApprovalHandler approvalChain(
DepartmentManagerHandler dept,
DirectorHandler director,
VPHandler vp) {
dept.setNext(director).setNext(vp);
return dept;
}
}
// ==================== 使用 ====================
@RestController
@RequestMapping("/api/approval")
class ApprovalController {
@Autowired
private AbstractApprovalHandler approvalChain;
@PostMapping("/submit")
public String submit(@RequestBody ApprovalContext context) {
context.setApproved(true);
approvalChain.handle(context);
return context.isApproved() ? "审批通过" : "审批拒绝:" + context.getRejectReason();
}
}五、优缺点
优点
- 降低耦合度:请求发送者不需要知道哪个对象处理请求,只需知道链首
- 增强灵活性:可以动态增删或重新排列处理者
- 符合开闭原则:新增处理者无需修改现有代码
- 单一职责:每个处理者只关注自己的处理逻辑
缺点
- 请求可能被遗漏:如果链配置不当,请求可能无人处理
- 性能开销:请求需要遍历整个链,链过长会影响性能
- 调试不便:链式调用使得堆栈较深,排错较困难
- 可能导致循环:不当的链配置可能导致死循环
六、适用场景
- 审批流程:OA 系统中的请假、报销、合同审批
- Web 请求处理:Servlet Filter、Spring Interceptor
- 日志框架:Logback / Log4j 的 Appender 链
- 敏感词过滤:消息系统、评论系统的多级过滤
- 网关路由:API 网关中的鉴权、限流、熔断等过滤器链
- 事件处理:GUI 事件冒泡机制
七、JDK / Spring 框架中的实际应用
| 框架 | 应用位置 | 说明 |
|---|---|---|
| Servlet | javax.servlet.FilterChain | Filter 链式调用 |
| Spring MVC | HandlerInterceptor | 拦截器链 |
| Spring Security | SecurityFilterChain | 安全过滤器链 |
| Netty | ChannelPipeline | 管道中的 ChannelHandler 链 |
| MyBatis | InterceptorChain | 插件拦截器链 |
| JDK | java.util.logging.Logger | 日志处理器链 |
八、与其他模式的关系
与装饰器模式
- 相似:都是链式结构,递归调用
- 区别:装饰器关注增强功能(每个装饰器必须传递请求),责任链关注请求分发(处理者可以选择不传递)
与组合模式
- 责任链模式常与组合模式结合使用,父组件可以作为子组件的后继处理者
与模板方法模式
- 抽象处理者中的
handleRequest()方法可以使用模板方法模式定义骨架
九、面试常见问题
Q1:责任链模式和装饰器模式有什么区别?
A:责任链模式中,每个处理者可以决定是否继续传递请求,请求可能在中途被处理而终止;装饰器模式中,每个装饰器都会执行自己的增强逻辑并一定将请求传递给下一个装饰器,最终必须到达被装饰的核心对象。
Q2:如何处理责任链中某个处理者抛出异常?
A:可以给每个处理者添加 try-catch 包裹,或者在抽象处理者中统一处理异常,记录日志后决定是继续传递还是中断。也可以引入补偿机制,让前面的处理者执行回滚。
Q3:如何保证责任链不出现死循环?
A:设计时确保每个处理者不会将请求转发给已经处理过的对象。可以通过计数器限制最大传递次数,或者在链中维护已访问的处理者集合,检测到重复则抛出异常。
Q4:责任链模式在 Spring Security 中是如何使用的?
A:Spring Security 使用 SecurityFilterChain 接口,每个 FilterChain 包含多个 Filter(如 UsernamePasswordAuthenticationFilter、ExceptionTranslationFilter 等),形成一条过滤器链,请求依次经过每个 Filter 进行认证、授权等处理。
Q5:纯责任链和不纯责任链各有什么优缺点?
A:纯责任链逻辑清晰,每个处理者职责单一,但可能导致重复代码;不纯责任链更灵活,每个处理者可以部分处理,但增加了复杂度,需要谨慎设计处理顺序。
