设计模式:责任链模式

欢迎你来读这篇博客,这篇博客主要是关于责任链模式
其中包括责任链模式的核心思想、适用场景、经典责任链、流水线式责任链、Spring Boot 落地方式,以及 Java 后端开发中下单校验链的完整案例。

序言

在 Java 后端开发中,我们经常会遇到一类流程:

一个请求进入系统后,需要经过多个处理步骤,每个步骤负责一小段职责。

比如用户下单时,可能要依次做:

  • 参数校验;
  • 用户状态校验;
  • 商品状态校验;
  • 库存校验;
  • 风控校验;
  • 优惠券校验;
  • 价格校验;
  • 幂等校验。

如果把这些逻辑全部写在一个方法里,很容易变成这样:

1
2
3
4
5
6
7
8
9
10
public void createOrder(CreateOrderCommand command) {
// 参数校验
// 用户校验
// 商品校验
// 库存校验
// 优惠券校验
// 风控校验
// 幂等校验
// 创建订单
}

刚开始还能看。

后来需求一多,方法越来越长,判断越来越多,if else 越来越密,最后一个下单方法像一碗加满配料的麻辣烫:什么都有,但已经看不清主食了。

责任链模式就是为了解决这类问题:

将多个处理器串成一条链,让请求沿着链依次传递,每个处理器只负责自己的职责。

简单说:

责任链模式就是把一个复杂流程拆成多个小处理器,然后按顺序串起来执行。

它的核心价值是:

  • 拆分职责;
  • 减少大方法;
  • 降低耦合;
  • 支持灵活扩展;
  • 支持动态编排处理顺序。

正文

chapter 1:什么是责任链模式

责任链模式,英文是 Chain of Responsibility Pattern,属于行为型设计模式。

它的定义是:

使多个对象都有机会处理请求,从而避免请求发送者和接收者之间的耦合。将这些对象连成一条链,并沿着这条链传递请求,直到有对象处理它为止。

传统责任链强调:

请求沿着链传递,直到某个处理器处理完成。

但是在真实 Java 后端项目中,我们还经常使用一种更常见的变体:

请求沿着链依次经过多个处理器,每个处理器都做一部分处理。

例如:

  • Servlet FilterChain;
  • Spring Security FilterChain;
  • Netty ChannelPipeline;
  • WebFlux Filter;
  • Gateway Filter;
  • 业务参数校验链;
  • 风控规则处理链;
  • 订单创建前置校验链。

所以这篇博客会讲两种责任链:

  1. 经典责任链:一个请求由链上的某个处理器处理。
  2. 流水线式责任链:一个请求依次经过链上的多个处理器。

后端业务里,第二种更常见。

chapter 2:责任链模式解决什么问题

责任链模式主要解决的是:

多个处理步骤耦合在一个地方,导致代码臃肿、难扩展、难维护。

比如下单校验:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public void validate(CreateOrderCommand command) {
if (command == null) {
throw new IllegalArgumentException("command can not be null");
}

if (command.getUserId() == null) {
throw new IllegalArgumentException("userId can not be null");
}

if (command.getProductId() == null) {
throw new IllegalArgumentException("productId can not be null");
}

if (!userService.isAvailable(command.getUserId())) {
throw new IllegalStateException("user is not available");
}

if (!productService.isOnSale(command.getProductId())) {
throw new IllegalStateException("product is not on sale");
}

if (!inventoryService.hasStock(command.getProductId(), command.getQuantity())) {
throw new IllegalStateException("stock is not enough");
}

if (!riskService.check(command.getUserId(), command.getProductId())) {
throw new IllegalStateException("risk rejected");
}
}

这个方法的问题是:

  • 所有校验逻辑耦合在一起;
  • 新增校验规则要修改原方法;
  • 不同业务场景无法灵活复用部分校验;
  • 单元测试粒度太大;
  • 顺序调整不方便;
  • 方法会越来越长。

责任链模式的做法是:

每个校验规则写成一个独立处理器,然后把处理器按顺序组成一条链。

例如:

1
2
3
4
5
参数校验处理器
-> 用户状态校验处理器
-> 商品状态校验处理器
-> 库存校验处理器
-> 风控校验处理器

这样每个处理器只做一件事。

新增规则时新增处理器即可。

chapter 3:责任链模式的核心角色

责任链模式一般包含几个角色:

  1. Handler 抽象处理器:定义处理请求的接口,通常持有下一个处理器。
  2. ConcreteHandler 具体处理器:处理自己负责的逻辑。
  3. Request 请求对象:被处理的请求。
  4. Client 客户端:组装责任链并发起请求。

经典结构如下:

1
2
3
4
5
6
7
8
9
10
Client


Handler A


Handler B


Handler C

在代码中,抽象处理器通常会有:

1
protected Handler next;

然后提供:

1
setNext(Handler next)

处理器处理完之后,可以选择:

  • 继续传给下一个处理器;
  • 中断链路;
  • 直接返回结果;
  • 抛出异常。

chapter 4:经典责任链与流水线责任链

责任链模式有两种常见使用方式。

1. 经典责任链

经典责任链中,请求会沿着链传递,直到某个处理器能处理它。

例如审批流程:

