欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 美食 > SpringBoot - 用责任链模式实现业务编排

SpringBoot - 用责任链模式实现业务编排

2025/4/13 0:59:01 来源:https://blog.csdn.net/yangshangwei/article/details/146099231  浏览:    关键词:SpringBoot - 用责任链模式实现业务编排

文章目录

  • 前因
  • 责任链:像工作台一样组织代码
  • Code
    • SEQ
    • 3.1 定义处理器规范
    • 3.2 实现具体处理器
    • 3.3 共享上下文
    • 3.4 组装责任链
  • 适用场景
  • 优势

在这里插入图片描述


前因

2000多行的业务逻辑里,各种校验规则、促销计算、库存操作像意大利面条一样缠绕在一起。最要命的是这样的代码结构:

public void createOrder(OrderRequest request) {// 参数校验if(request.getUserId() == null){throw new Exception("用户ID不能为空");}if(request.getItems().isEmpty()){throw new Exception("商品不能为空");}// 库存检查for(Item item : request.getItems()){if(!inventoryService.checkStock(item)){throw new Exception("库存不足");}}// 优惠计算if(request.getCouponId() != null){// 复杂的优惠计算逻辑...}// 风控检查if(riskService.checkRisk(request)){throw new Exception("风控拦截");}// 保存订单// ...
}

每次需求变更都像在雷区跳舞,稍有不慎就会引发连锁反应。新来的小伙伴看着满屏的if-else,战战兢兢地问:“咱们能不能换个写法?”


责任链:像工作台一样组织代码

这时候就该责任链模式登场了!这个设计模式的核心思想是:把每个处理步骤拆成独立的处理器,像流水线一样连接起来

想象一下快递分拣系统:

  1. 包裹先过安检机(校验处理器)
  2. 然后到分拣区(路由处理器)
  3. 接着称重计费(价格处理器)
  4. 最后装车发货(持久化处理器)

每个环节只关心自己的职责,处理完就交给下一个环节。这样无论是要调整环节顺序,还是增加新的处理环节,都变得非常灵活。

Code

SEQ

客户端 OrderController OrderChainManager ValidationHandler StockCheckHandler DiscountHandler OrderContext POST /orders (OrderRequest) 创建OrderContext executeChain(context) 获取所有处理器并排序 handle(context) 参数校验 return false return true alt [校验失败] [校验成功] handle(context) 库存检查 return false return true alt [库存不足] [库存充足] handle(context) 优惠计算 return true 继续后续处理器... alt [库存充足] alt [校验成功] 返回OrderResult 200 OK (OrderResult) 400 BadRequest (ErrorInfo) alt [全部成功] [任何失败] 客户端 OrderController OrderChainManager ValidationHandler StockCheckHandler DiscountHandler OrderContext

3.1 定义处理器规范

package com.artisan.chain.handler;import com.artisan.chain.model.OrderContext;/*** 处理器接口定义*/
public interface OrderHandler {/*** 处理 : 返回true ,继续处理,返回false,终止处理** @param orderContext* @return*/boolean handle(OrderContext orderContext);/*** 获取处理顺序** @return*/int getOrder();
}

3.2 实现具体处理器

参数校验处理器

 package com.artisan.chain.handler;import com.artisan.chain.model.OrderContext;
import com.artisan.chain.model.OrderRequest;
import com.artisan.chain.model.OrderResult;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;@Component
@Order(100)
public class ValidationHandler implements OrderHandler {/*** 处理订单请求的函数* 该函数主要用于验证订单请求中的必要信息是否完整* 如果验证失败,它会设置上下文结果并返回false** @param context 订单上下文,包含订单请求和结果* @return 如果验证通过,返回true;否则返回false*/@Overridepublic boolean handle(OrderContext context) {// 获取订单请求OrderRequest request = context.getRequest();// 验证用户ID是否为空if (StringUtils.isEmpty(request.getUserId())) {// 如果用户ID为空,设置验证失败的结果并返回context.setResult(OrderResult.fail("VALIDATION_ERROR", "用户ID不能为空"));return false;}// 验证订单商品列表是否为空if (CollectionUtils.isEmpty(request.getItems())) {// 如果订单商品列表为空,设置验证失败的结果并返回context.setResult(OrderResult.fail("VALIDATION_ERROR", "订单商品不能为空"));return false;}// 如果所有验证都通过,返回truereturn true;}/*** 获取当前对象的顺序值** 顺序值用于确定对象处理的优先级或显示顺序* 值越小 优先级越高** @return 返回顺序值100,表示当前对象的默认顺序位置*/@Overridepublic int getOrder() {return 100;}
}

库存检查处理器

