基于 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. 测试效果
文章目录
- 基于 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) | 定义所有策略类的统一方法(规范策略的行为) | 自定义接口(如 PayStrategy 、OrderHandler ) |
策略实现类(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. 工作流程
- Spring 容器启动,加载所有 bean 定义
RegisterHandleContext
作为BeanFactoryPostProcessor
被调用- 扫描指定包下的类,收集带有
@BeforeApproveHandler
注解的处理器类 - 创建
FlowHandleContext
并注册到 Spring 容器 - 当需要处理某个流程时,调用
FlowHandleContext.getHandle(processKey)
获取对应处理器 - 使用获取到的处理器实例执行具体的处理逻辑
方式二: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]}
}