1
组长审批 -> 经理审批 -> 总监审批 -> CEO 审批

如果请假 1 天,组长处理。

如果请假 5 天,经理处理。

如果请假 15 天,总监处理。

如果请假 30 天,CEO 处理。

特点是:

通常只有一个处理器真正处理请求。

2. 流水线式责任链

流水线式责任链中,请求会经过多个处理器,每个处理器都做一部分工作。

例如下单校验:

1
参数校验 -> 用户校验 -> 商品校验 -> 库存校验 -> 风控校验

特点是:

多个处理器都参与处理请求。

这类责任链在 Java 后端开发中非常常见。

Servlet FilterChain 就是典型例子:

1
编码过滤器 -> 登录过滤器 -> 权限过滤器 -> 业务 Servlet

每个过滤器都可以在请求前后做处理,也可以选择中断请求。

chapter 5:经典责任链案例:请假审批

先看一个经典责任链示例。

定义请假请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class LeaveRequest {

private final String applicant;

private final int days;

private final String reason;

public LeaveRequest(String applicant, int days, String reason) {
this.applicant = applicant;
this.days = days;
this.reason = reason;
}

public String getApplicant() {
return applicant;
}

public int getDays() {
return days;
}

public String getReason() {
return reason;
}
}

定义抽象审批人:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public abstract class Approver {

private Approver next;

public Approver setNext(Approver next) {
this.next = next;
return next;
}

public final void approve(LeaveRequest request) {
if (canApprove(request)) {
doApprove(request);
return;
}

if (next != null) {
next.approve(request);
return;
}

throw new IllegalStateException("No approver can handle this request");
}

protected abstract boolean canApprove(LeaveRequest request);

protected abstract void doApprove(LeaveRequest request);
}

组长:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class TeamLeaderApprover extends Approver {

@Override
protected boolean canApprove(LeaveRequest request) {
return request.getDays() <= 1;
}

@Override
protected void doApprove(LeaveRequest request) {
System.out.println("组长审批通过:" + request.getApplicant()
+ ",请假天数:" + request.getDays());
}
}

经理:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ManagerApprover extends Approver {

@Override
protected boolean canApprove(LeaveRequest request) {
return request.getDays() <= 5;
}

@Override
protected void doApprove(LeaveRequest request) {
System.out.println("经理审批通过:" + request.getApplicant()
+ ",请假天数:" + request.getDays());
}
}

总监:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class DirectorApprover extends Approver {

@Override
protected boolean canApprove(LeaveRequest request) {
return request.getDays() <= 15;
}

@Override
protected void doApprove(LeaveRequest request) {
System.out.println("总监审批通过:" + request.getApplicant()
+ ",请假天数:" + request.getDays());
}
}

客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class LeaveApproveDemo {

public static void main(String[] args) {
Approver teamLeader = new TeamLeaderApprover();
Approver manager = new ManagerApprover();
Approver director = new DirectorApprover();

teamLeader.setNext(manager).setNext(director);

LeaveRequest request = new LeaveRequest("SuperMario", 3, "回家休息");

teamLeader.approve(request);
}
}

这个例子中,请假 3 天,组长不能处理,传给经理,经理可以处理,所以经理审批通过。

这就是经典责任链。

chapter 6:经典责任链的特点

经典责任链适合:

  • 多个对象都有机会处理同一个请求;
  • 具体由谁处理,需要运行时判断;
  • 请求处理者不固定;
  • 希望请求发送者和处理者解耦。

典型场景:

  • 请假审批;
  • 报销审批;
  • 客服工单分派;
  • 异常处理;
  • 消息路由;
  • 日志级别处理;
  • 优惠券匹配;
  • 规则命中。

它的特点是:

找到一个能处理的处理器后,链路通常就结束。

chapter 7:流水线责任链案例背景:下单校验链

接下来讲 Java 后端更常见的流水线式责任链。

假设我们有一个创建订单流程。

在真正创建订单之前,需要依次执行这些校验:

  1. 参数校验;
  2. 用户状态校验;
  3. 商品状态校验;
  4. 库存校验;
  5. 风控校验;
  6. 幂等校验。

任何一个校验失败,都应该中断流程。

如果全部通过,才继续创建订单。

这就是典型的业务校验链。

chapter 8:定义下单请求 CreateOrderCommand

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class CreateOrderCommand {

private final String requestId;

private final Long userId;

private final Long productId;

private final Integer quantity;

public CreateOrderCommand(String requestId, Long userId, Long productId, Integer quantity) {
this.requestId = requestId;
this.userId = userId;
this.productId = productId;
this.quantity = quantity;
}

public String getRequestId() {
return requestId;
}

public Long getUserId() {
return userId;
}

public Long getProductId() {
return productId;
}

public Integer getQuantity() {
return quantity;
}
}

这里的 requestId 可以用于幂等校验。

chapter 9:定义处理上下文 OrderCreateContext

责任链中经常需要一个上下文对象,用来保存处理过程中的中间数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.util.HashMap;
import java.util.Map;