 package com.artisan.chain.handler;import com.artisan.chain.model.OrderContext;
import com.artisan.chain.model.OrderRequest;
import com.artisan.chain.model.OrderResult;
import com.artisan.chain.service.InventoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;/*** 库存检查处理器,用于在订单处理流程中检查商品库存*/
@Component
@Order(200)
public class StockCheckHandler implements OrderHandler{// 注入库存服务,用于检查商品库存情况@Autowiredprivate InventoryService inventoryService;/*** 处理订单中的库存检查逻辑* 遍历订单中的每项商品,检查库存是否充足* 如果任何商品的库存不足,订单处理失败,并更新订单上下文的结果** @param context 订单上下文,包含订单请求和处理结果* @return 如果所有商品库存充足,返回true;否则返回false*/@Overridepublic boolean handle(OrderContext context) {// 遍历订单中的每个商品项,检查库存for (OrderRequest.OrderItem item : context.getRequest().getItems()) {// 如果库存检查失败,更新订单上下文的结果为库存错误,并返回falseif (!inventoryService.checkStock(item.getSkuId(), item.getQuantity())) {context.setResult(OrderResult.fail("STOCK_ERROR","商品[" + item.getSkuId() + "]库存不足"));return false;}}// 所有商品库存充足,返回truereturn true;}/*** 获取订单处理的顺序* 用于确定在订单处理流程中执行库存检查的顺序** @return 订单处理的顺序值*/@Overridepublic int getOrder() {return 200;}
}
package com.artisan.chain.handler;import com.artisan.chain.model.OrderContext;
import com.artisan.chain.model.OrderRequest;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;import java.util.List;/*** 定义一个处理折扣的组件*/
@Component
@Order(300)
public class DiscountHandler implements OrderHandler {/*** 处理订单中的折扣计算** @param context 订单上下文,包含订单请求和属性* @return 总是返回true,表示处理成功*/@Overridepublic boolean handle(OrderContext context) {// 模拟优惠计算double total = calculateTotal(context.getRequest().getItems());double discount = calculateDiscount(total, context.getRequest().getCouponId());// 将最终金额存入上下文中context.getAttributes().put("finalAmount", total - discount);return true;}/*** 计算订单总金额** @param items 订单中的商品列表* @return 订单总金额*/private double calculateTotal(List<OrderRequest.OrderItem> items) {// 模拟价格计算,这里简化处理,实际应根据商品价格和数量计算return items.stream().mapToDouble(item -> item.getQuantity() * 100.0) // 模拟价格.sum();}/*** 计算折扣金额** @param total    订单总金额* @param couponId 优惠券ID,如果为空则不应用折扣* @return 折扣金额*/private double calculateDiscount(double total, String couponId) {// 模拟优惠计算,如果有优惠券ID,则应用10%的折扣return couponId != null ? total * 0.1 : 0;}/*** 获取处理顺序** @return 处理顺序值*/@Overridepublic int getOrder() {return 300;}
}

3.3 共享上下文

package com.artisan.chain.model;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.HashMap;
import java.util.Map;/*** 上下文对象(共享数据载体)** 订单上下文类,用于处理订单请求并生成订单结果* 它封装了订单请求、处理结果以及相关属性**/@Data
@NoArgsConstructor
@AllArgsConstructor
public class OrderContext {/*** 订单请求对象,包含订单的相关请求信息*/private OrderRequest request;/*** 订单处理结果对象,用于存储订单处理后的信息*/private OrderResult result = new OrderResult();/*** 订单相关属性集合,用于存储订单处理过程中需要的临时信息* 键为属性名称,值为属性值*/private Map<String, Object> attributes = new HashMap<>();/*** 构造方法,初始化订单上下文** @param request 订单请求对象,不能为空*/public OrderContext(OrderRequest request) {this.request = request;}
}

3.4 组装责任链

