Java 设计模式—— 责任链模式:从原理到 SpringBoot 最优实现

Java 设计模式------ 责任链模式:从原理到 SpringBoot 最优实现

责任链模式是解决 "请求需多步处理" 场景的核心设计模式,通过将多个处理节点连成链,让请求沿链传递直至被处理(或全部节点处理完毕),实现 "请求发送者" 与 "处理者" 的解耦。本文将从原理拆解、2 种实战写法(Spring 事件、抽象类)、场景对比、避坑指南四个维度,帮你掌握责任链模式在实际项目中的灵活应用,尤其是在复杂业务流程中的落地技巧。

文章目录

  • [Java 设计模式------ 责任链模式:从原理到 SpringBoot 最优实现](#Java 设计模式—— 责任链模式:从原理到 SpringBoot 最优实现)
    • [一、责任链模式核心认知:看透 "链" 的本质](#一、责任链模式核心认知:看透 “链” 的本质)
      • [1. 传统流程调用的痛点](#1. 传统流程调用的痛点)
      • [2. 责任链模式的 "解耦" 逻辑](#2. 责任链模式的 “解耦” 逻辑)
    • [二、2 种实战写法:从 Spring 生态到自定义链](#二、2 种实战写法:从 Spring 生态到自定义链)
      • [写法 1:基于 Spring 事件(自动注册 + 顺序可控)](#写法 1:基于 Spring 事件(自动注册 + 顺序可控))
        • 核心思路
        • 完整代码实现
          • [1. 定义业务数据类(事件载体)](#1. 定义业务数据类(事件载体))
          • [2. 定义事件类(Spring 事件规范)](#2. 定义事件类(Spring 事件规范))
          • [3. 定义事件发布器(发起事件)](#3. 定义事件发布器(发起事件))
          • [4. 定义具体监听器(责任链节点)](#4. 定义具体监听器(责任链节点))
          • [5. 定义业务服务(实际业务逻辑)](#5. 定义业务服务(实际业务逻辑))
          • [6. 测试类(客户端:发起请求)](#6. 测试类(客户端:发起请求))
          • [7. 测试结果(控制台输出)](#7. 测试结果(控制台输出))
        • 优缺点分析
        • 适用场景
      • [写法 2:基于抽象类(手动组装 + 灵活控制)](#写法 2:基于抽象类(手动组装 + 灵活控制))
        • 核心思路
        • 完整代码实现
          • [1. 业务数据类(与写法 1 一致,复用 CancelOrderBO)](#1. 业务数据类(与写法 1 一致,复用 CancelOrderBO))
          • [2. 定义抽象处理者(责任链核心)](#2. 定义抽象处理者(责任链核心))
          • [3. 定义具体处理者(责任链节点)](#3. 定义具体处理者(责任链节点))
          • [4. 定义责任链工厂(组装链,可选)](#4. 定义责任链工厂(组装链,可选))
          • [5. 测试类(客户端:发起请求)](#5. 测试类(客户端:发起请求))
          • [6. 测试结果(正常流程,控制台输出)](#6. 测试结果(正常流程,控制台输出))
          • [7. 异常流程测试(库存处理失败)](#7. 异常流程测试(库存处理失败))
        • 优缺点分析
        • 适用场景
    • [三、关键对比:2 种写法与观察者模式的区别](#三、关键对比:2 种写法与观察者模式的区别)
      • [1. 责任链模式 2 种写法对比](#1. 责任链模式 2 种写法对比)
      • [2. 责任链模式 vs 观察者模式](#2. 责任链模式 vs 观察者模式)
    • [四、避坑指南:责任链模式实战中的 5 个关键问题](#四、避坑指南:责任链模式实战中的 5 个关键问题)
      • [1. 避免 "链断裂":确保节点正确传递请求](#1. 避免 “链断裂”:确保节点正确传递请求)
      • [2. 异常处理:统一捕获 vs 节点自行处理](#2. 异常处理:统一捕获 vs 节点自行处理)
      • [3. 链的复用:用工厂类封装组装逻辑](#3. 链的复用:用工厂类封装组装逻辑)
      • [4. 动态调整:支持运行时修改链](#4. 动态调整:支持运行时修改链)
      • [5. 性能优化:避免链过长导致响应慢](#5. 性能优化:避免链过长导致响应慢)
    • [五、总结:从 "能用" 到 "用好" 责任链模式](#五、总结:从 “能用” 到 “用好” 责任链模式)

一、责任链模式核心认知:看透 "链" 的本质

在深入代码前,先明确责任链模式的核心价值 ------ 它不是简单的 "流程调用",而是通过 "链式结构" 解决 "多处理节点灵活组合" 的痛点。

1. 传统流程调用的痛点

以 "订单取消" 场景为例,传统写法会将 "恢复库存""退回余额""归还优惠券" 等步骤硬编码在一个方法中:

java 复制代码
public void cancelOrder(String orderNo) {
   // 1. 恢复库存
   storageService.restoreStock(orderNo);

   // 2. 退回用户余额
   accountService.refundBalance(orderNo);

   // 3. 归还优惠券
   couponService.returnCoupon(orderNo);
}

这种写法的问题会随业务扩展暴露:

  • 耦合严重 :新增 "日志记录" 步骤需修改cancelOrder方法,违反开闭原则;

  • 顺序难调整:若业务要求 "先退余额再恢复库存",需手动调整代码执行顺序;

  • 容错性差:一个步骤失败会导致后续步骤全部中断,且无法灵活控制 "是否继续执行"。

2. 责任链模式的 "解耦" 逻辑

责任链模式通过 "抽象处理节点 + 链式组装" 解决上述问题,核心包含 3 个角色:

  • 抽象处理者(Abstract Handler):定义处理请求的接口,包含 "设置下一个节点" 和 "处理请求" 的方法;

  • 具体处理者(Concrete Handler):实现抽象处理者,处理自身负责的业务,若需继续传递则调用下一个节点;

  • 客户端(Client):组装责任链,发起请求(无需关心具体处理节点)。

最终实现 "新增节点不改旧代码、调整顺序只需重组链、失败控制灵活" 的理想状态。

二、2 种实战写法:从 Spring 生态到自定义链

根据项目是否依赖 Spring 框架,责任链模式有 2 种主流实现方式,适用场景和灵活性各不相同。

写法 1:基于 Spring 事件(自动注册 + 顺序可控)

核心思路

利用 Spring 的 事件驱动模型 :将 "订单取消" 封装为事件,各处理步骤(恢复库存、退余额)作为事件监听器;通过@Order注解控制监听器执行顺序,Spring 自动将监听器按顺序执行,本质是 "隐式责任链"。

完整代码实现
1. 定义业务数据类(事件载体)
java 复制代码
package com.boke.desginpattern.bo;

import lombok.AllArgsConstructor;

import lombok.Data;

import lombok.NoArgsConstructor;

/**
 * 取消订单业务数据:包含订单号、用户ID、商品ID等核心信息
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CancelOrderBO {
   private String orderNo;      // 订单号
   private Long userId;         // 用户ID
   private String productId;    // 商品I
   private Integer quantity;    // 商品数量
   private BigDecimal amount;   // 订单金额
}
2. 定义事件类(Spring 事件规范)
java 复制代码
package com.boke.desginpattern.chainspring.event;

import com.boke.desginpattern.bo.CancelOrderBO;

import org.springframework.context.ApplicationEvent;

/**
 * 取消订单事件:继承Spring ApplicationEvent,用于传递业务数据
 */
public class CancelOrderEvent extends ApplicationEvent {
   // 构造方法:传入业务数据(事件源)
   public CancelOrderEvent(CancelOrderBO cancelOrderBO) {
       super(cancelOrderBO);
   }

   // 简化获取业务数据的方法(避免强制类型转换)
   public CancelOrderBO getCancelOrderBO() {
       return (CancelOrderBO) super.getSource();
   }
}
3. 定义事件发布器(发起事件)
java 复制代码
package com.boke.desginpattern.chainspring.publisher;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;

/**
 * 事件发布器:实现ApplicationEventPublisherAware,注入Spring事件发布器
 */
@Component
public class CancelOrderEventPublisher implements ApplicationEventPublisherAware {
   // Spring自动注入的事件发布器
   private ApplicationEventPublisher applicationEventPublisher;

   // 实现接口方法:注入发布器
   @Override
   public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
       this.applicationEventPublisher = applicationEventPublisher;
   }

   // 自定义发布方法:对外提供发布取消订单事件的入口
   public void publishCancelOrderEvent(CancelOrderBO cancelOrderBO) {
       applicationEventPublisher.publishEvent(new CancelOrderEvent(cancelOrderBO));
       System.out.printf("取消订单事件发布成功:订单号[%s]%n", cancelOrderBO.getOrderNo());
   }
}
4. 定义具体监听器(责任链节点)

通过@Order注解控制执行顺序(value 值越小,执行越靠前),每个监听器负责一个业务步骤。

java 复制代码
// 监听器1:恢复库存(顺序1,最先执行)
package com.boke.desginpattern.chainspring.listener;

import com.boke.desginpattern.chainspring.event.CancelOrderEvent;
import com.boke.desginpattern.service.StorageService;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;

@Component
@Order(1)  // 执行顺序:1(最先处理)
public class StorageCancelListener implements ApplicationListener<CancelOrderEvent> {
   @Resource
   private StorageService storageService;

   // 处理事件:恢复库存
   @Override
   public void onApplicationEvent(CancelOrderEvent event) {
       CancelOrderBO bo = event.getCancelOrderBO();
       System.out.printf("开始处理库存恢复:订单号[%s],商品[%s],数量[%d]%n",
               bo.getOrderNo(), bo.getProductId(), bo.getQuantity());
       // 调用库存服务恢复库存(实际业务需包含数据库操作、异常处理)
       storageService.restoreStock(bo.getProductId(), bo.getQuantity());
       System.out.printf("库存恢复完成:订单号[%s]%n", bo.getOrderNo());
   }
}

// 监听器2:退回用户余额(顺序2,库存之后执行)
package com.boke.desginpattern.chainspring.listener;

import com.boke.desginpattern.chainspring.event.CancelOrderEvent;
import com.boke.desginpattern.service.AccountService;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;

@Component
@Order(2)  // 执行顺序:2(库存之后)
public class AccountRefundListener implements ApplicationListener<CancelOrderEvent> {
   @Resource
   private AccountService accountService;

   @Override
   public void onApplicationEvent(CancelOrderEvent event) {
       CancelOrderBO bo = event.getCancelOrderBO();
       System.out.printf("开始处理余额退回:订单号[%s],用户[%d],金额[%s]%n",
               bo.getOrderNo(), bo.getUserId(), bo.getAmount());
       // 调用账户服务退回余额
       accountService.refundBalance(bo.getUserId(), bo.getAmount());
       System.out.printf("余额退回完成:订单号[%s]%n", bo.getOrderNo());
   }
}

// 监听器3:归还优惠券(顺序3,最后执行)
package com.boke.desginpattern.chainspring.listener;

import com.boke.desginpattern.chainspring.event.CancelOrderEvent;
import com.boke.desginpattern.service.CouponService;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;

@Component
@Order(3)  // 执行顺序:3(最后处理)
public class CouponReturnListener implements ApplicationListener<CancelOrderEvent> {
   @Resource
   private CouponService couponService;

   @Override
   public void onApplicationEvent(CancelOrderEvent event) {
       CancelOrderBO bo = event.getCancelOrderBO();
       System.out.printf("开始处理优惠券归还:订单号[%s],用户[%d]%n",
               bo.getOrderNo(), bo.getUserId());
       // 调用优惠券服务归还优惠券(假设通过订单号查询关联的优惠券)
       couponService.returnCouponByOrderNo(bo.getOrderNo());
       System.out.printf("优惠券归还完成:订单号[%s]%n", bo.getOrderNo());
   }
}
5. 定义业务服务(实际业务逻辑)
java 复制代码
// 库存服务
package com.boke.desginpattern.service;

import org.springframework.stereotype.Service;

@Service
public class StorageService {
   // 恢复库存(实际需操作数据库,此处简化)
   public void restoreStock(String productId, Integer quantity) {
       // 模拟数据库操作:update storage set stock = stock + #{quantity} where product_id = #{productId}
   }
}

// 账户服务
package com.boke.desginpattern.service;

import java.math.BigDecimal;
import org.springframework.stereotype.Service;

@Service
public class AccountService {
   // 退回余额(实际需操作数据库,此处简化)
   public void refundBalance(Long userId, BigDecimal amount) {
       // 模拟数据库操作:update account set balance = balance + #{amount} where user_id = #{userId}
   }
}

// 优惠券服务
package com.boke.desginpattern.service;

import org.springframework.stereotype.Service;

@Service
public class CouponService {
   // 按订单号归还优惠券(实际需操作数据库,此处简化)
   public void returnCouponByOrderNo(String orderNo) {
       // 模拟数据库操作:update coupon set status = 'UNUSED' where order_no = #{orderNo}
   }
}
6. 测试类(客户端:发起请求)
java 复制代码
package com.boke.desginpattern.chainspring.controller;

import com.boke.desginpattern.bo.CancelOrderBO;
import com.boke.desginpattern.chainspring.publisher.CancelOrderEventPublisher;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;

/**
 * 订单控制器:发起取消订单请求,组装责任链(Spring自动完成)
 */
@RestController
@RequestMapping("/api/order")
public class OrderController {
   @Resource
   private CancelOrderEventPublisher eventPublisher;

   /**
    * 取消订单接口
    */
   @PostMapping("/cancel")
   public String cancelOrder(@RequestBody CancelOrderBO cancelOrderBO) {
       // 发布取消订单事件:Spring自动调用所有监听器(按@Order顺序)
       eventPublisher.publishCancelOrderEvent(cancelOrderBO);
       return "订单取消请求已提交,订单号:" + cancelOrderBO.getOrderNo();
   }
}
7. 测试结果(控制台输出)
text 复制代码
取消订单事件发布成功:订单号[TEST_20240601001]
开始处理库存恢复:订单号[TEST_20240601001],商品[PROD_001],数量[2]
库存恢复完成:订单号[TEST_20240601001]
开始处理余额退回:订单号[TEST_20240601001],用户[1001],金额[199.80]
余额退回完成:订单号[TEST_20240601001]
开始处理优惠券归还:订单号[TEST_20240601001],用户[1001]
优惠券归还完成:订单号[TEST_20240601001]
优缺点分析
优点 缺点
Spring 自动注册监听器,无需手动组装责任链 监听器间无直接依赖,无法控制 "前一个失败后是否继续执行"(默认继续)
通过@Order轻松调整执行顺序,维护成本低 本质是 "广播式执行",非严格意义的 "链传递"(每个监听器都接收完整事件)
符合 Spring 生态习惯,集成便捷,无需额外代码 不支持动态增减节点(需新增 / 删除监听器类)
适用场景
  • SpringBoot 项目,处理步骤无强依赖(如日志记录、通知发送等辅助步骤);

  • 无需灵活控制 "失败后是否继续" 的场景;

  • 追求开发效率,不想手动管理责任链组装。

写法 2:基于抽象类(手动组装 + 灵活控制)

核心思路

自定义 抽象处理者 + 具体处理者:抽象类定义 "设置下一个节点" 和 "处理请求" 的方法,具体处理者实现自身业务逻辑;客户端手动组装责任链,支持 "前一个节点失败后中断链""动态增减节点",是 "严格意义的责任链"。

完整代码实现
1. 业务数据类(与写法 1 一致,复用 CancelOrderBO)
2. 定义抽象处理者(责任链核心)
java 复制代码
package com.boke.desginpattern.chainabstract.handler;

import com.boke.desginpattern.bo.CancelOrderBO;

/**
 * 取消订单抽象处理者:定义责任链的核心方法
 */
public abstract class AbstractCancelOrderHandler {

   // 下一个处理节点(责任链的关键)
   protected AbstractCancelOrderHandler nextHandler;
   
   /**
    * 设置下一个处理节点
    */
   public void setNextHandler(AbstractCancelOrderHandler nextHandler) {
       this.nextHandler = nextHandler;
   }

   /**
    * 处理请求(抽象方法,由具体处理者实现)
    * @return true:处理成功,可继续传递;false:处理失败,中断传递
    */
   public abstract boolean handle(CancelOrderBO cancelOrderBO);

   /**
    * 传递请求到下一个节点(封装通用逻辑,避免具体处理者重复代码)
    */
   protected void passToNext(CancelOrderBO cancelOrderBO) {
       if (nextHandler != null) {
           System.out.printf("当前节点处理完成,传递至下一个节点:%s%n",
                   nextHandler.getClass().getSimpleName());
           nextHandler.handle(cancelOrderBO);
       } else {
           System.out.println("所有节点处理完成,责任链执行结束");
       }
   }
}
3. 定义具体处理者(责任链节点)

每个处理者实现handle方法,处理自身业务,通过passToNext传递请求(或中断)。

java 复制代码
// 具体处理者1:恢复库存
package com.boke.desginpattern.chainabstract.handler.impl;

import com.boke.desginpattern.bo.CancelOrderBO;
import com.boke.desginpattern.chainabstract.handler.AbstractCancelOrderHandler;
import com.boke.desginpattern.service.StorageService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;

@Component
public class StorageCancelHandler extends AbstractCancelOrderHandler {
   @Resource
   private StorageService storageService;
   
   @Override
   public boolean handle(CancelOrderBO cancelOrderBO) {
       try {
           System.out.printf("库存节点开始处理:订单号[%s],商品[%s],数量[%d]%n",
                   cancelOrderBO.getOrderNo(), cancelOrderBO.getProductId(), cancelOrderBO.getQuantity());
           // 核心业务:恢复库存(若失败抛异常)
           storageService.restoreStock(cancelOrderBO.getProductId(), cancelOrderBO.getQuantity());
           System.out.printf("库存节点处理成功:订单号[%s]%n", cancelOrderBO.getOrderNo());
           // 处理成功,传递至下一个节点
           passToNext(cancelOrderBO);
           return true;
       } catch (Exception e) {
           System.err.printf("库存节点处理失败:订单号[%s],原因:%s%n",
                   cancelOrderBO.getOrderNo(), e.getMessage());
           // 处理失败,中断责任链
           return false;
       }
   }
}

// 具体处理者2:退回余额
package com.boke.desginpattern.chainabstract.handler.impl;

import com.boke.desginpattern.bo.CancelOrderBO;
import com.boke.desginpattern.chainabstract.handler.AbstractCancelOrderHandler;
import com.boke.desginpattern.service.AccountService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.math.BigDecimal;

@Component
public class AccountRefundHandler extends AbstractCancelOrderHandler {
   @Resource
   private AccountService accountService;

   @Override
   public boolean handle(CancelOrderBO cancelOrderBO) {
       try {
           System.out.printf("余额节点开始处理:订单号[%s],用户[%d],金额[%s]%n",
                   cancelOrderBO.getOrderNo(), cancelOrderBO.getUserId(), cancelOrderBO.getAmount());
           // 核心业务:退回余额
           accountService.refundBalance(cancelOrderBO.getUserId(), cancelOrderBO.getAmount());
           System.out.printf("余额节点处理成功:订单号[%s]%n", cancelOrderBO.getOrderNo());
           // 传递至下一个节点
           passToNext(cancelOrderBO);
           return true;
       } catch (Exception e) {
           System.err.printf("余额节点处理失败:订单号[%s],原因:%s%n",
                   cancelOrderBO.getOrderNo(), e.getMessage());
           // 失败中断
           return false;
       }
   }
}

// 具体处理者3:归还优惠券
package com.boke.desginpattern.chainabstract.handler.impl;

import com.boke.desginpattern.bo.CancelOrderBO;
import com.boke.desginpattern.chainabstract.handler.AbstractCancelOrderHandler;
import com.boke.desginpattern.service.CouponService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;

@Component
public class CouponReturnHandler extends AbstractCancelOrderHandler {
   @Resource
   private CouponService couponService;

   @Override
   public boolean handle(CancelOrderBO cancelOrderBO) {
       try {
           System.out.printf("优惠券节点开始处理:订单号[%s],用户[%d]%n",
                   cancelOrderBO.getOrderNo(), cancelOrderBO.getUserId());
           // 核心业务:归还优惠券
           couponService.returnCouponByOrderNo(cancelOrderBO.getOrderNo());
           System.out.printf("优惠券节点处理成功:订单号[%s]%n", cancelOrderBO.getOrderNo());
           // 传递至下一个节点(若无则结束)
           passToNext(cancelOrderBO);
           return true;
       } catch (Exception e) {
           System.err.printf("优惠券节点处理失败:订单号[%s],原因:%s%n",
                   cancelOrderBO.getOrderNo(), e.getMessage());
           // 失败中断
           return false;
       }
   }
}
4. 定义责任链工厂(组装链,可选)

为避免控制器中手动组装链的代码冗余,可新增工厂类封装组装逻辑:

java 复制代码
package com.boke.desginpattern.chainabstract.factory;

import com.boke.desginpattern.chainabstract.handler.AbstractCancelOrderHandler;
import com.boke.desginpattern.chainabstract.handler.impl.AccountRefundHandler;
import com.boke.desginpattern.chainabstract.handler.impl.CouponReturnHandler;
import com.boke.desginpattern.chainabstract.handler.impl.StorageCancelHandler;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;

/**
 * 责任链工厂:封装链的组装逻辑,对外提供完整的责任链
 */
@Component
public class CancelOrderChainFactory {
   @Resource
   private StorageCancelHandler storageCancelHandler;

   @Resource
   private AccountRefundHandler accountRefundHandler;

   @Resource
   private CouponReturnHandler couponReturnHandler;

   /**
    * 构建取消订单责任链:库存 → 余额 → 优惠券
    */
   public AbstractCancelOrderHandler buildChain() {
       // 组装顺序:库存节点 → 余额节点 → 优惠券节点
       storageCancelHandler.setNextHandler(accountRefundHandler);
       accountRefundHandler.setNextHandler(couponReturnHandler);
       // 返回链的第一个节点(入口)
       return storageCancelHandler;
   }

   /**
    * 构建自定义顺序的责任链(支持动态调整)
    */
   public AbstractCancelOrderHandler buildCustomChain(AbstractCancelOrderHandler... handlers) {
       if (handlers == null || handlers.length == 0) {
           throw new IllegalArgumentException("责任链节点不能为空");
       }
       // 按数组顺序组装链
       for (int i = 0; i < handlers.length - 1; i++) {
           handlers[i].setNextHandler(handlers[i + 1]);
       }
       return handlers[0];
   }
}
5. 测试类(客户端:发起请求)
java 复制代码
package com.boke.desginpattern.chainabstract.controller;

import com.boke.desginpattern.bo.CancelOrderBO;
import com.boke.desginpattern.chainabstract.factory.CancelOrderChainFactory;
import com.boke.desginpattern.chainabstract.handler.AbstractCancelOrderHandler;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;

/**
 * 订单控制器:通过工厂获取责任链,发起请求
 */
@RestController
@RequestMapping("/api/order")
public class OrderController {
   @Resource
   private CancelOrderChainFactory chainFactory;

   /**
    * 取消订单接口(使用默认责任链)
    */
   @PostMapping("/cancel")
   public String cancelOrder(@RequestBody CancelOrderBO cancelOrderBO) {
       // 1. 从工厂获取组装好的责任链
       AbstractCancelOrderHandler chain = chainFactory.buildChain();
       
       // 2. 发起请求:调用链的第一个节点
       boolean result = chain.handle(cancelOrderBO);

       // 3. 根据结果返回响应
       if (result) {
           return "订单取消成功(所有节点处理完成),订单号:" + cancelOrderBO.getOrderNo();
       } else {
           return "订单取消失败(部分节点处理异常),订单号:" + cancelOrderBO.getOrderNo();
       }
   }

   /**
    * 取消订单接口(使用自定义顺序的责任链,示例:先退余额再恢复库存)
    */
   @PostMapping("/cancel/custom")
   public String cancelOrderCustom(@RequestBody CancelOrderBO cancelOrderBO) {
       // 1. 获取所有节点(实际项目可通过Spring注入)
       AbstractCancelOrderHandler storageHandler = chainFactory.getStorageCancelHandler();
       AbstractCancelOrderHandler accountHandler = chainFactory.getAccountRefundHandler();
       AbstractCancelOrderHandler couponHandler = chainFactory.getCouponReturnHandler();
       
       // 2. 自定义组装顺序:余额 → 库存 → 优惠券
       AbstractCancelOrderHandler customChain = chainFactory.buildCustomChain(accountHandler, storageHandler, couponHandler);
       
       // 3. 发起请求
       boolean result = customChain.handle(cancelOrderBO);
       return result ? "自定义顺序取消成功" : "自定义顺序取消失败";
   }
}
6. 测试结果(正常流程,控制台输出)
text 复制代码
库存节点开始处理:订单号[TEST_20240601002],商品[PROD_002],数量[1]
库存节点处理成功:订单号[TEST_20240601002]
当前节点处理完成,传递至下一个节点:AccountRefundHandler
余额节点开始处理:订单号[TEST_20240601002],用户[1002],金额[99.90]
余额节点处理成功:订单号[TEST_20240601002]
当前节点处理完成,传递至下一个节点:CouponReturnHandler
优惠券节点开始处理:订单号[TEST_20240601002],用户[1002]
优惠券节点处理成功:订单号[TEST_20240601002]
当前节点处理完成,传递至下一个节点:null
所有节点处理完成,责任链执行结束
7. 异常流程测试(库存处理失败)
text 复制代码
库存节点开始处理:订单号[TEST_20240601003],商品[PROD_003],数量[3]
库存节点处理失败:订单号[TEST_20240601003],原因:商品不存在

(余额、优惠券节点未执行,链中断)

优缺点分析
优点 缺点
手动组装链,支持动态调整顺序、增减节点,灵活性极高 需手动维护链的组装逻辑(可通过工厂类优化)
支持 "前一个节点失败后中断链",容错性强 代码量略多(需定义抽象类、具体处理者)
节点间通过 "下一个节点" 强关联,符合严格的责任链定义 非 Spring 项目需手动管理节点的创建(如 new 对象)
适用场景
  • 中大型项目,处理步骤有强依赖(如 "必须恢复库存后才能退余额");

  • 需要灵活控制 "失败后是否继续执行" 的场景(如支付失败需中断后续步骤);

  • 需动态调整处理顺序或增减节点的业务(如活动期间新增 "赠送积分" 步骤)。

三、关键对比:2 种写法与观察者模式的区别

1. 责任链模式 2 种写法对比

对比维度 基于 Spring 事件 基于抽象类
链的组装 Spring 自动完成(监听器注册) 手动组装(工厂类封装)
执行顺序控制 @Order注解,简单直观 手动setNextHandler,灵活
失败控制 无法中断(默认所有监听器执行) 可中断(返回 false 或不调用passToNext
节点依赖 无依赖(各监听器独立接收事件) 强依赖(前一个节点决定是否传递)
适用场景 辅助步骤、无强依赖 核心步骤、有强依赖

2. 责任链模式 vs 观察者模式

很多人会混淆这两种模式,核心区别在于 "节点关系" 和 "执行逻辑":

  • 观察者模式:"一对多广播",观察者间无顺序、无依赖,发布者发送事件后所有观察者都会执行(如订单创建后,日志、通知、统计同时执行);

  • 责任链模式:"链式传递",节点间有顺序、有依赖,请求沿链传递,可中断(如订单取消需按 "库存→余额→优惠券" 顺序执行,前一步失败则中断)。

四、避坑指南:责任链模式实战中的 5 个关键问题

1. 避免 "链断裂":确保节点正确传递请求

问题:具体处理者忘记调用passToNext,导致链中断(非预期)。

解决方案:

  • 在抽象类中封装passToNext方法,强制具体处理者调用(或明确中断);

  • 新增 "链完整性校验":工厂类组装链后,检查是否存在 "最后一个节点未设置为 null" 的情况。

2. 异常处理:统一捕获 vs 节点自行处理

问题:某个节点抛出未捕获异常,导致整个链中断且无法定位问题。

解决方案:

  • 每个具体处理者的handle方法中捕获异常,记录日志后返回false(明确中断);

  • 抽象类中新增handleException抽象方法,统一异常处理逻辑(如告警、重试)。

3. 链的复用:用工厂类封装组装逻辑

问题:多个客户端需要使用相同的责任链,重复组装代码冗余。

解决方案:

  • 新增责任链工厂类(如CancelOrderChainFactory),封装组装逻辑,对外提供buildChain方法;

  • 支持自定义链(如buildCustomChain),满足不同场景的需求。

4. 动态调整:支持运行时修改链

问题:需要根据业务条件动态增减节点(如 VIP 用户取消订单需新增 "赠送积分" 步骤)。

解决方案:

  • 工厂类中新增addHandler/removeHandler方法,支持运行时调整节点;

  • 结合配置中心(如 Nacos),通过配置决定是否启用某节点(如pay.strategy.coupon.enabled=true)。

5. 性能优化:避免链过长导致响应慢

问题:责任链节点过多(如超过 10 个),每个节点处理耗时,导致整体响应慢。

解决方案:

  • 拆分长链为多个短链(如 "核心步骤链"+"辅助步骤链"),核心步骤同步执行,辅助步骤异步执行;

  • 节点处理逻辑异步化(如用@Async注解,需注意异步后的异常处理)。

五、总结:从 "能用" 到 "用好" 责任链模式

责任链模式的核心不是 "链式调用",而是 "灵活组合处理节点,解耦请求与处理"。选择哪种写法,需根据业务场景判断:

  • 若用 SpringBoot 且处理步骤无强依赖,选 "基于 Spring 事件"(开发快、维护简单);

  • 若处理步骤有强依赖或需灵活控制失败,选 "基于抽象类"(灵活、容错性强)。

在实际项目中,责任链模式常与其他模式结合使用:

  • 责任链 + 工厂模式:封装链的组装逻辑,提高复用性;

  • 责任链 + 模板方法:抽象类中定义处理流程模板,具体处理者实现差异化逻辑;

  • 责任链 + 观察者模式:核心步骤用责任链(同步),辅助步骤用观察者(异步)。

掌握这些技巧,才能在复杂业务中真正发挥责任链模式的价值,写出 "易扩展、易维护、高容错" 的代码。

相关推荐
自由的疯7 小时前
Java Kubernetes本地部署RuoYi框架jar包
java·后端·架构
Query*7 小时前
Java 设计模式——适配器模式:从原理到3种实战的完整指南
java·设计模式·适配器模式
Meteors.7 小时前
23种设计模式——状态模式(State Pattern)
java·设计模式·状态模式
yaoxin52112310 小时前
211. Java 异常 - Java 异常机制总结
java·开发语言·python
Predestination王瀞潞14 小时前
Java EE开发技术(Servlet整合JDBC银行管理系统-上)
java·servlet·java-ee·jdbc
寻星探路14 小时前
Java EE初阶启程记13---JUC(java.util.concurrent) 的常见类
java·开发语言·java-ee
怪兽201415 小时前
什么是 Redis?
java·数据库·redis·缓存·面试
Gu_yyqx15 小时前
Java 队列
java
落日漫游15 小时前
数据结构笔试核心考点
java·开发语言·算法