public class OrderCreateContext {

private final CreateOrderCommand command;

private final Map<String, Object> attributes = new HashMap<>();

public OrderCreateContext(CreateOrderCommand command) {
this.command = command;
}

public CreateOrderCommand getCommand() {
return command;
}

public void put(String key, Object value) {
attributes.put(key, value);
}

@SuppressWarnings("unchecked")
public <T> T get(String key) {
return (T) attributes.get(key);
}
}

为什么需要 Context?

因为链上的处理器可能需要共享数据。

例如:

  • 商品校验器查询出商品信息;
  • 库存校验器可能需要商品信息;
  • 价格计算器也可能需要商品信息;
  • 后续创建订单时也需要这些数据。

如果每个处理器都重复查询,就会浪费。

上下文对象可以承载这些中间结果。

chapter 10:定义处理器接口 OrderCreateHandler

1
2
3
4
public interface OrderCreateHandler {

void handle(OrderCreateContext context);
}

这是最简单的流水线处理器接口。

每个处理器只需要实现 handle() 方法。

如果校验失败,直接抛异常。

如果校验通过,就正常返回。

链路执行器会继续调用下一个处理器。

chapter 11:定义参数校验处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class ParamValidateHandler implements OrderCreateHandler {

@Override
public void handle(OrderCreateContext context) {
CreateOrderCommand command = context.getCommand();

if (command == null) {
throw new IllegalArgumentException("command can not be null");
}

if (command.getRequestId() == null || command.getRequestId().isBlank()) {
throw new IllegalArgumentException("requestId can not be blank");
}

if (command.getUserId() == null) {
throw new IllegalArgumentException("userId can not be null");
}

if (command.getProductId() == null) {
throw new IllegalArgumentException("productId can not be null");
}

if (command.getQuantity() == null || command.getQuantity() <= 0) {
throw new IllegalArgumentException("quantity must be greater than 0");
}

System.out.println("参数校验通过");
}
}

这个处理器只负责参数校验。

它不关心用户状态、商品状态、库存和风控。

chapter 12:定义用户状态校验处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class UserStatusValidateHandler implements OrderCreateHandler {

@Override
public void handle(OrderCreateContext context) {
Long userId = context.getCommand().getUserId();

System.out.println("校验用户状态,userId = " + userId);

boolean userAvailable = true;

if (!userAvailable) {
throw new IllegalStateException("user is not available");
}

context.put("userAvailable", true);

System.out.println("用户状态校验通过");
}
}

真实项目中,这里可能会调用:

1
userService.getUser(userId)

然后判断:

  • 用户是否存在;
  • 是否被冻结;
  • 是否实名;
  • 是否命中黑名单;
  • 是否有下单权限。

chapter 13:定义商品状态校验处理器

先定义商品信息对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import java.math.BigDecimal;

public class ProductInfo {

private final Long productId;

private final String productName;

private final BigDecimal price;

private final boolean onSale;

public ProductInfo(Long productId, String productName, BigDecimal price, boolean onSale) {
this.productId = productId;
this.productName = productName;
this.price = price;
this.onSale = onSale;
}

public Long getProductId() {
return productId;
}

public String getProductName() {
return productName;
}

public BigDecimal getPrice() {
return price;
}

public boolean isOnSale() {
return onSale;
}
}

商品校验处理器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.math.BigDecimal;

public class ProductStatusValidateHandler implements OrderCreateHandler {

@Override
public void handle(OrderCreateContext context) {
Long productId = context.getCommand().getProductId();

System.out.println("查询并校验商品状态,productId = " + productId);

ProductInfo productInfo = new ProductInfo(
productId,
"设计模式课程",
new BigDecimal("99.00"),
true
);

if (!productInfo.isOnSale()) {
throw new IllegalStateException("product is not on sale");
}

context.put("productInfo", productInfo);

System.out.println("商品状态校验通过");
}
}

这里把 productInfo 放进上下文中:

1
context.put("productInfo", productInfo);

后续处理器可以复用。

chapter 14:定义库存校验处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class StockValidateHandler implements OrderCreateHandler {

@Override
public void handle(OrderCreateContext context) {
ProductInfo productInfo = context.get("productInfo");

Integer quantity = context.getCommand().getQuantity();

System.out.println("校验库存,productId = " + productInfo.getProductId()
+ ",quantity = " + quantity);

int availableStock = 100;

if (availableStock < quantity) {
throw new IllegalStateException("stock is not enough");
}

context.put("stockChecked", true);

System.out.println("库存校验通过");
}
}

库存处理器依赖前面商品处理器放入的商品信息。

所以处理器顺序很重要。

如果库存校验在商品校验之前执行,就可能拿不到 productInfo

责任链不是散装零件,顺序错了,车能不能跑要看运气。

chapter 15:定义风控校验处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class RiskValidateHandler implements OrderCreateHandler {

@Override
public void handle(OrderCreateContext context) {
Long userId = context.getCommand().getUserId();
Long productId = context.getCommand().getProductId();

System.out.println("执行风控校验,userId = " + userId + ",productId = " + productId);

boolean riskPassed = true;

if (!riskPassed) {
throw new IllegalStateException("risk check rejected");
}

context.put("riskPassed", true);

System.out.println("风控校验通过");
}
}

