当前位置: 首页 > news >正文

基于 Spring 的策略模式框架,用于根据不同的类的标识获取对应的处理器实例

基于 Spring 的策略模式框架,用于根据不同的类的标识获取对应的处理器实例

文章目录

  • 基于 Spring 的策略模式框架,用于根据不同的类的标识获取对应的处理器实例
    • @[toc]
    • 一、策略模式的核心角色(Spring 中对应实现)
    • 二、实现方式
      • 方式一:`Spring 自动扫描并组装所有策略实现类到 Map中`
        • 1. 核心注册类实现BeanFactoryPostProcessor
        • 2. BeforeApproveHandler 注解
        • 3. 业务BeforeApproveHandleService 接口
        • 4. 具体业务处理器实现类(以第一个为例)
        • 5. 具体业务处理器实现类(以第二个为例)
        • 6. 具体业务处理器实现类(以第三个为例)
        • 7. 策略上下文(管理和选择策略)
        • 8. 客户端使用
        • 9. 工作流程
      • 方式二:`Spring 自动扫描并组装所有策略实现类到 List 中`
        • 1. 定义策略接口
        • 2. 实现具体策略类
        • 3. 策略上下文(注入 List <策略接口>)
        • 4. 使用策略模式
        • 5. 测试效果

比方说我们有一个支付方式业务功能,这个支付功能里面有多种不同支付方式的场景的需求,如微信支付、支付宝、银联……等支付方式但是这些支付方式都需要走一样的,申请,审批……等过程,只是业务逻辑不同而已,那么我们就可以定义一个接口,用不同的实现类来实现这个接口,通过不同的业务走不同的实现,那么我们就可以使用策略模式解决,而不用写一堆 if---else

一、策略模式的核心角色(Spring 中对应实现)

首先明确策略模式的 3 个核心角色,以及在 Spring 中如何落地:

