设计模式-责任链模式
什么是责任链模式
责任链模式(Chain of Responsibility)将多个处理者连成一条链,请求沿链依次传递,每个处理者决定是否处理、是否继续向后传递。
核心思想:消灭多层
if-else判断"谁来处理"的代码,将每个处理逻辑封装为独立的处理者,通过链式组合替代硬编码的分支判断。适用场景:
- 订单风控(黑名单 → 金额限制 → 频率限制 → 实名认证)
- 参数校验链(非空 → 格式 → 业务规则)
- 审批流(组长 → 经理 → 总监 → CEO)
- 敏感词过滤(关键词 → 正则 → AI 审核)
- Web 过滤器 / 拦截器链
传统写法的问题
public Result riskCheck(Order order) {
// 第一关:黑名单
if (blackListService.isBlocked(order.getUserId())) {
return Result.fail("用户在黑名单中");
}
// 第二关:金额上限
if (order.getAmount().compareTo(new BigDecimal("50000")) > 0) {
return Result.fail("单笔金额超限");
}
// 第三关:频率限制
if (frequencyService.isOverLimit(order.getUserId())) {
return Result.fail("操作过于频繁");
}
// 第四关:实名认证
if (!kycService.isPassed(order.getUserId())) {
return Result.fail("未完成实名认证");
}
return Result.ok();
}
问题:
- 每新增一道风控规则,必须修改此方法,违反开闭原则
- 规则顺序写死在代码中,调整顺序需要改动核心逻辑
- 各规则无法单独测试和复用
Spring 最佳实践
1. 定义处理者接口
public interface RiskHandler {
/**
* 执行顺序,数值越小越先执行
*/
int order();
/**
* 执行风控校验,返回 null 表示通过,继续向后传递
*/
Result handle(Order order);
}
关键设计:接口中包含
order()方法,链的构建顺序完全由各处理者自身声明,无需外部配置。
2. 实现各处理者(交给 Spring 管理)
@Component
public class BlackListHandler implements RiskHandler {
private final BlackListService blackListService;
@Override
public int order() { return 1; }
@Override
public Result handle(Order order) {
if (blackListService.isBlocked(order.getUserId())) {
return Result.fail("用户在黑名单中");
}
return null; // 通过,继续向后
}
}
@Component
public class AmountLimitHandler implements RiskHandler {
private static final BigDecimal MAX_AMOUNT = new BigDecimal("50000");
@Override
public int order() { return 2; }
@Override
public Result handle(Order order) {
if (order.getAmount().compareTo(MAX_AMOUNT) > 0) {
return Result.fail("单笔金额超限");
}
return null;
}
}
@Component
public class FrequencyHandler implements RiskHandler {
private final FrequencyService frequencyService;
@Override
public int order() { return 3; }
@Override
public Result handle(Order order) {
if (frequencyService.isOverLimit(order.getUserId())) {
return Result.fail("操作过于频繁");
}
return null;
}
}
@Component
public class KycHandler implements RiskHandler {
private final KycService kycService;
@Override
public int order() { return 4; }
@Override
public Result handle(Order order) {
if (!kycService.isPassed(order.getUserId())) {
return Result.fail("未完成实名认证");
}
return null;
}
}
3. 构建责任链(核心:Spring 自动注入 List)
@Component
public class RiskHandlerChain {
private final List<RiskHandler> handlers;
/**
* Spring 自动将所有 RiskHandler 实现注入为 List
* 在构造器中按 order() 排序,确保执行顺序正确
*/
public RiskHandlerChain(List<RiskHandler> handlers) {
this.handlers = handlers.stream()
.sorted(Comparator.comparingInt(RiskHandler::order))
.collect(Collectors.toUnmodifiableList());
}
/**
* 沿链执行,遇到第一个不通过的处理者立即返回(短路)
*/
public Result execute(Order order) {
for (RiskHandler handler : handlers) {
Result result = handler.handle(order);
if (result != null) {
return result; // 被拦截,直接返回失败原因
}
}
return Result.ok();
}
}
List<RiskHandler>注入时 Spring 会收集容器中所有实现。新增一道风控规则只需新建一个@Component,无需修改任何现有代码。
4. 服务层调用(零 if-else)
@Service
@RequiredArgsConstructor
public class OrderService {
private final RiskHandlerChain riskChain;
public OrderVO createOrder(OrderRequest request) {
Order order = Order.from(request);
Result riskResult = riskChain.execute(order);
if (!riskResult.isSuccess()) {
throw new BusinessException(riskResult.getMessage());
}
// 风控通过,继续业务逻辑
return doCreate(order);
}
}
两种执行模式
责任链有两种执行语义,核心区别在于:处理者失败后是立即中断还是继续向后传递。
拦截链(遇到处理者拦截即短路,上面的风控就是这种):
请求 → Handler1 通过 → Handler2 通过 → Handler3 拦截 ✗ → 返回失败
管道链(所有处理者都执行,常用于数据加工):
处理者返回带状态的
ProcessResult,链根据结果决定是中断还是继续。
public interface DataProcessor {
int order();
// 返回 ProcessResult,包含加工后的数据和是否成功
ProcessResult process(OrderData data);
}
@Value
public class ProcessResult {
boolean success;
OrderData data;
String errorMsg;
public static ProcessResult ok(OrderData data) {
return new ProcessResult(true, data, null);
}
public static ProcessResult fail(String errorMsg) {
return new ProcessResult(false, null, errorMsg);
}
}
@Component
public class DataProcessChain {
private final List<DataProcessor> processors;
public DataProcessChain(List<DataProcessor> processors) {
this.processors = processors.stream()
.sorted(Comparator.comparingInt(DataProcessor::order))
.collect(Collectors.toUnmodifiableList());
}
public ProcessResult process(OrderData data) {
for (DataProcessor processor : processors) {
ProcessResult result = processor.process(data);
if (!result.isSuccess()) {
// 快速失败:某一步加工失败,立即中断
log.error("[管道链] 处理失败 processor={} msg={}",
processor.getClass().getSimpleName(), result.getErrorMsg());
return result;
}
data = result.getData(); // 加工结果传给下一个处理者
}
return ProcessResult.ok(data);
}
}
管道链失败策略:
- Fail-Fast(快速失败):某步失败立即
return,适合上游结果是下游输入的强依赖场景- Best-Effort(容错继续):某步失败记录日志后
continue,适合各步骤相互独立、允许部分失败的场景(如异步通知、数据同步)两者代码差异只有一行:
return result换成continue,按业务选择即可。
框架中的责任链
Spring 和 Java 生态中责任链随处可见,理解模式本质后再看这些框架实现会非常清晰。
Servlet Filter(标准管道链):
@Component
@Order(1)
public class AuthFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
// 前置处理
if (!isAuthenticated(req)) {
((HttpServletResponse) res).sendError(401);
return; // 短路,不调用 chain.doFilter 即中断链
}
chain.doFilter(req, res); // 调用才继续向后传递
// 后置处理(响应返回时执行)
}
}
Spring HandlerInterceptor:
@Component
public class RateLimitInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
if (isOverLimit(request)) {
response.setStatus(429);
return false; // 返回 false 中断链
}
return true; // 返回 true 继续向后
}
}
FilterChain.doFilter()和HandlerInterceptor.preHandle()的返回值,本质上都是"是否继续向后传递"的控制开关,与我们手写的责任链逻辑完全一致。
进阶:用 @Order 注解替代 order() 方法
如果不想在接口中定义
order()方法,可以直接用 Spring 的@Order注解,Spring 注入 List 时会自动按@Order值排序:
public interface RiskHandler {
Result handle(Order order);
}
@Component
@Order(1)
public class BlackListHandler implements RiskHandler { ... }
@Component
@Order(2)
public class AmountLimitHandler implements RiskHandler { ... }
@Component
public class RiskHandlerChain {
// Spring 注入时已按 @Order 排好序,无需手动排序
private final List<RiskHandler> handlers;
public RiskHandlerChain(List<RiskHandler> handlers) {
this.handlers = List.copyOf(handlers);
}
}
推荐在接口中定义
order()方法而非依赖@Order,因为order()是接口契约的一部分,实现类必须显式声明优先级,不会因漏加注解而导致顺序错乱。