真实项目中,风控处理器可能包括:

  • 黑名单校验;
  • 频率校验;
  • 设备指纹;
  • IP 风险;
  • 异常行为;
  • 用户画像;
  • 风控引擎调用。

chapter 16:定义幂等校验处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class IdempotentValidateHandler implements OrderCreateHandler {

private final Set<String> processedRequestIds = ConcurrentHashMap.newKeySet();

@Override
public void handle(OrderCreateContext context) {
String requestId = context.getCommand().getRequestId();

System.out.println("执行幂等校验,requestId = " + requestId);

boolean firstRequest = processedRequestIds.add(requestId);

if (!firstRequest) {
throw new IllegalStateException("duplicate request, requestId = " + requestId);
}

System.out.println("幂等校验通过");
}
}

这里用内存 Set 只是演示。

真实项目中,幂等校验通常会使用:

  • Redis;
  • 数据库唯一索引;
  • 分布式锁;
  • requestId 表;
  • 去重表;
  • MQ 消息去重。

内存 Set 只能在单 JVM 内有效,分布式场景不够。

chapter 17:定义责任链执行器

现在定义一个执行器,按顺序执行处理器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.util.List;

public class OrderCreateHandlerChain {

private final List<OrderCreateHandler> handlers;

public OrderCreateHandlerChain(List<OrderCreateHandler> handlers) {
this.handlers = List.copyOf(handlers);
}

public void execute(OrderCreateContext context) {
for (OrderCreateHandler handler : handlers) {
handler.handle(context);
}
}
}

这个版本非常简单。

如果某个处理器抛异常,后续处理器就不会执行。

这正好符合校验链的语义:

前置校验失败,立即中断。

chapter 18:客户端使用下单校验链

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.util.List;

public class OrderCreateChainDemo {

public static void main(String[] args) {
List<OrderCreateHandler> handlers = List.of(
new ParamValidateHandler(),
new UserStatusValidateHandler(),
new ProductStatusValidateHandler(),
new StockValidateHandler(),
new RiskValidateHandler(),
new IdempotentValidateHandler()
);

OrderCreateHandlerChain chain = new OrderCreateHandlerChain(handlers);

CreateOrderCommand command = new CreateOrderCommand(
"REQ-202604010001",
1L,
100L,
2
);

OrderCreateContext context = new OrderCreateContext(command);

chain.execute(context);

ProductInfo productInfo = context.get("productInfo");

System.out.println("所有校验通过,可以创建订单,productName = " + productInfo.getProductName());
}
}

这个结构就比一个巨大校验方法清晰很多。

每个处理器都是一个独立类。

新增规则时新增处理器即可。

chapter 19:带中断能力的责任链

有些场景不希望通过异常中断,而是希望处理器返回是否继续。

可以定义结果对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class HandlerResult {

private final boolean proceed;

private final String message;

private HandlerResult(boolean proceed, String message) {
this.proceed = proceed;
this.message = message;
}

public static HandlerResult proceed() {
return new HandlerResult(true, "continue");
}

public static HandlerResult stop(String message) {
return new HandlerResult(false, message);
}

public boolean isProceed() {
return proceed;
}

public String getMessage() {
return message;
}
}

处理器接口:

1
2
3
4
public interface StoppableOrderCreateHandler {

HandlerResult handle(OrderCreateContext context);
}

执行器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.util.List;

public class StoppableOrderCreateHandlerChain {

private final List<StoppableOrderCreateHandler> handlers;

public StoppableOrderCreateHandlerChain(List<StoppableOrderCreateHandler> handlers) {
this.handlers = List.copyOf(handlers);
}

public HandlerResult execute(OrderCreateContext context) {
for (StoppableOrderCreateHandler handler : handlers) {
HandlerResult result = handler.handle(context);

if (!result.isProceed()) {
return result;
}
}

return HandlerResult.proceed();
}
}

这种方式适合不想通过异常控制流程的场景。

例如:

  • 风控拒绝;
  • 活动规则不命中;
  • 过滤器提前返回;
  • 消息处理提前停止。

chapter 20:责任链中处理器顺序怎么管理

责任链非常依赖顺序。

常见管理方式有几种。

1. 手动 List 顺序

1
2
3
4
5
6
List.of(
new ParamValidateHandler(),
new UserStatusValidateHandler(),
new ProductStatusValidateHandler(),
new StockValidateHandler()
)

优点是简单直观。

缺点是新增处理器要手动改列表。

2. order 字段排序

给处理器增加排序方法:

1
2
3
4
public interface OrderedOrderCreateHandler extends OrderCreateHandler {

int order();
}

执行器中排序:

1
2
3
handlers.stream()
.sorted(Comparator.comparingInt(OrderedOrderCreateHandler::order))
.toList();

3. Spring @Order

在 Spring Boot 中,可以使用 @Order 控制 Bean 顺序。

1
2
3
4
@Component
@Order(100)
public class ParamValidateHandler implements OrderCreateHandler {
}

Spring 注入 List<OrderCreateHandler> 时,会按照顺序排列。

4. 配置化编排

可以通过配置控制责任链启用哪些处理器。

例如:

1
2
3
4
5
6
7
8
order:
create-chain:
handlers:
- paramValidate
- userStatusValidate
- productStatusValidate
- stockValidate
- riskValidate

