Java 责任链模式从入门到实战(后端必看,附案例+面试考点)
前言:责任链模式(Chain of Responsibility Pattern)是Java行为型设计模式中的核心模式之一,核心思想是"将请求的处理者串联成一条链,请求沿着链依次传递,直到某个处理者能够处理该请求为止",彻底解耦请求发送者与接收者,让多个处理者各司其职、灵活组合。
后端开发中,我们经常遇到"多步骤流程处理"的场景:比如用户注册时的参数校验、权限判断、数据预处理;订单支付后的库存扣减、积分增加、消息通知;接口请求的日志记录、限流控制、参数解密等。如果用传统的if-else或switch-case嵌套实现,代码会臃肿不堪、难以维护------新增一个处理步骤要修改原有代码,调整处理顺序要重构逻辑,排查问题时要在层层嵌套中找突破口。
本文从入门到实战,用极简语言拆解责任链模式核心,结合可直接复制运行的入门案例、真实业务实战(Spring Boot环境),以及高频面试考点,带你吃透责任链模式------新手能快速上手,中级开发者能落地项目,面试时能轻松应对追问,看完就能用在实际开发中。
一、为什么Java后端必须掌握责任链模式?(痛点直击)
先看3个Java后端开发中最常见的场景,你一定遇到过,这也是责任链模式的核心应用场景,更是面试中高频提及的"解耦"典型场景:
-
场景1:电商订单退款流程------用户申请退款,需依次经过"订单校验(是否可退)→ 库存回补 → 退款打款 → 消息通知",每个步骤对应一个处理逻辑,若用if-else嵌套,新增"优惠券回退"步骤需修改原有代码,耦合度极高;
-
场景2:接口请求拦截流程------接口调用前,需依次执行"日志记录 → 权限校验 → 限流控制 → 参数校验",每个拦截逻辑独立,若后续新增"接口加密校验",需修改拦截入口代码,扩展性极差;
-
场景3:多级审批流程------员工提交请假申请,需根据请假天数,依次经过"主管(≤3天)→ 经理(≤7天)→ 总经理(>7天)"审批,若后续调整审批权限或新增"总监审批"节点,需重构整个审批逻辑。
这些场景的共性问题:请求发送者与多个处理者强耦合,处理流程硬编码,新增/修改处理步骤需改动核心代码,违背开闭原则,代码可维护性、扩展性极差。
而责任链模式的核心价值,就是"解耦+灵活"------将每个处理步骤封装成独立的处理者对象,串联成一条链,请求从链的一端发起,沿着链依次传递,每个处理者只关注自己的核心职责,无需知道链的整体结构、其他处理者的逻辑,也无需知道请求的最终处理者是谁。
简单说,责任链模式就像"公司报销审批流程":员工提交报销单(请求),先交给部门经理(第一个处理者),部门经理可审批≤2000元的报销单,超出则传递给财务(下一个处理者),财务核对金额后传递给出纳(最后一个处理者)打款;每个审批者只做自己职责内的事,不用关心下一个审批者是谁、怎么处理,新增"总监审批"节点,只需插入链条,无需修改其他审批者的代码。
核心结论:责任链模式不是"花里胡哨"的设计,而是后端开发的"流程解耦神器"------初级开发者用它摆脱if-else嵌套,减少代码冗余;中级开发者用它设计可扩展的业务流程;高级开发者用它理解框架底层(如Spring MVC拦截器链),面试时更是中高级岗位的必问考点。
二、责任链模式核心概念(极简入门,无需死记硬背)
责任链模式的本质很简单:将多个处理请求的对象串联成一条链,请求沿着链传递,每个处理者自主决定是否处理请求,或传递给下一个处理者,实现请求发送者与接收者的解耦。
无论哪种实现方式,责任链模式都包含3个核心角色,用Java接口/抽象类定义,结构清晰,新手也能快速上手,这也是面试中常考的核心知识点。
2.1 核心角色(3个核心,必记)
-
抽象处理者(Handler):定义所有具体处理者的统一接口,包含两个核心方法------① 处理请求的方法(handleRequest),用于接收并处理请求;② 设置下一个处理者的方法(setNextHandler),用于将处理者串联成链,持有下一个处理者的引用。
-
具体处理者(ConcreteHandler):实现抽象处理者接口,重写处理请求的方法。在方法中判断自己是否能处理当前请求:能处理则直接执行处理逻辑;不能处理则将请求传递给下一个处理者(若有);若没有下一个处理者,则表示请求未被处理(可抛出异常或返回提示)。
-
请求对象(Request):封装请求的相关信息(如报销单的金额、申请人;订单的ID、退款金额等),方便处理者获取信息并执行处理逻辑,避免处理者方法参数过多,提升代码可读性和可维护性。
核心原则:每个处理者只关注自己的职责,不关心链的整体结构和其他处理者的逻辑;请求发送者只需将请求发送到链的起点,无需关心请求的处理过程和最终处理者。这是责任链模式的灵魂,也是它与传统if-else嵌套的核心区别。
补充:责任链模式分为两种实现形态------纯责任链(每个处理者要么完全处理请求,要么完全传递,最终必有一个处理者处理请求)和不纯责任链(处理者可部分处理请求后再传递,多个处理者共同处理一个请求,也允许请求最终无人处理),实际开发中不纯责任链更常用(如Spring MVC拦截器链)。
2.2 责任链模式的核心流程(一句话看懂)
客户端创建请求对象 → 创建所有具体处理者,通过setNextHandler串联成链 → 客户端将请求发送给链的第一个处理者 → 处理者判断是否能处理请求:能处理则执行逻辑,不能处理则传递给下一个处理者 → 依次传递,直到请求被处理(或到达链尾)。
三、责任链模式入门实现(附可复制代码,新手必练)
以"请假审批流程"为入门案例,模拟公司请假审批规则:员工提交请假申请,主管可审批≤3天,经理可审批≤7天,总经理可审批所有天数(无上限),用责任链模式实现,对比"传统if-else实现"和"责任链模式实现"的差异,一看就懂、一练就会。
3.1 普通实现:if-else嵌套的反例(痛点凸显)
若不使用责任链模式,用if-else嵌套实现请假审批,会导致代码臃肿、耦合度高,后续调整审批权限或新增审批节点,需修改所有嵌套逻辑,维护成本极高。
java
// 请假请求对象(封装请求信息)
public class LeaveRequest {
private String employeeName; // 员工姓名
private int leaveDays; // 请假天数
private String reason; // 请假原因
// 构造器、getter/setter
public LeaveRequest(String employeeName, int leaveDays, String reason) {
this.employeeName = employeeName;
this.leaveDays = leaveDays;
this.reason = reason;
}
// getter方法(处理者需获取请求信息)
public String getEmployeeName() { return employeeName; }
public int getLeaveDays() { return leaveDays; }
public String getReason() { return reason; }
}
// 请假审批服务(if-else嵌套实现,耦合度极高)
public class LeaveApprovalService {
// 审批方法(所有审批逻辑嵌套在一起)
public void approve(LeaveRequest request) {
int leaveDays = request.getLeaveDays();
String employeeName = request.getEmployeeName();
if (leaveDays <= 3) {
// 主管审批
System.out.println("主管审批通过:员工" + employeeName + "请假" + leaveDays + "天,原因:" + request.getReason());
} else if (leaveDays <= 7) {
// 经理审批
System.out.println("经理审批通过:员工" + employeeName + "请假" + leaveDays + "天,原因:" + request.getReason());
} else {
// 总经理审批
System.out.println("总经理审批通过:员工" + employeeName + "请假" + leaveDays + "天,原因:" + request.getReason());
}
// 痛点:
// 1. 若新增"总监审批"(4-7天),需修改if-else判断条件,违背开闭原则
// 2. 所有审批逻辑耦合在一起,代码臃肿,维护难度大
// 3. 审批顺序固定,无法动态调整(如先经理审批再主管审批)
}
}
// 客户端调用
public class Client {
public static void main(String[] args) {
LeaveApprovalService service = new LeaveApprovalService();
// 测试3种请假场景
service.approve(new LeaveRequest("张三", 2, "事假"));
service.approve(new LeaveRequest("李四", 5, "病假"));
service.approve(new LeaveRequest("王五", 10, "年假"));
}
}
【运行结果】:
text
主管审批通过:员工张三请假2天,原因:事假
经理审批通过:员工李四请假5天,原因:病假
总经理审批通过:员工王五请假10天,原因:年假
【缺点极其明显】:
-
耦合度极高:所有审批逻辑耦合在一个方法中,新增/修改审批节点需修改原有代码,违背开闭原则;
-
代码臃肿:审批节点越多,if-else嵌套层数越多,代码可读性、可维护性极差;
-
灵活性差:审批顺序固定,无法动态调整,也无法动态新增/删除审批节点;
-
职责混乱:一个方法承担所有审批逻辑,违背单一职责原则,排查问题时难以定位。
3.2 责任链模式实现:解耦的优雅代码(正例)
用责任链模式重构请假审批案例,定义抽象处理者、具体处理者和请求对象,将每个审批节点封装成独立的处理者,串联成链,实现审批逻辑与请求发送者的解耦,后续新增/修改审批节点无需修改原有代码。
java
// 1. 请求对象:请假请求(封装请求信息,不变)
public class LeaveRequest {
private String employeeName; // 员工姓名
private int leaveDays; // 请假天数
private String reason; // 请假原因
public LeaveRequest(String employeeName, int leaveDays, String reason) {
this.employeeName = employeeName;
this.leaveDays = leaveDays;
this.reason = reason;
}
// getter方法
public String getEmployeeName() { return employeeName; }
public int getLeaveDays() { return leaveDays; }
public String getReason() { return reason; }
}
// 2. 抽象处理者:审批者(定义统一接口)
public abstract class ApproverHandler {
// 持有下一个审批者的引用(用于串联成链)
protected ApproverHandler nextHandler;
// 设置下一个审批者(支持链式调用,如supervisor.setNext(manager).setNext(generalManager))
public ApproverHandler setNextHandler(ApproverHandler nextHandler) {
this.nextHandler = nextHandler;
return nextHandler; // 链式调用关键:返回下一个处理者
}
// 抽象审批方法(核心,子类必须实现)
public abstract void handleRequest(LeaveRequest request);
}
// 3. 具体处理者1:主管(可审批≤3天)
public class SupervisorHandler extends ApproverHandler {
@Override
public void handleRequest(LeaveRequest request) {
int leaveDays = request.getLeaveDays();
// 判断自己是否能处理
if (leaveDays <= 3) {
System.out.println("主管审批通过:员工" + request.getEmployeeName() + "请假" + leaveDays + "天,原因:" + request.getReason());
} else {
// 不能处理,传递给下一个审批者
if (nextHandler != null) {
System.out.println("主管:请假天数超出我的审批权限,传递给经理审批");
nextHandler.handleRequest(request);
} else {
// 没有下一个审批者,审批失败
System.out.println("审批失败:员工" + request.getEmployeeName() + "请假天数超出所有审批权限");
}
}
}
}
// 4. 具体处理者2:经理(可审批≤7天)
public class ManagerHandler extends ApproverHandler {
@Override
public void handleRequest(LeaveRequest request) {
int leaveDays = request.getLeaveDays();
// 判断自己是否能处理(仅处理3-7天,因为≤3天已被主管处理)
if (leaveDays > 3 && leaveDays <= 7) {
System.out.println("经理审批通过:员工" + request.getEmployeeName() + "请假" + leaveDays + "天,原因:" + request.getReason());
} else {
// 不能处理,传递给下一个审批者
if (nextHandler != null) {
System.out.println("经理:请假天数超出我的审批权限,传递给总经理审批");
nextHandler.handleRequest(request);
} else {
System.out.println("审批失败:员工" + request.getEmployeeName() + "请假天数超出所有审批权限");
}
}
}
}
// 5. 具体处理者3:总经理(可审批所有天数,链的终结节点)
public class GeneralManagerHandler extends ApproverHandler {
@Override
public void handleRequest(LeaveRequest request) {
// 总经理无审批上限,直接处理
System.out.println("总经理审批通过:员工" + request.getEmployeeName() + "请假" + request.getLeaveDays() + "天,原因:" + request.getReason());
}
}
// 6. 客户端调用(构建责任链,发起请求)
public class Client {
public static void main(String[] args) {
// 1. 创建所有具体处理者
ApproverHandler supervisor = new SupervisorHandler(); // 主管
ApproverHandler manager = new ManagerHandler(); // 经理
ApproverHandler generalManager = new GeneralManagerHandler(); // 总经理
// 2. 构建责任链:主管 → 经理 → 总经理(链式调用,简洁优雅)
supervisor.setNextHandler(manager).setNextHandler(generalManager);
// 3. 发起请求(只需将请求发送给链的起点,无需关心后续处理者)
System.out.println("=== 测试1:请假2天 ===");
supervisor.handleRequest(new LeaveRequest("张三", 2, "事假"));
System.out.println("\n=== 测试2:请假5天 ===");
supervisor.handleRequest(new LeaveRequest("李四", 5, "病假"));
System.out.println("\n=== 测试3:请假10天 ===");
supervisor.handleRequest(new LeaveRequest("王五", 10, "年假"));
// 亮点1:新增"总监审批"节点,只需新增DirectorHandler类,插入链条即可,无需修改原有代码
// 亮点2:可动态调整审批顺序,如"经理→主管→总经理",只需修改setNextHandler的顺序
// 亮点3:每个审批者职责单一,代码清晰,排查问题时可快速定位
}
}
【运行结果】:
text
=== 测试1:请假2天 ===
主管审批通过:员工张三请假2天,原因:事假
=== 测试2:请假5天 ===
主管:请假天数超出我的审批权限,传递给经理审批
经理审批通过:员工李四请假5天,原因:病假
=== 测试3:请假10天 ===
主管:请假天数超出我的审批权限,传递给经理审批
经理:请假天数超出我的审批权限,传递给总经理审批
总经理审批通过:员工王五请假10天,原因:年假
【代码优势极其明显】:
-
解耦彻底:请求发送者(客户端)只需将请求发送给链的起点,无需知道后续处理者是谁、处理逻辑是什么;处理者之间也互不依赖,新增/修改处理者无需修改原有代码,符合开闭原则;
-
职责清晰:每个处理者只负责自己的审批范围,符合单一职责原则,代码可读性、可维护性大幅提升;
-
灵活性高:可动态调整处理者的顺序、新增/删除处理者,无需重构核心逻辑(如新增总监审批,只需新增类并插入链条);
-
可扩展性强:后续新增审批规则(如加班审批、报销审批),可复用现有责任链结构,只需新增对应的处理者和请求对象。
【核心总结】:责任链模式的核心不是"串联处理",而是"解耦请求与处理"------通过抽象接口定义处理规范,将每个处理步骤封装成独立对象,串联成链,让请求沿着链自动传递,既解决了if-else嵌套的臃肿问题,又提升了代码的可扩展性和可维护性。
四、责任链模式实战(真实业务场景,可直接复用)
结合Java后端最常见的"电商订单创建流程",用责任链模式实现"订单创建前的多步骤校验与处理",贴合真实项目开发(Spring Boot环境),代码可直接复制到项目中使用,解决实际开发中的流程解耦痛点。
4.1 实战场景说明
场景:电商系统中,用户创建订单时,需依次经过5个处理步骤,每个步骤独立,后续可能新增/调整步骤:
-
参数校验处理器:校验订单参数(用户ID、商品ID、购买数量是否合法);
-
库存校验处理器:校验商品库存是否充足;
-
优惠券抵扣处理器:若用户使用优惠券,校验优惠券是否有效并抵扣金额;
-
价格计算处理器:计算订单最终价格(商品原价-优惠券抵扣);
-
订单保存处理器:将订单信息保存到数据库,完成订单创建。
核心需求:各处理步骤解耦,新增处理步骤(如"会员折扣处理器")无需修改原有代码;支持动态调整处理步骤顺序;结合Spring Boot依赖注入,贴合真实项目开发规范;支持异常处理,某个步骤失败则终止流程。
4.2 实战代码实现(Spring Boot环境,可直接复用)
java
// 1. 公共实体:订单请求对象(封装订单相关信息)
@Data
public class OrderRequest {
private Long userId; // 用户ID
private Long productId; // 商品ID
private Integer quantity; // 购买数量
private Long couponId; // 优惠券ID(可选)
private Double originalPrice; // 商品原价
private Double finalPrice; // 最终价格(处理后赋值)
}
// 2. 公共实体:响应结果封装(贴合真实接口开发规范)
@Data
public class Result {
private Integer code; // 200成功,500失败
private String message;
private Object data;
public static Result success(String message, Object data) {
Result result = new Result();
result.setCode(200);
result.setMessage(message);
result.setData(data);
return result;
}
public static Result fail(String message) {
Result result = new Result();
result.setCode(500);
result.setMessage(message);
return result;
}
}
// 3. 抽象处理者:订单处理器(定义统一接口)
public interface OrderHandler {
// 处理订单请求,返回处理结果(失败则终止流程)
Result handle(OrderRequest request);
// 设置下一个处理器(用于串联成链)
OrderHandler setNextHandler(OrderHandler nextHandler);
}
// 4. 抽象实现类:基础处理器(实现setNextHandler,避免子类重复实现)
public abstract class AbstractOrderHandler implements OrderHandler {
protected OrderHandler nextHandler; // 下一个处理器
@Override
public OrderHandler setNextHandler(OrderHandler nextHandler) {
this.nextHandler = nextHandler;
return nextHandler; // 支持链式调用
}
}
// 5. 具体处理者1:参数校验处理器
@Component
public class ParamCheckHandler extends AbstractOrderHandler {
@Override
public Result handle(OrderRequest request) {
// 1. 校验参数合法性
if (request.getUserId() == null || request.getUserId() <= 0) {
return Result.fail("用户ID不合法");
}
if (request.getProductId() == null || request.getProductId() <= 0) {
return Result.fail("商品ID不合法");
}
if (request.getQuantity() == null || request.getQuantity() <= 0) {
return Result.fail("购买数量不合法");
}
if (request.getOriginalPrice() == null || request.getOriginalPrice() <= 0) {
return Result.fail("商品原价不合法");
}
System.out.println("参数校验处理器:参数校验通过");
// 参数校验通过,传递给下一个处理器
if (nextHandler != null) {
return nextHandler.handle(request);
}
// 没有下一个处理器,返回成功(理论上不会走到这,因为最后一个是保存处理器)
return Result.success("订单处理成功", null);
}
}
// 6. 具体处理者2:库存校验处理器
@Component
public class StockCheckHandler extends AbstractOrderHandler {
@Override
public Result handle(OrderRequest request) {
// 模拟库存校验(真实项目中从数据库查询库存)
int stock = 100; // 模拟商品库存
if (request.getQuantity() > stock) {
return Result.fail("商品库存不足,当前库存:" + stock);
}
System.out.println("库存校验处理器:库存充足,校验通过");
// 库存校验通过,传递给下一个处理器
if (nextHandler != null) {
return nextHandler.handle(request);
}
return Result.success("订单处理成功", null);
}
}
// 7. 具体处理者3:优惠券抵扣处理器
@Component
public class CouponDeductHandler extends AbstractOrderHandler {
@Override
public Result handle(OrderRequest request) {
Long couponId = request.getCouponId();
// 若未使用优惠券,直接传递给下一个处理器
if (couponId == null || couponId <= 0) {
System.out.println("优惠券抵扣处理器:未使用优惠券,直接放行");
if (nextHandler != null) {
return nextHandler.handle(request);
}
return Result.success("订单处理成功", null);
}
// 模拟优惠券校验(真实项目中从数据库查询优惠券信息)
double couponAmount = 10.0; // 模拟优惠券抵扣金额
if (couponAmount > request.getOriginalPrice()) {
return Result.fail("优惠券抵扣金额大于商品原价,抵扣失败");
}
// 优惠券校验通过,抵扣金额
request.setFinalPrice(request.getOriginalPrice() - couponAmount);
System.out.println("优惠券抵扣处理器:优惠券校验通过,抵扣金额:" + couponAmount + "元");
// 传递给下一个处理器
if (nextHandler != null) {
return nextHandler.handle(request);
}
return Result.success("订单处理成功", null);
}
}
// 8. 具体处理者4:价格计算处理器
@Component
public class PriceCalculateHandler extends AbstractOrderHandler {
@Override
public Result handle(OrderRequest request) {
// 若未使用优惠券,最终价格 = 原价
if (request.getFinalPrice() == null) {
request.setFinalPrice(request.getOriginalPrice());
}
System.out.println("价格计算处理器:订单最终价格计算完成,最终价格:" + request.getFinalPrice() + "元");
// 价格计算完成,传递给下一个处理器
if (nextHandler != null) {
return nextHandler.handle(request);
}
return Result.success("订单处理成功", null);
}
}
// 9. 具体处理者5:订单保存处理器(链的终结节点)
@Component
public class OrderSaveHandler extends AbstractOrderHandler {
@Override
public Result handle(OrderRequest request) {
// 模拟订单保存到数据库(真实项目中调用DAO层保存)
Long orderId = System.currentTimeMillis(); // 模拟订单ID
request.setFinalPrice(request.getFinalPrice() == null ? request.getOriginalPrice() : request.getFinalPrice());
System.out.println("订单保存处理器:订单保存成功,订单ID:" + orderId + ",最终价格:" + request.getFinalPrice() + "元");
// 终结节点,无需传递给下一个处理器,返回成功结果
return Result.success("订单创建成功", orderId);
}
}
// 10. 责任链工厂:构建责任链(统一管理链的构建,避免客户端硬编码)
@Component
public class OrderHandlerChainFactory {
// 注入所有订单处理器(Spring自动将OrderHandler实现类注入到List中)
private final List<OrderHandler> orderHandlers;
// 构造方法注入
public OrderHandlerChainFactory(List<OrderHandler> orderHandlers) {
this.orderHandlers = orderHandlers;
}
// 构建责任链(按指定顺序串联处理器)
public OrderHandler buildChain() {
if (orderHandlers.isEmpty()) {
throw new RuntimeException("订单处理器不能为空");
}
// 按处理器类型排序(确保顺序:参数校验→库存校验→优惠券抵扣→价格计算→订单保存)
OrderHandler paramCheckHandler = orderHandlers.stream()
.filter(handler -> handler instanceof ParamCheckHandler)
.findFirst()
.orElseThrow(() -> new RuntimeException("未找到参数校验处理器"));
OrderHandler stockCheckHandler = orderHandlers.stream()
.filter(handler -> handler instanceof StockCheckHandler)
.findFirst()
.orElseThrow(() -> new RuntimeException("未找到库存校验处理器"));
OrderHandler couponDeductHandler = orderHandlers.stream()
.filter(handler -> handler instanceof CouponDeductHandler)
.findFirst()
.orElseThrow(() -> new RuntimeException("未找到优惠券抵扣处理器"));
OrderHandler priceCalculateHandler = orderHandlers.stream()
.filter(handler -> handler instanceof PriceCalculateHandler)
.findFirst()
.orElseThrow(() -> new RuntimeException("未找到价格计算处理器"));
OrderHandler orderSaveHandler = orderHandlers.stream()
.filter(handler -> handler instanceof OrderSaveHandler)
.findFirst()
.orElseThrow(() -> new RuntimeException("未找到订单保存处理器"));
// 串联成链:参数校验 → 库存校验 → 优惠券抵扣 → 价格计算 → 订单保存
paramCheckHandler.setNextHandler(stockCheckHandler)
.setNextHandler(couponDeductHandler)
.setNextHandler(priceCalculateHandler)
.setNextHandler(orderSaveHandler);
return paramCheckHandler; // 返回链的起点
}
}
// 11. 服务层:订单服务(调用责任链处理订单)
@Service
public class OrderService {
private final OrderHandler orderHandlerChain;
// 注入责任链(通过工厂构建)
public OrderService(OrderHandlerChainFactory chainFactory) {
this.orderHandlerChain = chainFactory.buildChain();
}
// 创建订单(对外提供服务)
public Result createOrder(OrderRequest request) {
try {
// 发起请求(只需调用链的起点,无需关心后续处理)
return orderHandlerChain.handle(request);
} catch (Exception e) {
return Result.fail("订单创建失败:" + e.getMessage());
}
}
}
// 12. 控制层:接口对外提供访问(Spring Boot Controller)
@RestController
@RequestMapping("/order")
public class OrderController {
private final OrderService orderService;
public OrderController(OrderService orderService) {
this.orderService = orderService;
}
// 创建订单接口
@PostMapping("/create")
public Result createOrder(@RequestBody OrderRequest request) {
return orderService.createOrder(request);
}
}
// 13. 测试类(模拟接口调用,Spring Boot环境可直接注入测试)
public class OrderChainTest {
public static void main(String[] args) {
// 模拟Spring容器注入所有处理器
List<OrderHandler> handlers = new ArrayList<>();
handlers.add(new ParamCheckHandler());
handlers.add(new StockCheckHandler());
handlers.add(new CouponDeductHandler());
handlers.add(new PriceCalculateHandler());
handlers.add(new OrderSaveHandler());
// 构建责任链
OrderHandlerChainFactory factory = new OrderHandlerChainFactory(handlers);
OrderHandler chain = factory.buildChain();
// 模拟订单请求(使用优惠券)
OrderRequest request1 = new OrderRequest();
request1.setUserId(1L);
request1.setProductId(1001L);
request1.setQuantity(2);
request1.setOriginalPrice(100.0);
request1.setCouponId(10001L);
// 处理订单
Result result1 = chain.handle(request1);
System.out.println("=== 使用优惠券的订单 ===");
System.out.println(result1);
// 模拟订单请求(不使用优惠券)
OrderRequest request2 = new OrderRequest();
request2.setUserId(2L);
request2.setProductId(1002L);
request2.setQuantity(1);
request2.setOriginalPrice(50.0);
// 不设置couponId,即不使用优惠券
Result result2 = chain.handle(request2);
System.out.println("\n=== 不使用优惠券的订单 ===");
System.out.println(result2);
// 模拟订单请求(参数不合法,测试流程终止)
OrderRequest request3 = new OrderRequest();
request3.setUserId(-1L); // 非法用户ID
request3.setProductId(1003L);
request3.setQuantity(3);
request3.setOriginalPrice(150.0);
Result result3 = chain.handle(request3);
System.out.println("\n=== 参数不合法的订单 ===");
System.out.println(result3);
}
}
【运行结果】:
text
参数校验处理器:参数校验通过
库存校验处理器:库存充足,校验通过
优惠券抵扣处理器:优惠券校验通过,抵扣金额:10.0元
价格计算处理器:订单最终价格计算完成,最终价格:90.0元
订单保存处理器:订单保存成功,订单ID:1713327600000,最终价格:90.0元
=== 使用优惠券的订单 ===
Result(code=200, message=订单创建成功, data=1713327600000)
参数校验处理器:参数校验通过
库存校验处理器:库存充足,校验通过
优惠券抵扣处理器:未使用优惠券,直接放行
价格计算处理器:订单最终价格计算完成,最终价格:50.0元
订单保存处理器:订单保存成功,订单ID:1713327600001,最终价格:50.0元
=== 不使用优惠券的订单 ===
Result(code=200, message=订单创建成功, data=1713327600001)
=== 参数不合法的订单 ===
Result(code=500, message=用户ID不合法)
【实战亮点】:
-
贴合Spring Boot实战:使用@Component、@Service、@RestController等注解,利用Spring自动注入处理器,通过工厂类统一构建责任链,避免客户端硬编码,符合真实项目开发规范;
-
解耦彻底:各处理步骤封装成独立处理器,新增/修改处理器无需修改原有代码(如新增"会员折扣处理器",只需新增类并调整工厂类的链顺序);
-
流程可控:支持异常处理,某个步骤失败(如参数不合法、库存不足),直接终止流程并返回失败信息,避免无效处理;
-
灵活性高:可通过工厂类动态调整处理器顺序,无需修改核心业务逻辑;
-
可复用性强:处理器可在其他流程中复用(如参数校验处理器可复用在订单修改、订单取消等流程);
-
接口规范:统一响应结果封装,贴合真实接口开发需求,便于前端对接。
补充:真实项目中,可结合配置文件动态配置处理器顺序(如在yaml中定义处理器顺序,工厂类读取配置构建链),实现更灵活的流程配置,无需修改代码即可调整处理顺序。
五、责任链模式在JDK/框架中的应用(面试必提)
责任链模式的核心价值是"流程解耦、灵活扩展",这也是它被广泛应用在JDK源码和主流Java框架中的原因,掌握这些应用场景,面试时能加分不少,还能帮助你理解框架底层设计思想。
5.1 JDK 中的责任链模式(最常见,面试高频)
JDK中最典型的责任链模式应用是java\.util\.logging\.Logger(日志处理器链),不同级别的日志处理器串联成链,日志请求沿着链传递,直到被某个处理器处理(或所有处理器处理完毕)。
java
// JDK Logger责任链示例
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
public class JdkLoggerChainTest {
public static void main(String[] args) {
// 1. 创建日志处理器(具体处理者)
ConsoleHandler consoleHandler = new ConsoleHandler(); // 控制台日志处理器
consoleHandler.setLevel(Level.INFO); // 只处理INFO及以上级别日志
// 2. 创建日志记录器(链的起点)
Logger logger = Logger.getLogger("OrderLogger");
logger.setLevel(Level.ALL); // 接收所有级别日志
logger.addHandler(consoleHandler); // 将处理器加入链
// 3. 发起日志请求(日志沿着链传递)
logger.severe("【严重错误】订单创建失败,数据库连接异常"); // SEVERE级别,会被处理
logger.warning("【警告】商品库存不足,即将售罄"); // WARNING级别,会被处理
logger.info("【信息】订单创建成功,订单ID:123456"); // INFO级别,会被处理
logger.fine("【详细信息】用户ID:1,购买商品ID:1001"); // FINE级别,低于处理器级别,不被处理
// 核心原理:
// Logger是抽象处理者,ConsoleHandler是具体处理者
// 日志请求从Logger发起,传递给所有添加的Handler,每个Handler判断是否处理该级别日志
// 符合责任链模式的核心逻辑:请求链式传递,处理者自主判断是否处理
}
}
【核心注意点】:
-
JDK的Logger责任链属于"不纯责任链",多个处理器可共同处理一个请求(如同时添加控制台处理器和文件处理器,日志会同时输出到控制台和文件);
-
每个Handler有自己的级别,只有日志级别高于等于Handler的级别,才会被处理,否则会被忽略(相当于"不能处理,传递给下一个处理器");
-
可通过addHandler()方法动态添加处理器,removeHandler()方法删除处理器,灵活调整责任链。
5.2 框架中的责任链模式(实战必掌握)
5.2.1 Spring MVC 中的拦截器链(最常用)
Spring MVC的HandlerInterceptor(拦截器)是责任链模式的典型实现,多个拦截器串联成链,对HTTP请求进行预处理和后处理,请求沿着拦截器链依次传递,每个拦截器完成自己的职责后放行,直到请求到达Controller。
核心对应关系:
-
抽象处理者:
HandlerInterceptor接口(定义preHandle、postHandle、afterCompletion三个方法); -
具体处理者:自定义拦截器(实现HandlerInterceptor接口,如登录拦截器、日志拦截器);
-
责任链管理:
HandlerExecutionChain类(管理拦截器链,负责调用拦截器的方法,控制请求传递); -
请求对象:
HttpServletRequest、HttpServletResponse。
java
// Spring MVC 自定义拦截器示例(登录拦截器)
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// 具体处理者:登录拦截器
public class LoginInterceptor implements HandlerInterceptor {
// 预处理:请求到达Controller前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 模拟登录校验(真实项目中从Session或Token获取用户信息)
String token = request.getHeader("token");
if (token == null || token.isEmpty()) {
response.setStatus(401);
response.getWriter().write("未登录,请先登录");
return false; // 拦截请求,终止链的传递
}
// 登录校验通过,放行,传递给下一个拦截器
System.out.println("登录拦截器:登录校验通过,放行");
return true;
}
}
// 配置拦截器链(Spring Boot配置类)
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 WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 构建拦截器链:登录拦截器 → 日志拦截器
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") // 拦截所有请求
.excludePathPatterns("/login"); // 排除登录接口
registry.addInterceptor(new LogInterceptor())
.addPathPatterns("/**");
}
}
// 日志拦截器(另一个具体处理者)
class LogInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("日志拦截器:记录请求信息,请求URL:" + request.getRequestURI());
return true; // 放行,传递给下一个拦截器
}
}
核心流程:浏览器发起请求 → 登录拦截器preHandle(校验登录) → 日志拦截器preHandle(记录日志) → Controller处理请求 → 日志拦截器postHandle → 登录拦截器postHandle → 日志拦截器afterCompletion → 登录拦截器afterCompletion。
5.2.2 Spring AOP 中的方法拦截器链
Spring AOP中的MethodInterceptor(方法拦截器)也是责任链模式的应用,多个拦截器围绕目标方法形成一条链,方法调用时,拦截器依次执行环绕逻辑,完成事务管理、缓存处理、日志记录等横切关注点。
核心逻辑:ReflectiveMethodInvocation类管理拦截器链,通过递归调用的方式,让请求(方法调用)沿着拦截器链传递,每个拦截器执行自己的逻辑后,调用下一个拦截器,直到目标方法执行完毕。
5.2.3 Netty 中的 ChannelHandler 链
Netty中的ChannelHandler(通道处理器)是责任链模式的经典实现,用于处理网络请求,多个ChannelHandler串联成链,网络数据沿着链依次传递,每个处理器负责数据的编码、解码、业务处理等职责,属于纯责任链模式(每个请求必须被某一个处理器处理)。
六、责任链模式高频面试考点(必背)
责任链模式是Java后端面试高频考点,尤其是中高级岗位,重点考察核心思想、框架应用、与其他模式的区别,以下4个考点必背,看完直接应对面试追问。
1. 责任链模式的核心是什么?解决了什么问题?
必背答案:
核心:将请求的处理者串联成一条链,请求沿着链依次传递,每个处理者自主决定是否处理请求或传递给下一个处理者,解耦请求发送者与接收者。
解决的问题:
-
请求发送者与多个处理者强耦合,新增/修改处理步骤需修改原有代码;
-
if-else嵌套过多,代码臃肿、可读性差、维护成本高;
-
处理流程固定,无法动态调整处理顺序、新增/删除处理步骤;
-
处理者职责混乱,违背单一职责原则。
2. 责任链模式的两种实现形态(纯责任链 vs 不纯责任链)有什么区别?
经典面试题,必背:
-
纯责任链:
-
每个处理者要么完全处理请求,要么完全传递给下一个处理者;
-
请求最终必有一个处理者处理,不会出现"无人处理"的情况;
-
示例:Netty的ChannelHandler链、请假审批流程。
-
-
不纯责任链:
-
处理者可部分处理请求后,再传递给下一个处理者(多个处理者共同处理一个请求);
-
允许请求最终无人处理(可抛出异常或返回提示);
-
示例:Spring MVC的拦截器链、JDK的Logger日志链。
-
3. 责任链模式和观察者模式、策略模式的区别?(面试高频追问)
必背答案:
-
责任链模式 vs 观察者模式:
-
责任链模式:一对一传递,请求沿着链依次传递,每个处理者自主判断是否处理,最终可能一个处理者处理请求;
-
观察者模式:一对多通知,主题状态变更时,同时通知所有观察者,所有观察者都会执行处理逻辑,无传递过程。
-
-
责任链模式 vs 策略模式:
-
责任链模式:多个处理者串联,请求自动传递,处理者自主决定是否处理,无需客户端指定处理者;
-
策略模式:多个策略并列,客户端需主动指定一个策略执行,策略之间无关联,也无传递过程。
-
4. 责任链模式的优缺点?适用场景是什么?
必背答案:
优点:
-
解耦:请求发送者与接收者解耦,无需知道对方的存在,新增/修改处理者无需修改原有代码;
-
灵活:可动态调整处理者顺序、新增/删除处理者,符合开闭原则;
-
职责清晰:每个处理者只关注自己的核心职责,符合单一职责原则,代码可读性、可维护性提升;
-
可复用:处理者可在不同流程中复用(如参数校验处理器可复用在多个业务流程)。
缺点:
-
性能损耗:请求需沿着链依次传递,链过长会导致处理效率降低;
-
调试困难:请求的传递过程隐蔽,链过长时,排查问题难以定位具体哪个处理者出现异常;
-
可能出现请求无人处理:若链中没有处理者能处理请求,且未做异常处理,会导致请求"丢失";
-
类数量增多:每个处理步骤都需封装成独立的处理者类,可能导致类的数量增多。
适用场景:
-
请求需要经过多个处理步骤,且处理步骤可扩展、可调整(如订单创建、用户注册);
-
多级审批流程(如请假审批、报销审批);
-
请求拦截、过滤场景(如Spring MVC拦截器、接口请求限流);
-
日志处理、异常处理等场景。
七、总结
责任链模式是Java后端开发必掌握的流程解耦神器,核心思想就是"请求链式传递,处理者各司其职",通过将每个处理步骤封装成独立的处理者对象,串联成链,