一、前言:这坑我替你踩过了
最近在重构公司的订单审核系统,我接手了一段"祖传代码"——
一个 OrderService.process() 方法里堆了 200 多行 if-else:
if(风控没过) return;
if(库存不够) return;
if(没支付) return;
if(黑名单) return;
if(超区域) return;
if(优惠券失效) return;
// ... 后面还有 50 个 if
看得我 CPU 温度先上来了。
更离谱的是:产品说要加一个"大促活动专属审核",我盯着这堆 if-else 看了半小时,头发又少了几根。
最后没办法,上 责任链模式。改完之后:
- 加一个审核环节?新增一个类,链上插一节,完事。
- 调整顺序?改一行配置。
- 删一个环节?移除一个节点。
- 测试?每个节点单独测,互不干扰。
那一刻我才真正明白:
责任链模式(Chain of Responsibility),不是设计模式里的"选修课",而是企业级开发里的"必修课"。 不会它,你的代码迟早变成意大利面 🍝。
二、什么是责任链模式?
1. 一句话定义
将请求的发送者和接收者解耦,让多个对象都有机会处理这个请求,把这些对象连成一条链,请求沿着链传递,直到有一个对象处理它为止。
2. 生活里的责任链
你以为是新概念?其实你每天都在用。
| 场景 | 链路 |
|---|---|
| 请假审批 | 员工 → 组长 → 经理 → 总监 → CEO |
| 快递分拣 | 北京站 → 华北区 → 北京市 → 朝阳区 → 派送员 |
| 击鼓传花 | 鼓声不停,花就一直在传 |
| 公司报销 | 500 以下主管批,5000 以下经理批,50000 财务批 |
| Spring 请求 | CORS → 鉴权 → 日志 → 限流 → Controller |
核心本质就一句话:
发送者只管"扔出去",谁接手、怎么处理,发送者不关心。
3. 三个核心角色
| 角色 | 职责 | 人话翻译 |
|---|---|---|
| 抽象处理者(Handler) | 定义处理接口,持有下一个处理者引用 | 审批模板,规定"你能批多少,批不了交给谁" |
| 具体处理者(ConcreteHandler) | 实现处理逻辑,能处理就处理,处理不了就往下传 | 组长、经理、总监,每个人各管一段 |
| 客户端(Client) | 拼装责任链,发起请求 | 拼链路的人,把节点串起来 |
三、纯 vs 不纯责任链
这个点面试经常问,但很多人说不清。
1. 纯责任链
一个请求只能被一个处理者处理,处理完就停。
典型场景:请假审批。
- 组长批了 → 后面经理、总监都不会再碰这件事。
2. 不纯责任链
请求可以经过多个处理者,每个都做点事。
典型场景:Spring 过滤器链。
- 日志过滤器 → 鉴权过滤器 → 限流过滤器 → Controller
- 每个过滤器都"动一下"这个请求,最后所有节点都执行完。
怎么区分?
看你需求是"找一个能处理的人"(纯),还是"让一串人依次处理"(不纯)。
四、Java 代码实现(从入门到企业级)
版本一:青铜版(纯责任链 - 请假审批)
Step 1:请假请求实体
/**
* 请假请求 - 责任链里传递的"包裹"
*/
@Data
@AllArgsConstructor
public class LeaveRequest {
/** 请假人 */
private String name;
/** 请假天数 */
private int days;
/** 请假原因 */
private String reason;
}
Step 2:抽象处理者
/**
* 抽象审批者 - 所有审批人的"爸爸"
*/
public abstract class Approver {
/** 下一个审批人 - 链的核心 */
protected Approver next;
/** 当前审批人姓名 */
protected String name;
public Approver(String name) {
this.name = name;
}
/**
* 设置下一个审批人(支持链式调用)
* 返回 next 是为了能 .setNext(a).setNext(b) 一直点下去
*/
public Approver setNext(Approver next) {
this.next = next;
return next;
}
/**
* 抽象审批方法 - 子类必须实现自己的审批规则
*/
public abstract void approve(LeaveRequest request);
}
Step 3:具体处理者
/** 组长:批 3 天以内 */
public class TeamLeader extends Approver {
public TeamLeader() { super("组长"); }
@Override
public void approve(LeaveRequest request) {
if (request.getDays() <= 3) {
System.out.println("✅ " + name + " 批了 " + request.getName()
+ " 的 " + request.getDays() + " 天假");
} else if (next != null) {
// 超出权限?踢给下家
next.approve(request);
} else {
System.out.println("❌ 没人能批,没人能背锅");
}
}
}
/** 经理:批 3-7 天 */
public class Manager extends Approver {
public Manager() { super("经理"); }
@Override
public void approve(LeaveRequest request) {
if (request.getDays() <= 7) {
System.out.println("✅ " + name + " 批了 " + request.getName()
+ " 的 " + request.getDays() + " 天假");
} else if (next != null) {
next.approve(request);
} else {
System.out.println("❌ 没人能批");
}
}
}
/** 总监:批 7-30 天 */
public class Director extends Approver {
public Director() { super("总监"); }
@Override
public void approve(LeaveRequest request) {
if (request.getDays() <= 30) {
System.out.println("✅ " + name + " 批了 " + request.getName()
+ " 的 " + request.getDays() + " 天假");
} else if (next != null) {
next.approve(request);
} else {
System.out.println("❌ " + request.getName()
+ " 你这是请假还是辞职?");
}
}
}
Step 4:客户端拼装
public class Client {
public static void main(String[] args) {
// 1. 先 new 出所有节点
Approver teamLeader = new TeamLeader();
Approver manager = new Manager();
Approver director = new Director();
// 2. 拼成链:组长 → 经理 → 总监
teamLeader.setNext(manager).setNext(director);
// 3. 员工提交请求,只管扔给链头
teamLeader.approve(new LeaveRequest("张三", 1, "感冒"));
teamLeader.approve(new LeaveRequest("李四", 5, "婚礼"));
teamLeader.approve(new LeaveRequest("王五", 15, "出国"));
}
}
输出:
✅ 组长 批了 张三 的 1 天假
✅ 经理 批了 李四 的 5 天假
✅ 总监 批了 王五 的 15 天假
是不是很清晰?员工不用关心"我这个假该找谁",直接交给链头就行。
版本二:王者版(企业级 - 订单审核)
青铜版能跑,但生产环境你会发现:
- ❌ 处理器是写死的
new - ❌ 顺序硬编码
- ❌ 每个处理器重复
if-else判断 - ❌ 想插一个新节点?得改代码
优化思路:用 Spring + 模板方法 + 责任链,做成"可配置"的责任链。
Step 1:处理器接口
/**
* 订单处理器 - 顶层接口
*/
public interface OrderHandler {
/**
* 处理订单
* @param order 订单
* @return true=继续往下传,false=终止链
*/
boolean handle(Order order);
}
Step 2:抽象模板(消除重复 if-else)
/**
* 抽象处理器 - 模板方法模式 + 责任链
* 把"判断 + 传递"逻辑抽到父类,子类只关心"自己能处理啥"
*/
public abstract class AbstractOrderHandler implements OrderHandler {
@Getter
private final String name;
public AbstractOrderHandler(String name) {
this.name = name;
}
/**
* 模板方法:处理流程固定
*/
@Override
public final boolean handle(Order order) {
// 前置日志
log.info("🔗 [{}] 开始处理订单 {}", name, order.getId());
// 1. 执行自己的处理逻辑
boolean passed = doHandle(order);
if (!passed) {
log.warn("❌ [{}] 拦截订单 {}", name, order.getId());
// 拦截后也要执行后置,避免资源泄漏
afterHandle(order, false);
return false; // 终止链
}
// 2. 找下一个节点
OrderHandler next = next();
boolean nextResult = (next != null) ? next.handle(order) : true;
// 3. 后置处理(无论成功失败都执行)
afterHandle(order, nextResult);
return nextResult;
}
/** 子类实现:具体的处理逻辑 */
protected abstract boolean doHandle(Order order);
/** 子类实现:下一个节点是谁 */
protected abstract OrderHandler next();
/** 钩子方法:后置处理,默认空实现 */
protected void afterHandle(Order order, boolean success) {
// 默认啥也不干,子类按需重写
}
}
Step 3:具体处理器
/** 风控检查 */
@Component
public class RiskHandler extends AbstractOrderHandler {
public RiskHandler() { super("风控检查"); }
@Override
protected boolean doHandle(Order order) {
// 业务:检查账号是否在黑名单
return !blackListService.isBlack(order.getUserId());
}
@Override
protected OrderHandler next() {
return SpringContextHolder.getBean(InventoryHandler.class);
}
}
/** 库存检查 */
@Component
public class InventoryHandler extends AbstractOrderHandler {
public InventoryHandler() { super("库存检查"); }
@Override
protected boolean doHandle(Order order) {
return inventoryService.check(order.getSkuId(), order.getCount());
}
@Override
protected OrderHandler next() {
return SpringContextHolder.getBean(PayHandler.class);
}
}
/** 支付检查 */
@Component
public class PayHandler extends AbstractOrderHandler {
public PayHandler() { super("支付检查"); }
@Override
protected boolean doHandle(Order order) {
return payService.isPaid(order.getOrderId());
}
@Override
protected OrderHandler next() {
return null; // 我是链尾,没下家了
}
}
Step 4:链的拼装(用配置类,顺序可配)
/**
* 责任链工厂 - 链的"组装车间"
*/
@Configuration
public class OrderHandlerChainConfig {
@Bean("orderHandlerChain")
public OrderHandler orderHandlerChain(
RiskHandler risk,
InventoryHandler inventory,
PayHandler pay) {
// 链式组装:风控 → 库存 → 支付
risk.setNext(inventory).setNext(pay);
return risk;
}
}
Step 5:使用
@Service
public class OrderService {
@Resource(name = "orderHandlerChain")
private OrderHandler orderHandlerChain;
public void submit(Order order) {
boolean success = orderHandlerChain.handle(order);
if (success) {
// 走到这里,说明整条链都通过了
saveOrder(order);
} else {
// 任意一环失败
log.info("订单 {} 审核被拒", order.getId());
}
}
}
新增一个审核环节?
写个新 Handler 继承
AbstractOrderHandler,改一下next()就行。完全不用动现有代码。
这就是 "对扩展开放,对修改关闭" —— 开闭原则的教科书级应用。
五、Spring 中的责任链(你以为你没写过,其实天天用)
你以为责任链是面试题?No,Spring 全家桶到处都是。
1. Spring MVC 拦截器(HandlerInterceptor)
public interface HandlerInterceptor {
boolean preHandle(...); // 1. 前置:可以拦截
void postHandle(...); // 2. 后置:Controller 执行完
void afterCompletion(...); // 3. 最终:视图渲染完
}
preHandle 返回 false?后面的拦截器和 Controller 都不执行。
这不就是责任链么?
2. Spring Security 过滤器链
Spring Security 那 WebSecurityConfigurerAdapter 里配的:
http.addFilter(new CORSFilter())
.addFilter(new JwtAuthFilter())
.addFilter(new RateLimitFilter())
.addFilter(new AuditLogFilter());
**一个请求过来,依次过这堆 Filter,**就是教科书级的责任链。
3. Servlet Filter
public class LogFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
log.info("请求进来");
chain.doFilter(req, res); // 传给下一个
log.info("请求出去");
}
}
chain.doFilter() 这一行,就是责任链的"传球"动作。
4. Netty 的 ChannelPipeline
Netty 里数据从入站到出站,要过 N 个 ChannelHandler,每个都能拦截、处理、传给下一个。这也是责任链。
六、责任链 vs 策略模式 vs 装饰器模式
面试高频混淆点,一张表搞清:
| 维度 | 责任链模式 | 策略模式 | 装饰器模式 |
|---|---|---|---|
| 核心目的 | 不知道谁来处理,让一串人试试 | 选一个算法 | 给对象加功能 |
| 结构 | 链式传递 | 持有多个可替换策略 | 包装另一个对象 |
| 典型场景 | 审批、过滤器 | 支付方式、排序算法 | IO 流包装、动态加日志 |
| 客户端关注 | 不关心谁处理 | 关心选哪个策略 | 关心叠加后效果 |
| 代码特征 | 每个 handler 都有 next | Context 持有 Strategy | 构造函数传入同类对象 |
一句话总结:
- 责任链:这事儿该谁干?挨个问一圈。
- 策略:这事儿用啥法子干?挑一个。
- 装饰器:这事儿干完了,再加点料。
七、避坑指南(我踩过的 7 个坑)
坑 1:链没终止,导致死循环
// ❌ 错误:A 的 next 是 B,B 的 next 又指向 A
a.setNext(b);
b.setNext(a);
解法:链尾的 next() 必须返回 null。
坑 2:忘了处理"没人能处理"的情况
} else if (next != null) {
next.approve(request);
}
// ❌ 如果 next 为 null,请求就这么丢了
解法:
} else {
throw new NoHandlerException("无人处理: " + request);
}
坑 3:链顺序写错,业务全乱
限流应该放在鉴权前面还是后面?
- 放前面:未鉴权的恶意请求直接被限流,省资源。
- 放后面:只对真实用户限流,限流更精准。
**场景不同,顺序不同,**没有标准答案。
坑 4:性能问题
责任链是链式调用,节点越多性能越差。
建议:
- 高频链路控制在 5~8 个节点以内。
- 简单校验(如参数非空)放链头,复杂业务放链尾。
- 高频请求考虑短路机制。
坑 5:日志混乱
10 个节点每个都打 5 行日志,定位问题像大海捞针。
建议:链路 traceId 一打通到底。
@Slf4j
public abstract class AbstractHandler {
public boolean handle(Request req) {
MDC.put("traceId", req.getTraceId());
log.info("[{}] 开始处理", name);
// ...
}
}
坑 6:线程安全问题
如果 Handler 是有状态的,且被多线程共用,别用成员变量。
// ❌ 错误:count 是共享的
private int count = 0;
解法:状态从 Request 传,不要放 Handler 里。
坑 7:Spring Bean 注入问题
责任链里 next() 怎么拿到下一个 Bean?
// ❌ 直接 @Resource 注入会有循环依赖
@Resource
private InventoryHandler inventory;
// ✅ 推荐:用 ApplicationContext 或自定义注解顺序
@Order(1)
@Component
public class RiskHandler { ... }
八、何时该用责任链?三句话判断
✅ 用:
- 一个请求需要多个人依次处理(审批、风控、过滤)
- 处理顺序需要灵活调整
- 想让每个处理节点独立测试
❌ 别用:
- 只有 1-2 个处理环节 → 直接调方法
- 处理逻辑简单固定 → if-else 够了
- 链太长(>10 个)→ 考虑拆分或异步
九、总结(一张图带走)
┌──────────────────┐
│ Client 客户端 │
└────────┬─────────┘
│ 发起请求
▼
┌────────────────────┐
│ HandlerA 节点1 │──┐
│ - 能处理就处理 │ │ 不能处理
│ - 不能就 next() │◄─┘
└────────┬───────────┘
│ next
▼
┌────────────────────┐
│ HandlerB 节点2 │
└────────┬───────────┘
│ next
▼
...
▼
┌────────────────────┐
│ HandlerN 链尾 │
│ next() = null │
└────────────────────┘
🙏 作者介绍
📌 写文不易,Bug 更不易。
如果这篇文章对你有帮助,可以搜一搜:空门技术栈
这里分享:
- ✅ Java / Spring AI / 企业级项目实战
- ✅ Docker / RAG知识库 / 微服务踩坑
- ✅ Python、前端、AI应用落地
- ✅ 偶尔分享一些「头发保卫战」经验 😆
一个热爱技术、持续填坑的开发者, 陪你一起少踩坑,少加班,多写优雅代码。
📖 推荐阅读
🤝 技术交流 / 项目合作
平时也会做一些技术项目与咨询,包括:
- Java / Spring Boot 企业级项目开发
- AI 应用开发(LangChain、RAG、Agent、知识库)
- Docker / Linux / 私有化部署
- 系统功能开发、接口对接、性能优化
- 疑难问题排查与技术咨询
如果你:
- 想做 AI 项目,但不确定技术方案
- 项目卡在某个 Bug 很久
- 想把 AI 接入现有系统
- 需要企业级开发支持
欢迎交流。
📮 联系方式:
- Email:
2929119150@qq.com - 也可以私信我
- 技术交流可通过个人主页联系
有些坑,一个人踩是事故;一起踩,就是经验 😎