文章目录
- 一、背景:if-else
- 二、什么是责任链模式
- [三、Spring Boot 为什么特别适合责任链](#三、Spring Boot 为什么特别适合责任链)
- 四、核心设计思路
- [五、实战:Spring Boot + 责任链(@Order)](#五、实战:Spring Boot + 责任链(@Order))
- 六、@Order:责任链的"灵魂"
- [七、Controller 里会变得多干净](#七、Controller 里会变得多干净)
- 八、为什么这种写法特别适合真实项目
- 九、最佳实践建议
当 if-else 开始失控时,责任链是你最该想到的设计模式。
一、背景:if-else
在实际业务中,我们经常遇到这样的流程:
- 参数校验
- 权限校验
- 幂等 / 状态校验
- 核心业务处理
- 资源操作(文件、数据库、远程调用)
- 回调 / 通知
最初代码可能是这样的:
java
if (!validParam(req)) {
return error("参数错误");
}
if (!hasPermission(user, doc)) {
return error("无权限");
}
if (!stateOk(doc)) {
return error("状态不允许");
}
// 生成 PDF
// 上传文件
// 发 MQ
问题很明显:
- if-else 越来越长
- 顺序强耦合
- 新增步骤要改"主流程"
- 可读性、可维护性迅速下降
这正是责任链模式最适合出场的地方。
二、什么是责任链模式
责任链模式:把一件事交给一串处理者,一个处理不了就交给下一个。
现实中的例子:
- 请假审批:组长 → 经理 → 总监
- 安检流程:初检 → 复检 → 放行
- Web 请求:Filter → Interceptor → Controller
三、Spring Boot 为什么特别适合责任链
Spring 本身就天然支持"链式处理":
FilterChainHandlerInterceptorBean 注入 List<T>@Order控制顺序
👉 不用自己写 setNext,Spring 帮你把链排好
四、核心设计思路
三个核心角色
| 角色 | 作用 |
|---|---|
| Context | 在链条中传递数据 |
| Handler | 每个步骤只做一件事 |
| Chain | 按顺序执行所有 Handler |
执行效果
java
exportChain.execute(context);
而不是一堆 if-else。
五、实战:Spring Boot + 责任链(@Order)
请求 / 响应 DTO
csharp
public class ExportRequest {
private String docId;
private String userId;
public String getDocId() { return docId; }
public void setDocId(String docId) { this.docId = docId; }
public String getUserId() { return userId; }
public void setUserId(String userId) { this.userId = userId; }
}
csharp
public class ExportResponse {
private String pdfUrl;
public ExportResponse(String pdfUrl) {
this.pdfUrl = pdfUrl;
}
public String getPdfUrl() {
return pdfUrl;
}
}
Context:贯穿整个链路
java
public class ExportContext {
private String docId;
private String userId;
private byte[] pdfBytes;
private String pdfUrl;
public String getDocId() { return docId; }
public void setDocId(String docId) { this.docId = docId; }
public String getUserId() { return userId; }
public void setUserId(String userId) { this.userId = userId; }
public byte[] getPdfBytes() { return pdfBytes; }
public void setPdfBytes(byte[] pdfBytes) { this.pdfBytes = pdfBytes; }
public String getPdfUrl() { return pdfUrl; }
public void setPdfUrl(String pdfUrl) { this.pdfUrl = pdfUrl; }
}
Handler 接口
java
public interface ExportHandler {
void handle(ExportContext ctx);
}
核心:Chain(Spring 自动注入)
java
@Component
public class ExportChain {
private final List<ExportHandler> handlers;
public ExportChain(List<ExportHandler> handlers) {
this.handlers = handlers;
}
public void execute(ExportContext context) {
for (ExportHandler handler : handlers) {
System.out.println("执行:" + handler.getClass().getSimpleName());
handler.handle(context);
}
}
}
⚠️ 关键点:
Spring 会按照 @Order 自动排序后,再注入 List
六、@Order:责任链的"灵魂"
规则只有一条
text
数字越小,越先执行
各个处理节点
参数校验
java
@Component
@Order(10)
public class ValidateHandler implements ExportHandler {
@Override
public void handle(ExportContext context) {
System.out.println("👉 ValidateHandler");
if (context.getDocId() == null) {
throw new RuntimeException("docId 不能为空");
}
}
}
权限校验
java
@Component
@Order(20)
public class AuthHandler implements ExportHandler {
@Override
public void handle(ExportContext context) {
System.out.println("👉 AuthHandler");
// 模拟鉴权
}
}
生成 PDF
java
@Component
@Order(40)
public class GeneratePdfHandler implements ExportHandler {
@Override
public void handle(ExportContext context) {
System.out.println("👉 GeneratePdfHandler");
context.setPdfBytes("PDF DATA".getBytes());
}
}
上传文件
java
@Component
@Order(50)
public class UploadHandler implements ExportHandler {
@Override
public void handle(ExportContext context) {
System.out.println("👉 UploadHandler");
context.setPdfUrl("http://example.com/demo.pdf");
}
}
不需要任何手工组装。
七、Controller 里会变得多干净
java
private final ExportChain exportChain;
public FileController(ExportChain exportChain) {
this.exportChain = exportChain;
}
@PostMapping("/export/pdf")
public ExportResponse export(@RequestBody ExportRequest req) {
ExportContext ctx = new ExportContext();
ctx.setDocId(req.getDocId());
ctx.setUserId(req.getUserId());
exportChain.execute(ctx);
return new ExportResponse(ctx.getPdfUrl());
}
👉 Controller 只负责"发起流程",不关心细节
实际执行顺序

八、为什么这种写法特别适合真实项目
优点总结
-
消灭 if-else
-
职责清晰,每个 Handler 只干一件事
-
新增 / 删除步骤不影响主流程
-
顺序清楚,代码即文档
-
非常适合:
- 导出流程
- 审批流
- 校验流水线
- 中间件式处理
责任链模式不是"高级设计",
而是让复杂流程重新变得像流水线一样简单。
九、最佳实践建议
Order 编号建议
text
10 参数校验
20 权限 / 租户
30 幂等 / 状态
40 核心业务
50 IO / 远程调用
60 通知 / 回调
中间预留空位,方便插节点。
用异常中断链条
比 return boolean 更清晰:
java
throw new BizException("状态不允许");
Context 别用 Map
强类型 Context:
- 可读
- 可重构
- 少踩坑