这种方式更灵活,但复杂度也更高。

chapter 21:Spring Boot 中落地责任链

在 Spring Boot 中,责任链非常适合用 List<Handler> 注入。

先定义接口:

1
2
3
4
public interface OrderCreateHandler {

void handle(OrderCreateContext context);
}

参数校验处理器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Order(100)
public class ParamValidateHandler implements OrderCreateHandler {

@Override
public void handle(OrderCreateContext context) {
CreateOrderCommand command = context.getCommand();

if (command == null) {
throw new IllegalArgumentException("command can not be null");
}

if (command.getRequestId() == null || command.getRequestId().isBlank()) {
throw new IllegalArgumentException("requestId can not be blank");
}

if (command.getUserId() == null) {
throw new IllegalArgumentException("userId can not be null");
}

if (command.getProductId() == null) {
throw new IllegalArgumentException("productId can not be null");
}

if (command.getQuantity() == null || command.getQuantity() <= 0) {
throw new IllegalArgumentException("quantity must be greater than 0");
}
}
}

用户校验处理器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Order(200)
public class UserStatusValidateHandler implements OrderCreateHandler {

private final UserService userService;

public UserStatusValidateHandler(UserService userService) {
this.userService = userService;
}

@Override
public void handle(OrderCreateContext context) {
Long userId = context.getCommand().getUserId();

if (!userService.isAvailable(userId)) {
throw new IllegalStateException("user is not available");
}
}
}

商品校验处理器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Order(300)
public class ProductStatusValidateHandler implements OrderCreateHandler {

private final ProductService productService;

public ProductStatusValidateHandler(ProductService productService) {
this.productService = productService;
}

@Override
public void handle(OrderCreateContext context) {
ProductInfo productInfo = productService.getProduct(
context.getCommand().getProductId()
);

if (!productInfo.isOnSale()) {
throw new IllegalStateException("product is not on sale");
}

context.put("productInfo", productInfo);
}
}

责任链执行器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class OrderCreateHandlerChain {

private final List<OrderCreateHandler> handlers;

public OrderCreateHandlerChain(List<OrderCreateHandler> handlers) {
this.handlers = List.copyOf(handlers);
}

public void execute(OrderCreateContext context) {
for (OrderCreateHandler handler : handlers) {
handler.handle(context);
}
}
}

订单应用服务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class OrderApplicationService {

private final OrderCreateHandlerChain orderCreateHandlerChain;

private final OrderService orderService;

public OrderApplicationService(OrderCreateHandlerChain orderCreateHandlerChain,
OrderService orderService) {
this.orderCreateHandlerChain = orderCreateHandlerChain;
this.orderService = orderService;
}

@Transactional(rollbackFor = Exception.class)
public Long createOrder(CreateOrderCommand command) {
OrderCreateContext context = new OrderCreateContext(command);

orderCreateHandlerChain.execute(context);

ProductInfo productInfo = context.get("productInfo");

return orderService.createOrder(command, productInfo);
}
}

这种结构在实际项目里非常好用。

新增一个校验规则时,只需要新增一个 OrderCreateHandler Bean,并设置合适的 @Order

chapter 22:责任链和 Servlet FilterChain

Java Web 中最经典的责任链之一就是 Servlet FilterChain。

过滤器接口类似:

1
2
3
4
5
6
public interface Filter {

void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain);
}

过滤器内部可以选择继续执行:

1
chain.doFilter(request, response);

也可以直接中断:

1
2
response.getWriter().write("access denied");
return;

典型链路:

1
2
3
4
5
CharacterEncodingFilter
-> CorsFilter
-> AuthenticationFilter
-> AuthorizationFilter
-> DispatcherServlet

每个 Filter 都可以在请求前后做事情:

1
2
3
4
5
6
7
public void doFilter(request, response, chain) {
// 前置逻辑

chain.doFilter(request, response);

// 后置逻辑
}

这就是责任链模式在 Java Web 中的经典落地。

chapter 23:责任链和 Spring Security FilterChain

Spring Security 的过滤器链也是责任链思想。

常见过滤器包括:

  • SecurityContextPersistenceFilter
  • UsernamePasswordAuthenticationFilter
  • BasicAuthenticationFilter
  • ExceptionTranslationFilter
  • FilterSecurityInterceptor

请求进入后,会沿着安全过滤器链依次执行。

不同过滤器负责不同职责:

  • 认证;
  • 鉴权;
  • 异常处理;
  • 安全上下文保存;
  • 登录处理;
  • CSRF 校验。

这就是责任链模式的工程化体现。

chapter 24:责任链和 Netty ChannelPipeline

Netty 中的 ChannelPipeline 也是责任链思想。

入站事件沿着 pipeline 传播:

1
2
3
ChannelInboundHandler A
-> ChannelInboundHandler B
-> ChannelInboundHandler C

出站事件也会沿着 pipeline 传播。

每个 Handler 处理自己关心的事件。

例如:

  • 编码;
  • 解码;
  • 心跳;
  • 认证;
  • 业务处理;
  • 日志;
  • 流量控制。

Netty 的 Pipeline 是责任链模式非常高级的工程化实现。