策略模式角色作用说明Spring 中常见实现形式
策略接口(Strategy)定义所有策略类的统一方法(规范策略的行为)自定义接口(如 PayStrategyOrderHandler
策略实现类(ConcreteStrategy)实现策略接口,提供具体的业务逻辑(如 “支付宝支付”“微信支付”)标注 @Component 的 Spring Bean
策略上下文(Context)持有策略接口的引用,负责动态选择 / 注入具体策略,并对外提供调用入口注入策略集合(如 Map<策略标识, 策略接口>

二、实现方式

方式一:Spring 自动扫描并组装所有策略实现类到 Map中

1. 核心注册类实现BeanFactoryPostProcessor

实现 BeanFactoryPostProcessor 接口,在 Spring 容器加载完所有 Bean 定义(但未实例化任何 Bean 之前),主动扫描并收集策略类,构建策略标识与策略类的映射关系,再将这个映射关系注册到 Spring 容器中,供后续动态获取策略实例

// 引入Lombok的@Slf4j注解,自动生成日志对象
@Slf4j
// 标记为Spring组件,使其被Spring容器管理
@Component
// 实现BeanFactoryPostProcessor接口,在bean实例化前处理bean定义
public class RegisterHandleContext implements BeanFactoryPostProcessor {// 定义要扫描的包路径private static final String SCAN_PACKGE = "com.hy.material.flow";// 实现接口方法,在bean工厂处理完成后执行@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {// 创建Map存储"流程标识-处理器类"的映射(前置审批处理器)Map<String, Class> beforeApproveService = new HashMap<>();// 这个Map在当前代码中未使用,可能是预留扩展Map<String, Class> processNodeCodeService = new HashMap<>();// 扫描指定包下的所有类,并对每个类进行处理ClassScanner.scanPackage(SCAN_PACKGE).forEach(clazz -> {// 检查类上是否有@BeforeApproveHandler注解BeforeApproveHandler beforeApproveHandler = clazz.getAnnotation(BeforeApproveHandler.class);// 如果有该注解if (beforeApproveHandler != null) {// 将注解的值(流程标识)作为key,类本身作为value存入MapbeforeApproveService.put(beforeApproveHandler.value(), clazz);}});// 创建FlowHandleContext实例,传入处理器映射关系FlowHandleContext flowHandleContext = new FlowHandleContext(beforeApproveService);// 将FlowHandleContext实例注册为Spring单例bean,bean名称为类的全限定名beanFactory.registerSingleton(FlowHandleContext.class.getName(), flowHandleContext);}
}
2. BeforeApproveHandler 注解
// 注解作用目标:类/接口
@Target({ElementType.TYPE})
// 注解保留策略:运行时保留(可以通过反射获取)
@Retention(RetentionPolicy.RUNTIME)
// 自定义注解,用于标记前置审批处理器
public @interface BeforeApproveHandler {// 注解属性,存储流程标识,默认值为空字符串String value() default "";
}
3. 业务BeforeApproveHandleService 接口
// 前置审批处理器的接口,定义了处理器的标准
public interface BeforeApproveHandleService {// 统一方法:处理支付String pay(double amount);
}
4. 具体业务处理器实现类(以第一个为例)
// 引入Lombok的@Slf4j注解,自动生成日志对象
@Slf4j
// 标记为Spring服务类,注册为bean
@Service
// Lombok注解,自动生成全参构造方法
@AllArgsConstructor
// 标记为前置审批处理器,指定对应的支付宝支付,FlowConstants是自定义的枚举类
@BeforeApproveHandler(FlowConstants.alipay)
// 实现前置审批处理器接口
public class GylAllocateApplyApproveHandleExecute implements BeforeApproveHandleService {@Overridepublic String pay(double amount) {return "支付宝支付成功,金额:" + amount;}
}
5. 具体业务处理器实现类(以第二个为例)
@Slf4j
@Service
@AllArgsConstructor
@BeforeApproveHandler(FlowConstants.wechatPay)
public class GylAllocateApplyOrderApproveHandleExecute implements BeforeApproveHandleService {@Overridepublic String pay(double amount) {return "微信支付成功,金额:" + amount;}
}
6. 具体业务处理器实现类(以第三个为例)
@Slf4j
@Service
@AllArgsConstructor
@BeforeApproveHandler(FlowConstants.wangyin)
public class GylAllocateReceiptHandleExecute implements BeforeApproveHandleService {@Overridepublic String pay(double amount) {return "网银支付成功,金额:" + amount;}
}
7. 策略上下文(管理和选择策略)
// 引入Lombok的@Slf4j注解,自动生成日志对象
@Slf4j
// 处理器上下文类,用于管理和获取处理器
public class FlowHandleContext {// 存储"流程标识-处理器类"的映射关系private Map<String, Class> flowService;// 构造方法,初始化映射关系public FlowHandleContext(Map<String, Class> flowService) {this.flowService = flowService;}// 根据流程标识获取对应的处理器实例public BeforeApproveHandleService getHandle(String processKey) {try {// 根据processKey获取对应的枚举值ProcessKeyEnum processKeyEnum = ProcessKeyEnum.getByProcessKey(processKey);// 获取Spring应用上下文ApplicationContext applicationContext = SpringContextHolder.getApplicationContext();// 从映射中获取处理器类,如果找不到则使用默认的支付标识对应的处理器// 然后从Spring容器中获取该类的实例并返回return (BeforeApproveHandleService) applicationContext.getBean(flowService.getOrDefault(processKey, flowService.get(processKeyEnum.DEFAULT.getProcessKey())));} catch (Exception e) {// 记录错误日志log.error(String.format("Cannot find class [%s] in ProcessKeyEnum[%s].", flowService.toString(), processKey), e);}// 如果获取失败,抛出异常throw new CheckedException("不存在对应的支付");}
}
8. 客户端使用
@SpringBootTest
public class PaymentTest {@Autowiredprivate FlowHandleContext flowHandleContext;@Testpublic void testPay() {// 1. 选择支付宝支付(传入策略的Bean名称)String result1 = paymentContext.getHandle("alipay");System.out.println(result1); // 输出:支付宝支付成功// 2. 选择微信支付String result2 = paymentContext.getHandle("wechatPay");System.out.println(result2); // 输出:微信支付成功}
}

这样我们通过代码实现了一个基于 Spring 的策略模式框架,用于根据不同的流程标识(processKey)获取对应的处理器实例

9. 工作流程
  1. Spring 容器启动,加载所有 bean 定义
  2. RegisterHandleContext 作为 BeanFactoryPostProcessor 被调用
  3. 扫描指定包下的类,收集带有 @BeforeApproveHandler 注解的处理器类
  4. 创建 FlowHandleContext 并注册到 Spring 容器
  5. 当需要处理某个流程时,调用 FlowHandleContext.getHandle(processKey) 获取对应处理器
  6. 使用获取到的处理器实例执行具体的处理逻辑

方式二:Spring 自动扫描并组装所有策略实现类到 List 中

1. 定义策略接口

首先创建一个策略接口,声明所有策略需要实现的方法(包括一个用于标识策略的方法,方便后续匹配):

// 策略接口
public interface PaymentStrategy {// 策略标识:返回当前支付方式的类型(如"alipay"、"wechat")String getPaymentType();// 核心业务方法:处理支付String pay(BigDecimal amount);
}
2. 实现具体策略类

创建多个实现类,每个类对应一种具体策略,并使用 @Component 注解注册为 Spring Bean:

// 支付宝支付策略
@Component
public class AlipayStrategy implements PaymentStrategy {@Overridepublic String getPaymentType() {return "alipay"; // 策略标识}@Overridepublic String pay(BigDecimal amount) {return "支付宝支付成功,金额:" + amount.toPlainString();}
}// 微信支付策略
@Component
public class WechatPaymentStrategy implements PaymentStrategy {@Overridepublic String getPaymentType() {return "wechat"; // 策略标识}@Overridepublic String pay(BigDecimal amount) {return "微信支付成功,金额:" + amount.toPlainString();}
}// 银联支付策略
@Component
public class UnionPayStrategy implements PaymentStrategy {@Overridepublic String getPaymentType() {return "unionpay"; // 策略标识}@Overridepublic String pay(BigDecimal amount) {return "银联支付成功,金额:" + amount.toPlainString();}
}
3. 策略上下文(注入 List <策略接口>)

创建策略上下文类,通过构造器注入 List<PaymentStrategy>Spring 会自动将所有实现了该接口的 Bean 收集到这个 List 中。上下文类负责根据条件匹配具体策略:

// 策略上下文
@Component
public class PaymentContext {// Spring 自动注入:所有 PaymentStrategy 实现类的实例会被收集到 List 中private final List<PaymentStrategy> paymentStrategies;// 构造器注入(推荐,确保依赖不可变)public PaymentContext(List<PaymentStrategy> paymentStrategies) {this.paymentStrategies = paymentStrategies;}// 核心方法:根据支付类型匹配并执行对应的策略public String executePayment(String paymentType, BigDecimal amount) {// 遍历 List 匹配策略标识PaymentStrategy strategy = paymentStrategies.stream().filter(s -> s.getPaymentType().equals(paymentType)).findFirst().orElseThrow(() -> new IllegalArgumentException("不支持的支付方式:" + paymentType));// 执行策略return strategy.pay(amount);}// 扩展:获取所有支持的支付方式public List<String> getAllSupportedPaymentTypes() {return paymentStrategies.stream().map(PaymentStrategy::getPaymentType).collect(Collectors.toList());}
}
4. 使用策略模式

在业务代码中注入 PaymentContext,直接调用即可:

@Service
public class OrderService {private final PaymentContext paymentContext;@Autowiredpublic OrderService(PaymentContext paymentContext) {this.paymentContext = paymentContext;}public String processPayment(String paymentType, BigDecimal amount) {// 调用上下文执行支付(自动匹配策略)return paymentContext.executePayment(paymentType, amount);}public List<String> getSupportedPayments() {// 获取所有支持的支付方式return paymentContext.getAllSupportedPaymentTypes();}
}
5. 测试效果
@SpringBootTest
public class PaymentTest {@Autowiredprivate OrderService orderService;@Testpublic void testPayment() {// 测试微信支付String result1 = orderService.processPayment("wechat", new BigDecimal("100"));System.out.println(result1); // 输出:微信支付成功,金额:100// 测试支付宝支付String result2 = orderService.processPayment("alipay", new BigDecimal("200"));System.out.println(result2); // 输出:支付宝支付成功,金额:200// 查看所有支持的支付方式List<String> types = orderService.getSupportedPayments();System.out.println(types); // 输出:[alipay, wechat, unionpay]}
}

文章转载自:

http://YmSzyTGf.kjpny.cn
http://MhrJ6FtV.kjpny.cn
http://IG9jYnH8.kjpny.cn
http://mgLIBDQS.kjpny.cn
http://PDB45Jyj.kjpny.cn
http://1IqQwotE.kjpny.cn
http://sJmOYRod.kjpny.cn
http://3P3bGcol.kjpny.cn
http://UCxWPyrj.kjpny.cn
http://4phCb7jP.kjpny.cn
http://Un8U8bvE.kjpny.cn
http://PAUxzPmX.kjpny.cn
http://NM1ScXGc.kjpny.cn
http://WCLMFNfE.kjpny.cn
http://73psmWa1.kjpny.cn
http://Nz12DK8A.kjpny.cn
http://SLKGAmZ9.kjpny.cn
http://NENupSOw.kjpny.cn
http://8hEkUQwk.kjpny.cn
http://cvobcIq9.kjpny.cn
http://8DOwFtK6.kjpny.cn
http://9eXT44w0.kjpny.cn
http://7Mu0g3ct.kjpny.cn
http://eCVGLlJE.kjpny.cn
http://lczzuDmF.kjpny.cn
http://HMdCF4pW.kjpny.cn
http://zSNWCHra.kjpny.cn
http://X633uzvr.kjpny.cn
http://AVXcso69.kjpny.cn
http://WPuCiumA.kjpny.cn
http://www.dtcms.com/a/382231.html

相关文章:

  • Python:OpenCV 教程——从传统视觉到深度学习:YOLOv8 与 OpenCV DNN 模块协同实现工业缺陷检测
  • UTC时间戳转换
  • 【Unity进阶】Unity发布PC端,隐藏并自定义默认标题栏
  • 【Qt】编写Qt自定义Ui控件步骤
  • HTTP 状态码背后的逻辑:从请求到响应的完整流程解析(含完整流程图)
  • 如何规划活动宣传软文的发布节奏?
  • 什么是NTP?
  • n8n工作流平台入门学习指南
  • JVM 四大晋升机制
  • ES——(一)基本概念
  • 算法提升之树形数据结构
  • 使用 OpenTelemetry 从你的日志中获取更多信息
  • Java中IntStream的详细用法及典型案例
  • Python ast模块(Abstract Syntax Trees,抽象语法树)介绍及使用
  • UFO²:微软推出的新一代桌面 Agent 操作系统,深度整合 Windows 与智能自动化
  • 嵌入式ARM SOC开发中文专题分享一:ARM SOC外围资源介绍
  • Java 大视界 -- 基于 Java 的大数据分布式计算在气象灾害数值模拟与预警中的应用
  • Python项目全面打包指南:从EXE到绿色软件包
  • C语言---运算符
  • 什么是包装类
  • 59.[前端开发-Vue3]Day01-Vue初体验-MVVM-模板语法-常用指令
  • 1.13 Memory Profiler Package - Unity Objects(unity对象页签)
  • Nginx 请求到达nginx,但是到不了业务服?报错408
  • 若依分库分表,在admin模块可查询子库,在API模块无法查询
  • 幸运盒项目—测试报告
  • 如何告诉AI你的写作任务?
  • Windows11设置Jar包打开方式
  • 尝试MuJS
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘modin’问题
  • SceneSplat