欢迎你来读这篇博客,这篇博客主要是关于责任链模式。 其中包括责任链模式的核心思想、适用场景、经典责任链、流水线式责任链、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;
业务参数校验链;
风控规则处理链;
订单创建前置校验链。
所以这篇博客会讲两种责任链:
经典责任链 :一个请求由链上的某个处理器处理。
流水线式责任链 :一个请求依次经过链上的多个处理器。
后端业务里,第二种更常见。
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:责任链模式的核心角色 责任链模式一般包含几个角色:
Handler 抽象处理器 :定义处理请求的接口,通常持有下一个处理器。
ConcreteHandler 具体处理器 :处理自己负责的逻辑。
Request 请求对象 :被处理的请求。
Client 客户端 :组装责任链并发起请求。
经典结构如下:
1 2 3 4 5 6 7 8 9 10 Client │ ▼ Handler A │ ▼ Handler B │ ▼ Handler C
在代码中,抽象处理器通常会有:
然后提供:
处理器处理完之后,可以选择:
继续传给下一个处理器;
中断链路;
直接返回结果;
抛出异常。
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 后端更常见的流水线式责任链。
假设我们有一个创建订单流程。
在真正创建订单之前,需要依次执行这些校验:
参数校验;
用户状态校验;
商品状态校验;
库存校验;
风控校验;
幂等校验。
任何一个校验失败,都应该中断流程。
如果全部通过,才继续创建订单。
这就是典型的业务校验链。
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。
但它们关注点不同。
对比项
责任链模式
策略模式
目的
多个处理器按链路处理请求
从多个算法中选择一个执行
执行数量
可能多个处理器执行
通常一个策略执行
结构
链式传递
策略选择
典型场景
校验链、过滤器链、审批链
支付策略、折扣策略、排序策略
重点
顺序和传递
选择和替换
策略模式像:
责任链像:
如果一个请求只需要选一个处理器处理,可能是策略。
如果一个请求需要经过多个处理器,通常是责任链。
chapter 27:责任链模式和模板方法模式的区别 模板方法模式是在父类中固定流程,子类实现某些步骤。
责任链模式是把流程拆成多个独立处理器。
对比项
责任链模式
模板方法模式
流程结构
多个处理器组成链
父类固定算法骨架
扩展方式
新增处理器
继承父类并重写步骤
处理顺序
可动态组合
通常固定
灵活性
更高
较固定
典型场景
FilterChain、校验链
统一导入流程、固定算法流程
模板方法像固定菜谱:
责任链像流水线:
一个偏固定流程,一个偏可组合流程。
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. 可以做链路分组 不同业务场景可以使用不同链。
例如:
不要所有业务都共用一条巨型链。
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.
启示录 富贵岂由人,时会高志须酬。
能成功于千载者,必以近察远。