chapter 25:责任链和 Gateway Filter

在 Spring Cloud Gateway 中,过滤器链也非常典型。

请求进入网关后,会经过多个 Filter:

  • 认证 Filter;
  • 鉴权 Filter;
  • 限流 Filter;
  • 熔断 Filter;
  • 日志 Filter;
  • 路由 Filter;
  • 响应处理 Filter。

每个 Filter 负责一个横切职责。

这比把所有网关逻辑写在一个类里清晰得多。

chapter 26:责任链模式和策略模式的区别

责任链模式和策略模式都属于行为型模式,也都能消除大量 if else

但它们关注点不同。

对比项 责任链模式 策略模式
目的 多个处理器按链路处理请求 从多个算法中选择一个执行
执行数量 可能多个处理器执行 通常一个策略执行
结构 链式传递 策略选择
典型场景 校验链、过滤器链、审批链 支付策略、折扣策略、排序策略
重点 顺序和传递 选择和替换

策略模式像:

1
选一个人干活

责任链像:

1
一群人按顺序接力处理

如果一个请求只需要选一个处理器处理,可能是策略。

如果一个请求需要经过多个处理器,通常是责任链。

chapter 27:责任链模式和模板方法模式的区别

模板方法模式是在父类中固定流程,子类实现某些步骤。

责任链模式是把流程拆成多个独立处理器。

对比项 责任链模式 模板方法模式
流程结构 多个处理器组成链 父类固定算法骨架
扩展方式 新增处理器 继承父类并重写步骤
处理顺序 可动态组合 通常固定
灵活性 更高 较固定
典型场景 FilterChain、校验链 统一导入流程、固定算法流程

模板方法像固定菜谱:

1
洗菜 -> 切菜 -> 炒菜 -> 装盘

责任链像流水线:

1
岗位 A -> 岗位 B -> 岗位 C

一个偏固定流程,一个偏可组合流程。

chapter 28:责任链模式和装饰器模式的区别

责任链和装饰器都可能表现为一层套一层,但目的不同。

对比项 责任链模式 装饰器模式
目的 请求沿链传递处理 动态增强对象功能
结构 多个处理器组成链 装饰器包装组件
是否一定调用下一个 不一定 通常会调用被装饰对象
典型场景 过滤器链、校验链 日志增强、缓存增强、重试增强
关注点 处理流程 功能叠加

责任链可以中断。

装饰器通常是增强后继续调用目标对象。

chapter 29:责任链模式和代理模式的区别

对比项 责任链模式 代理模式
目的 多个处理器协作处理请求 控制对目标对象的访问
对象数量 多个处理器 通常一个代理和一个目标对象
结构 代理持有目标
典型场景 FilterChain、审批链 AOP、事务、远程代理
是否多个节点都处理 可以 通常代理控制一个目标

代理像一个中间人。

责任链像一条处理流水线。

chapter 30:责任链模式的优点

1. 降低请求发送者和处理者耦合

请求发送者不需要知道具体由谁处理,或者经过哪些处理器。

2. 拆分复杂逻辑

把大方法拆成多个小处理器,每个处理器职责单一。

3. 扩展性好

新增处理逻辑时,新增处理器即可。

4. 顺序可配置

处理器顺序可以通过 List、配置、@Order 等方式调整。

5. 支持中断

某个处理器可以决定是否继续向后传递。

6. 适合横切流程

例如认证、鉴权、限流、日志、风控、校验等。

chapter 31:责任链模式的缺点

1. 调用链变长

请求经过多个处理器,排查问题时要看整条链。

2. 顺序依赖明显

有些处理器依赖前面处理器的结果,顺序错了就会出问题。

3. 可能导致请求无人处理

经典责任链中,如果没有处理器能处理请求,可能出现遗漏。

4. 过度拆分会增加复杂度

如果每个小判断都拆成一个处理器,类数量会很多。

5. 上下文可能变成大杂烩

如果所有处理器都往 Context 里塞数据,Context 会越来越混乱。

这时需要规范 key、类型和职责边界。

chapter 32:适用场景

责任链模式适合以下场景。

1. 多个处理器按顺序处理请求

例如:

  • 参数校验链;
  • 风控规则链;
  • 订单创建前置校验链;
  • 支付前置校验链。

2. 请求可能由多个对象中的某一个处理

例如:

  • 请假审批;
  • 报销审批;
  • 工单分派;
  • 异常处理。

3. 需要动态调整处理顺序

例如通过配置控制启用哪些规则。

4. 需要支持中断

例如:

  • 登录失败直接返回;
  • 权限不足直接拒绝;
  • 风控失败中断下单;
  • 参数错误直接抛异常。

5. 需要拆分复杂流程

把一个巨大方法拆成多个小处理器。

chapter 33:不适合使用的场景

以下场景不建议使用责任链模式。

1. 处理步骤很少且稳定

如果只有两个简单步骤,直接写方法可能更清晰。

2. 处理器之间强依赖复杂

如果每个处理器都深度依赖前后处理器的内部实现,链会很脆弱。

3. 顺序非常复杂且经常变化

如果顺序逻辑本身很复杂,可能需要工作流引擎、规则引擎或状态机。

4. 需要严格事务一致性