 package com.artisan.chain.manager;import com.artisan.chain.handler.OrderHandler;
import com.artisan.chain.model.OrderContext;
import com.artisan.chain.model.OrderResult;
import org.springframework.stereotype.Component;import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;/*** 订单链式处理器管理类* 通过链式调用多个OrderHandler来处理订单逻辑* 该类负责构建和管理这些处理订单的处理器链*/
@Component
public  class OrderChainManager {/*** 存储所有的订单处理器,按照处理顺序排序*/private final List<OrderHandler> handlers;/*** 构造函数,初始化订单处理器链* @param handlers 一个未排序的订单处理器集合*/public OrderChainManager(List<OrderHandler> handlers) {// 根据每个处理器的顺序值进行排序,确保它们按照正确的顺序执行this.handlers = handlers.stream().sorted(Comparator.comparingInt(OrderHandler::getOrder)).collect(Collectors.toList());}/*** 执行订单处理逻辑* 遍历每个订单处理器,直到所有处理器都处理完毕或某个处理器决定中断链式处理* @param context 订单上下文,包含订单的处理信息和结果* @return 处理后的订单结果*/public OrderResult execute(OrderContext context) {// 遍历处理器链,如果某个处理器处理失败(返回false),则中断链式处理for (OrderHandler handler : handlers) {if (!handler.handle(context)) {break;}}// 返回最终的订单处理结果return context.getResult();}}
package com.artisan.chain.config;import com.artisan.chain.handler.OrderHandler;
import com.artisan.chain.manager.OrderChainManager;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.Collections;
import java.util.List;/*** 配置类,用于定义和配置订单处理链相关的Bean*/
@Configuration
public class HandlerConfig {/*** 创建并配置OrderChainManager Bean** @param handlersProvider 一个对象提供者,用于提供订单处理器列表如果未找到则提供一个空列表* @return 返回一个OrderChainManager实例,用于管理订单处理链*/@Beanpublic OrderChainManager orderChainManager(ObjectProvider<List<OrderHandler>> handlersProvider) {// 初始化OrderChainManager,使用提供的订单处理器列表,如果没有提供则使用空列表return new OrderChainManager(handlersProvider.getIfAvailable(Collections::emptyList));}
}
# 测试成功案例
curl -X POST http://localhost:8080/api/orders \
-H "Content-Type: application/json" \
-d '{"userId": "user123","items": [{"skuId": "SKU001", "quantity": 2},{"skuId": "SKU002", "quantity": 1}],"couponId": "COUPON2023"
}'# 测试库存不足
curl -X POST http://localhost:8080/api/orders \
-H "Content-Type: application/json" \
-d '{"userId": "user123","items": [{"skuId": "SKU001", "quantity": 200}]
}'

适用场景

  • 多步骤流程:订单创建、审批流、支付流程等

  • 动态业务:需要频繁调整步骤顺序的业务

  • 复杂校验:多层次、多条件的校验场景

  • 插件式架构:需要动态加载/卸载功能的系统

优势

  • 解耦性:每个处理逻辑独立成Handler,修改单个处理器不影响其他组件
  • 可扩展性:新增业务逻辑只需添加新Handler,无需修改主流程
  • 动态编排:通过配置灵活调整处理器执行顺序和启用状态
  • 可测试性:每个Handler可单独进行单元测试
  • 复用性:通用处理器(如日志记录)可跨多个业务场景复用

在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词