责任链中如果多个处理器都有副作用,要注意事务边界和回滚问题。

5. 处理器数量过多但缺少治理

几十上百个 Handler 串起来,如果没有分组、命名、日志、监控,会很难维护。

chapter 34:真实项目中的实践建议

1. Handler 职责要单一

一个 Handler 只做一类事情。

例如:

1
2
3
ParamValidateHandler
StockValidateHandler
RiskValidateHandler

不要写:

1
OrderCreateAllValidateHandler

这种名字一看就要长胖。

2. Handler 命名要清晰

推荐格式:

1
2
3
4
XxxValidateHandler
XxxCheckHandler
XxxFilter
XxxInterceptor

让人一眼知道它负责什么。

3. 顺序要显式

不要依赖“刚好注入顺序”。

使用:

  • @Order
  • order 字段;
  • 配置文件;
  • 枚举顺序;
  • 链构建器。

4. Context 不要乱用

Context 里保存中间数据可以,但要克制。

推荐定义常量 key:

1
2
3
4
5
6
7
public final class OrderCreateContextKeys {

public static final String PRODUCT_INFO = "productInfo";

private OrderCreateContextKeys() {
}
}

更好的方式是给 Context 增加明确字段。

1
private ProductInfo productInfo;

不要全靠 Map<String, Object>,否则后期会变成类型安全的荒野求生。

5. 注意 Handler 是否有副作用

校验类 Handler 最好不要修改数据库。

如果 Handler 会产生副作用,比如锁库存、扣优惠券,就要考虑:

  • 事务;
  • 回滚;
  • 幂等;
  • 补偿;
  • 异常处理。

6. 给链路加日志

责任链排查问题时,最好记录每个 Handler 的执行情况。

例如:

1
2
3
4
start ParamValidateHandler
success ParamValidateHandler
start StockValidateHandler
failed StockValidateHandler

不然一条链执行失败,你会像在黑盒里摸鱼。

7. 可以做链路分组

不同业务场景可以使用不同链。

例如:

1
2
3
4
普通下单链
秒杀下单链
预售下单链
后台补单链

不要所有业务都共用一条巨型链。

8. Spring 中用 List 注入很方便

1
2
3
public OrderCreateHandlerChain(List<OrderCreateHandler> handlers) {
this.handlers = handlers;
}

配合 @Order,可以很优雅地组织处理器。

9. 配置化链路要谨慎

配置化确实灵活,但也增加理解成本。

如果业务变化不频繁,代码编排更清晰。

不要为了“可配置”把简单代码变成半个工作流引擎。

chapter 35:完整案例代码汇总

请求对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class CreateOrderCommand {

private final String requestId;

private final Long userId;

private final Long productId;

private final Integer quantity;

public CreateOrderCommand(String requestId, Long userId, Long productId, Integer quantity) {
this.requestId = requestId;
this.userId = userId;
this.productId = productId;
this.quantity = quantity;
}

public String getRequestId() {
return requestId;
}

public Long getUserId() {
return userId;
}

public Long getProductId() {
return productId;
}

public Integer getQuantity() {
return quantity;
}
}

上下文对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.util.HashMap;
import java.util.Map;

public class OrderCreateContext {

private final CreateOrderCommand command;

private final Map<String, Object> attributes = new HashMap<>();

public OrderCreateContext(CreateOrderCommand command) {
this.command = command;
}

public CreateOrderCommand getCommand() {
return command;
}

public void put(String key, Object value) {
attributes.put(key, value);
}

@SuppressWarnings("unchecked")
public <T> T get(String key) {
return (T) attributes.get(key);
}
}

商品对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import java.math.BigDecimal;

public class ProductInfo {

private final Long productId;

private final String productName;

private final BigDecimal price;

private final boolean onSale;

public ProductInfo(Long productId, String productName, BigDecimal price, boolean onSale) {
this.productId = productId;
this.productName = productName;
this.price = price;
this.onSale = onSale;
}

public Long getProductId() {
return productId;
}

public String getProductName() {
return productName;
}

public BigDecimal getPrice() {
return price;
}

public boolean isOnSale() {
return onSale;
}
}

Handler 接口

1
2
3
4
public interface OrderCreateHandler {

void handle(OrderCreateContext context);
}

参数校验 Handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class ParamValidateHandler implements OrderCreateHandler {

@Override
public void handle(OrderCreateContext context) {
CreateOrderCommand command = context.getCommand();

if (command == null) {
throw new IllegalArgumentException("command can not be null");
}

if (command.getRequestId() == null || command.getRequestId().isBlank()) {
throw new IllegalArgumentException("requestId can not be blank");
}

if (command.getUserId() == null) {
throw new IllegalArgumentException("userId can not be null");
}

if (command.getProductId() == null) {
throw new IllegalArgumentException("productId can not be null");
}

if (command.getQuantity() == null || command.getQuantity() <= 0) {
throw new IllegalArgumentException("quantity must be greater than 0");
}

System.out.println("参数校验通过");
}
}

用户校验 Handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class UserStatusValidateHandler implements OrderCreateHandler {

@Override
public void handle(OrderCreateContext context) {
Long userId = context.getCommand().getUserId();

System.out.println("校验用户状态,userId = " + userId);

boolean userAvailable = true;

if (!userAvailable) {
throw new IllegalStateException("user is not available");
}

context.put("userAvailable", true);

System.out.println("用户状态校验通过");
}
}

商品校验 Handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.math.BigDecimal;

public class ProductStatusValidateHandler implements OrderCreateHandler {

@Override
public void handle(OrderCreateContext context) {
Long productId = context.getCommand().getProductId();

System.out.println("查询并校验商品状态,productId = " + productId);

ProductInfo productInfo = new ProductInfo(
productId,
"设计模式课程",
new BigDecimal("99.00"),
true
);

if (!productInfo.isOnSale()) {
throw new IllegalStateException("product is not on sale");
}

context.put("productInfo", productInfo);

System.out.println("商品状态校验通过");
}
}

库存校验 Handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class StockValidateHandler implements OrderCreateHandler {

@Override
public void handle(OrderCreateContext context) {
ProductInfo productInfo = context.get("productInfo");

Integer quantity = context.getCommand().getQuantity();

System.out.println("校验库存,productId = " + productInfo.getProductId()
+ ",quantity = " + quantity);

int availableStock = 100;

if (availableStock < quantity) {
throw new IllegalStateException("stock is not enough");
}

context.put("stockChecked", true);

System.out.println("库存校验通过");
}
}

风控校验 Handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class RiskValidateHandler implements OrderCreateHandler {

@Override
public void handle(OrderCreateContext context) {
Long userId = context.getCommand().getUserId();
Long productId = context.getCommand().getProductId();

System.out.println("执行风控校验,userId = " + userId + ",productId = " + productId);

boolean riskPassed = true;

if (!riskPassed) {
throw new IllegalStateException("risk check rejected");
}

context.put("riskPassed", true);

System.out.println("风控校验通过");
}
}

幂等校验 Handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

public class IdempotentValidateHandler implements OrderCreateHandler {

private final Set<String> processedRequestIds = ConcurrentHashMap.newKeySet();

@Override
public void handle(OrderCreateContext context) {
String requestId = context.getCommand().getRequestId();

System.out.println("执行幂等校验,requestId = " + requestId);

boolean firstRequest = processedRequestIds.add(requestId);

if (!firstRequest) {
throw new IllegalStateException("duplicate request, requestId = " + requestId);
}

System.out.println("幂等校验通过");
}
}

责任链执行器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.util.List;

public class OrderCreateHandlerChain {

private final List<OrderCreateHandler> handlers;

public OrderCreateHandlerChain(List<OrderCreateHandler> handlers) {
this.handlers = List.copyOf(handlers);
}

public void execute(OrderCreateContext context) {
for (OrderCreateHandler handler : handlers) {
handler.handle(context);
}
}
}

客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.util.List;

public class OrderCreateChainDemo {

public static void main(String[] args) {
List<OrderCreateHandler> handlers = List.of(
new ParamValidateHandler(),
new UserStatusValidateHandler(),
new ProductStatusValidateHandler(),
new StockValidateHandler(),
new RiskValidateHandler(),
new IdempotentValidateHandler()
);

OrderCreateHandlerChain chain = new OrderCreateHandlerChain(handlers);

CreateOrderCommand command = new CreateOrderCommand(
"REQ-202604010001",
1L,
100L,
2
);

OrderCreateContext context = new OrderCreateContext(command);

chain.execute(context);

ProductInfo productInfo = context.get("productInfo");

System.out.println("所有校验通过,可以创建订单,productName = " + productInfo.getProductName());
}
}

chapter 36:一句话总结

责任链模式的本质是:

将多个处理器按顺序组成一条链,让请求沿着链传递,每个处理器只负责自己的职责,并可以决定是否继续向后传递。

它特别适合:

  • 参数校验链;
  • 权限过滤链;
  • 风控规则链;
  • 审批流程;
  • 网关过滤器;
  • Servlet Filter;
  • Spring Security FilterChain;
  • Netty Pipeline;
  • 订单创建前置校验;
  • 支付前置校验。

责任链模式最重要的不是“把类拆多”,而是:

把流程拆清楚,把职责拆干净,把顺序管理好。

好的责任链像流水线:每一站职责明确,流转顺畅。

坏的责任链像踢皮球:谁都处理一点,谁也说不清最终是谁的锅。

参考资料

  • Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software.
  • Robert C. Martin. Agile Software Development, Principles, Patterns, and Practices.
  • Joshua Bloch. Effective Java.
  • Spring Framework Documentation: Core Technologies - The IoC Container.
  • Spring Framework Documentation: Web MVC Filters and Interceptors.
  • Spring Security Documentation: Security Filter Chain.
  • Netty Documentation: ChannelPipeline.
  • Refactoring Guru: Chain of Responsibility Pattern.
  • SourceMaking: Chain of Responsibility Design Pattern.

启示录

富贵岂由人,时会高志须酬。

能成功于千载者,必以近察远。


设计模式:责任链模式
https://allendericdalexander.github.io/2026/04/01/java/design/14chain-of-responsibility-pattern-blog/
作者
AtLuoFu
发布于
2026年4月1日
许可协议