SpringBoot 枚举驱动策略注册表
一、枚举驱动注册表版
核心设计思想
策略模式 + 枚举作为注册表 + 自动注入策略实例
新增加密算法:只需要 2 件事
- 新建类
XxxEncryptor extends AbstractEncryptor,实现加密解密逻辑- 在枚举
EncryptTypeEnum增加一个枚举常量,关联这个加密器 Bean
✅ 不需要修改工厂、不需要 if/switch、不需要改动已有业务代码
关键点:
- 枚举不再只存编码描述,持有策略Bean名称 / Class,充当策略注册表
- Spring 容器统一管理所有加密器实例
- 工厂类从Spring上下文按枚举获取对应的策略实现
- 完全开闭原则:对扩展开放,对修改关闭
整体结构
com.xxx.encrypt
├── AbstractEncryptor.java // 策略顶层抽象
├── strategy
│ ├── AesEncryptor.java // 策略实现1
│ ├── RsaEncryptor.java // 策略实现2
│ └── XxxEncryptor.java // 新增策略(后续扩展)
├── EncryptTypeEnum.java // 【枚举注册表】
├── EncryptStrategyFactory.java // 策略工厂,从Spring容器获取实例
└── EncryptService.java // 业务调用入口
1. 抽象策略 AbstractEncryptor
所有加密器统一规范,定义加密/解密接口
public abstract class AbstractEncryptor {
/**
* 加密
*/
public abstract String encrypt(String rawText);
/**
* 解密
*/
public abstract String decrypt(String cipherText);
/**
* 获取当前加密器对应的类型编码,和枚举匹配(可选校验)
*/
public abstract String getTypeCode();
}
实现类上加
@Component,交给Spring管理,单例Bean。
2. 枚举注册表 EncryptTypeEnum(核心)
枚举 = 策略注册表,每一个枚举项对应一个加密算法
字段:typeCode(编码)、desc(描述)、beanClass(策略实现类Class)
import lombok.Getter;
@Getter
public enum EncryptTypeEnum {
AES("AES", "AES对称加密", AesEncryptor.class),
RSA("RSA", "RSA非对称加密", RsaEncryptor.class);
// 新增算法只需要在这里追加一行:
// XXX("XXX", "xxx加密", XxxEncryptor.class);
private final String typeCode;
private final String desc;
/** 绑定策略实现Class,工厂通过Class从Spring拿Bean */
private final Class<? extends AbstractEncryptor> beanClass;
EncryptTypeEnum(String typeCode, String desc, Class<? extends AbstractEncryptor> beanClass) {
this.typeCode = typeCode;
this.desc = desc;
this.beanClass = beanClass;
}
/** 根据编码查找枚举 */
public static EncryptTypeEnum getByCode(String typeCode) {
for (EncryptTypeEnum e : EncryptTypeEnum.values()) {
if (e.getTypeCode().equals(typeCode)) {
return e;
}
}
throw new IllegalArgumentException("不支持的加密类型:" + typeCode);
}
}
3. 策略实现示例:AesEncryptor
import org.springframework.stereotype.Component;
@Component
public class AesEncryptor extends AbstractEncryptor {
@Override
public String encrypt(String rawText) {
// AES加密逻辑
return rawText + "_aes_encrypt";
}
@Override
public String decrypt(String cipherText) {
// AES解密逻辑
return cipherText.replace("_aes_encrypt", "");
}
@Override
public String getTypeCode() {
return EncryptTypeEnum.AES.getTypeCode();
}
}
@Component
public class RsaEncryptor extends AbstractEncryptor {
@Override
public String encrypt(String rawText) {
return rawText + "_rsa_encrypt";
}
@Override
public String decrypt(String cipherText) {
return cipherText.replace("_rsa_encrypt", "");
}
@Override
public String getTypeCode() {
return EncryptTypeEnum.RSA.getTypeCode();
}
}
4. 策略工厂 EncryptStrategyFactory
唯一的获取策略入口,从Spring上下文获取Bean,无任何if/switch
工厂不需要维护策略Map,枚举是注册表,Class是Bean的查找依据
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
@Component
public class EncryptStrategyFactory {
@Autowired
private ApplicationContext applicationContext;
/**
* 根据枚举获取加密器
*/
public AbstractEncryptor getEncryptor(EncryptTypeEnum encryptTypeEnum) {
// 直接通过枚举里保存的Class,从Spring容器拿Bean
return applicationContext.getBean(encryptTypeEnum.getBeanClass());
}
/**
* 根据typeCode字符串获取加密器(业务常用)
*/
public AbstractEncryptor getEncryptor(String typeCode) {
EncryptTypeEnum typeEnum = EncryptTypeEnum.getByCode(typeCode);
return getEncryptor(typeEnum);
}
}
原理:Spring容器已经实例化所有
@Component的加密器实现;枚举保存Class,工厂只是做查找转发。
5. 业务服务 EncryptService
业务层调用示例
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class EncryptService {
@Autowired
private EncryptStrategyFactory encryptStrategyFactory;
public String encrypt(String typeCode, String content) {
AbstractEncryptor encryptor = encryptStrategyFactory.getEncryptor(typeCode);
return encryptor.encrypt(content);
}
public String decrypt(String typeCode, String content) {
AbstractEncryptor encryptor = encryptStrategyFactory.getEncryptor(typeCode);
return encryptor.decrypt(content);
}
}
新增算法完整演示(只需要两步)
需求:新增SM4国密加密
- 新建实现类
@Component
public class Sm4Encryptor extends AbstractEncryptor {
@Override
public String encrypt(String rawText) {
return rawText + "_sm4_encrypt";
}
@Override
public String decrypt(String cipherText) {
return cipherText.replace("_sm4_encrypt", "");
}
@Override
public String getTypeCode() {
return EncryptTypeEnum.SM4.getTypeCode();
}
}
- 在枚举
EncryptTypeEnum添加一行常量
SM4("SM4", "SM4国密加密", Sm4Encryptor.class);
到此完成!工厂、Service、原有逻辑一行不改,直接可以使用:
encryptService.encrypt("SM4", "test");
调用方式
// 方式1:直接用枚举
AbstractEncryptor encryptor = factory.getEncryptor(EncryptTypeEnum.SM4);
String cipher = encryptor.encrypt("hello");
// 方式2:业务传字符串编码(http参数、数据库存储类型编码场景)
String cipher = encryptService.encrypt("SM4", "hello");
可选增强方案(生产常用)
方案A:启动自检(推荐)
项目启动时校验枚举中所有Class是否在Spring容器存在,防止漏写@Component:
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class EncryptCheckRunner implements CommandLineRunner {
@Autowired
private ApplicationContext ctx;
@Override
public void run(String... args) throws Exception {
for (EncryptTypeEnum e : EncryptTypeEnum.values()) {
// 启动时校验Bean存在
ctx.getBean(e.getBeanClass());
}
}
}
启动直接报错,提前发现问题,而不是运行时才抛异常。
方案B:枚举保存Bean名称(备选)
如果同一个类有多个Bean实例,不存Class,存beanName字符串:
SM4("SM4", "SM4国密加密", "sm4Encryptor");
工厂:ctx.getBean(beanName, AbstractEncryptor.class)
方案C:注册Map缓存(减少ApplicationContext反复查找)
工厂内部做一个本地Map<Class<?>, AbstractEncryptor>缓存,容器启动一次性加载,减少getBean开销。
@Component
public class EncryptStrategyFactory {
private final Map<Class<? extends AbstractEncryptor>, AbstractEncryptor> strategyMap;
@Autowired
public EncryptStrategyFactory(ApplicationContext ctx) {
Map<String, AbstractEncryptor> beans = ctx.getBeansOfType(AbstractEncryptor.class);
strategyMap = beans.values().stream()
.collect(Collectors.toMap(AbstractEncryptor::getClass, v -> v));
}
public AbstractEncryptor getEncryptor(EncryptTypeEnum type) {
return strategyMap.get(type.getBeanClass());
}
}
优缺点分析
✅ 优点
- 开闭原则:新增策略只加实现类+枚举常量,工厂无改动
- 消灭大量 if/else / switch,代码干净
- 枚举集中管理所有算法清单,文档化,一眼看到全部支持类型
- Spring统一管理Bean,依赖注入(加密器内部可以注入配置、redis等)
- 类型安全,编码和实现强绑定,不容易传错类型
⚠️ 缺点&坑点
- 枚举编译期固定:不能动态新增算法(运行时不重启新增算法不行,适合算法集合固定场景)>
如果需要运行时动态注册策略,这个方案不适用,要改用内存Map注册表。
- 枚举和实现类存在双向绑定,需要启动自检,防止漏写@Component
- 不能直接使用抽象类,所有实现必须交给Spring容器实例化,不能new
适用场景
- 算法集合预先确定、不会运行时动态新增
- 加解密、解析器、消息处理器、报表导出器、支付渠道处理器等>
比如:文件解析策略(excel/csv/pdf)、消息发送策略(短信/邮件/钉钉)都可以套用这套模板
二、注解驱动注册器版
不用手动改枚举,用注解自动扫描注册
改造目标:注解驱动策略注册器(不再手动维护枚举!)
原始方案痛点:新增策略必须手动修改枚举,容易忘记、枚举和实现类耦合。
改造后:自定义注解标记策略实现类 → Spring启动自动扫描所有带注解的加密器 → 自动构建注册表
✅ 新增算法:只需要写XxxEncryptor extends AbstractEncryptor+ 在类上加@EncryptStrategy(code = "SM4", desc = "国密SM4加密")
✅ 不用修改任何枚举、不用修改工厂、不用改注册表
核心变化:
- 去掉手动维护的
EncryptTypeEnum;- 新增注解
@EncryptStrategy,用来定义策略编码、描述;- 策略注册器
EncryptStrategyRegistry:Spring启动时扫描所有实现AbstractEncryptor且带注解的Bean,构建内存注册表;- 工厂直接从注册器获取策略实例;
- 保留原有抽象策略,业务调用方式几乎不变。
整体包结构
com.xxx.encrypt
├── AbstractEncryptor.java // 策略抽象父类(不变)
├── annotation
│ └── EncryptStrategy.java // 自定义策略注解
├── registry
│ └── EncryptStrategyRegistry.java // 【核心】策略注册器,自动扫描、维护注册表
├── EncryptStrategyFactory.java // 策略工厂,对外获取策略
├── EncryptService.java // 业务入口
└── strategy
├── AesEncryptor.java
├── RsaEncryptor.java
└── Sm4Encryptor.java(新增只需要写这个+加注解)
1. 自定义注解 @EncryptStrategy
标记策略实现类,定义策略唯一编码、描述
package com.xxx.encrypt.annotation;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EncryptStrategy {
/** 策略唯一编码,业务用来选择算法,全局不能重复 */
String code();
/** 策略描述 */
String desc() default "";
}
2. 抽象策略 AbstractEncryptor(基本不变,删掉getTypeCode可选)
package com.xxx.encrypt;
public abstract class AbstractEncryptor {
public abstract String encrypt(String rawText);
public abstract String decrypt(String cipherText);
}
3. 策略实现示例(加注解 + @Component)
AesEncryptor
package com.xxx.encrypt.strategy;
import com.xxx.encrypt.AbstractEncryptor;
import com.xxx.encrypt.annotation.EncryptStrategy;
import org.springframework.stereotype.Component;
@Component
@EncryptStrategy(code = "AES", desc = "AES对称加密")
public class AesEncryptor extends AbstractEncryptor {
@Override
public String encrypt(String rawText) {
return rawText + "_aes_encrypt";
}
@Override
public String decrypt(String cipherText) {
return cipherText.replace("_aes_encrypt", "");
}
}
RsaEncryptor
@Component
@EncryptStrategy(code = "RSA", desc = "RSA非对称加密")
public class RsaEncryptor extends AbstractEncryptor {
@Override
public String encrypt(String rawText) {
return rawText + "_rsa_encrypt";
}
@Override
public String decrypt(String cipherText) {
return cipherText.replace("_rsa_encrypt", "");
}
}
✅ 新增SM4示例(只写这个类+注解即可,无其他改动)
@Component
@EncryptStrategy(code = "SM4", desc = "国密SM4加密")
public class Sm4Encryptor extends AbstractEncryptor {
@Override
public String encrypt(String rawText) {
return rawText + "_sm4_encrypt";
}
@Override
public String decrypt(String cipherText) {
return cipherText.replace("_sm4_encrypt", "");
}
}
4. 策略注册器 EncryptStrategyRegistry【核心】
Spring容器初始化完成后,自动收集所有加密策略,内存维护注册表。
启动时自动校验:编码不能重复,重复直接启动失败,提前发现冲突。
package com.xxx.encrypt.registry;
import com.xxx.encrypt.AbstractEncryptor;
import com.xxx.encrypt.annotation.EncryptStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Component
public class EncryptStrategyRegistry {
/** key:策略code,value:加密器实例 */
private final Map<String, AbstractEncryptor> strategyMap = new ConcurrentHashMap<>();
@Autowired
private Map<String, AbstractEncryptor> allEncryptorBeans;
@PostConstruct
public void initRegistry() {
// 遍历所有AbstractEncryptor类型Bean
for (AbstractEncryptor encryptor : allEncryptorBeans.values()) {
Class<?> clazz = encryptor.getClass();
EncryptStrategy annotation = clazz.getAnnotation(EncryptStrategy.class);
if (annotation == null) {
// 可选:如果有抽象父类/临时实现类不加注解,直接跳过
log.warn("加密器 {} 未标记@EncryptStrategy注解,跳过注册", clazz.getName());
continue;
}
String code = annotation.code();
if (strategyMap.containsKey(code)) {
throw new IllegalArgumentException("策略编码【" + code + "】重复!类:" + clazz.getName());
}
strategyMap.put(code, encryptor);
log.info("注册加密策略 code={}, desc={}, class={}", code, annotation.desc(), clazz.getSimpleName());
}
}
/** 根据编码获取策略 */
public AbstractEncryptor getStrategy(String code) {
AbstractEncryptor encryptor = strategyMap.get(code);
if (encryptor == null) {
throw new IllegalArgumentException("不存在的加密策略:" + code);
}
return encryptor;
}
/** 获取全部策略编码清单,用于接口返回下拉列表 */
public Map<String, String> getAllStrategyDesc() {
return strategyMap.entrySet().stream()
.collect(ConcurrentHashMap::new, (map, entry) -> {
Class<?> clazz = entry.getValue().getClass();
EncryptStrategy anno = clazz.getAnnotation(EncryptStrategy.class);
map.put(entry.getKey(), anno.desc());
}, ConcurrentHashMap::putAll);
}
}
原理:
Map<String, AbstractEncryptor> allEncryptorBeans是Spring自动注入,key是beanName,value是所有实现AbstractEncryptor的Bean。@PostConstruct在Bean创建完成后一次性扫描注册。
5. 策略工厂 EncryptStrategyFactory(轻量化包装,对外统一入口)
工厂可以很薄,直接委托注册器,业务层依然面向工厂调用,方便后续扩展拦截、埋点。
package com.xxx.encrypt;
import com.xxx.encrypt.registry.EncryptStrategyRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class EncryptStrategyFactory {
@Autowired
private EncryptStrategyRegistry strategyRegistry;
public AbstractEncryptor getEncryptor(String code) {
return strategyRegistry.getStrategy(code);
}
}
6. 业务服务 EncryptService
业务调用几乎和老版本完全一致
package com.xxx.encrypt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class EncryptService {
@Autowired
private EncryptStrategyFactory encryptStrategyFactory;
public String encrypt(String typeCode, String content) {
AbstractEncryptor encryptor = encryptStrategyFactory.getEncryptor(typeCode);
return encryptor.encrypt(content);
}
public String decrypt(String typeCode, String content) {
AbstractEncryptor encryptor = encryptStrategyFactory.getEncryptor(typeCode);
return encryptor.decrypt(content);
}
}
7. 业务调用示例
// 加密
String cipher = encryptService.encrypt("SM4", "hello world");
// 解密
String raw = encryptService.decrypt("SM4", cipher);
// 获取全部策略列表(给前端下拉框)
Map<String, String> allStrategy = registry.getAllStrategyDesc();
✅ 新增策略完整流程
- 新建类
XxxEncryptor extends AbstractEncryptor - 类上加
@Component和@EncryptStrategy(code="XXX", desc="xxx算法") - 重启SpringBoot,自动注册到注册表>
不需要修改工厂、注册表、枚举,零侵入原有代码。
关键特性 & 坑点说明
✅ 优势对比枚举版
- 消除枚举维护成本,新增策略只关心实现类本身;
- 启动阶段自动校验编码唯一性,提前发现冲突;
getAllStrategyDesc()可以直接输出全部支持算法,用于接口文档、前端下拉;- 策略实现类可以自由放在任意包,只要被Spring扫描;
- 策略Bean可以正常使用依赖注入(注入配置、Redis、其他Service)。
⚠️ 坑点
- 必须加 @Component:否则不会被Spring实例化,不会被注入到
allEncryptorBeans; - 注解必须打在实际Bean类上,不能打在父类;如果是CGLIB代理类,注解读取不受影响;
- 编码code全局唯一;启动时校验,运行期不会再变(静态策略集);
- 如果策略类不在Spring扫描包范围,会扫描不到,需要调整
@ComponentScan; - 依然不支持运行时动态新增策略(新增策略还是要写代码+重启服务);如果需要运行时动态注册,可以给注册器开放
register()方法。
🧩 可选增强项
增强1:统一策略元数据对象(StrategyMeta)
把 code、desc、class封装成对象,注册表不再只存实例,同时保存元信息:
@Data
public class StrategyMeta {
private String code;
private String desc;
private Class<?> clazz;
private AbstractEncryptor instance;
}
注册表改为 Map<String, StrategyMeta>,方便统一管理元信息。
增强2:支持SpringBoot Starter自动装配(大型项目)
抽成starter,不需要手动@ComponentScan,自动启用策略注册。
增强3:增加策略优先级、是否启用开关
注解扩展属性:boolean enable() default true,int order() default 0;注册时过滤掉 enable=false 的策略。
@EncryptStrategy(code = "SM4", desc = "国密SM4", enable = true, order = 1)
增强4:AOP埋点,统一统计加密耗时、异常监控
在工厂层加AOP,所有加密解密统一日志、监控,策略实现只关心业务逻辑。
增强5:泛型版本
把 AbstractEncryptor<I,O> 改造为泛型策略,支持入参出参类型不一样场景。
对比两种方案选型建议
- 枚举注册表版:策略集合极少、几乎不新增,希望编译期强类型,代码可读性强;缺点每次新增要改枚举。
- 注解驱动注册器版:策略较多、经常新增,希望遵循插件化开发风格,新增策略对原有代码零改动;缺点运行期才收集元信息。
三、泛型版|注解驱动策略注册表
目标:
- 抽象策略改为泛型
AbstractStrategy<I,O>,入参I、出参O可以不一样 - 沿用注解
@Strategy自动扫描注册,不再绑定加密场景,通用策略框架 - 原有加密场景可以无缝迁移;同时支持任意策略:解析器、转换器、处理器等
- 保留:启动自动扫描、编码重复校验、注册表、工厂模式
示例业务场景:
- 加密策略:
AbstractStrategy<String,String>输入明文String,输出密文String- 文件解析:
AbstractStrategy<MultipartFile, List<RowData>>输入文件,输出行数据列表- 消息推送:
AbstractStrategy<MessageDTO, Boolean>输入消息对象,返回是否发送成功
包结构
com.xxx.strategy
├── annotation
│ └── Strategy.java // 策略注解
├── registry
│ └── StrategyRegistry.java // 策略注册器(核心)
├── AbstractStrategy.java // ✅ 泛型抽象策略
├── StrategyFactory.java // 策略工厂
├── StrategyService.java // 业务调用门面
└── impl
├── AesEncryptStrategy.java
├── Sm4EncryptStrategy.java
└── FileParseStrategy.java // 不同泛型示例
1. 自定义注解 @Strategy
package com.xxx.strategy.annotation;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Strategy {
/** 策略唯一编码 */
String code();
/** 策略描述 */
String desc() default "";
/** 是否启用 */
boolean enable() default true;
}
2. 泛型抽象父类 AbstractStrategy<I,O>
I:输入类型;O:输出类型
package com.xxx.strategy;
public interface AbstractStrategy<I, O> {
/**
* 策略执行方法
* @param input 入参
* @return 输出
*/
O execute(I input);
}
说明:这里改用接口而不是抽象类,泛型接口在Spring里注入
Map<String, AbstractStrategy<?,?>>更顺滑;如果你坚持用abstract class,我也可以切换回去。
如果需要异常统一封装,可以加默认方法:
public interface AbstractStrategy<I, O> {
default O execute(I input) {
try {
return doExecute(input);
} catch (Exception e) {
throw new StrategyExecuteException("策略执行异常", e);
}
}
O doExecute(I input);
}
配套自定义异常
StrategyExecuteException,方便上层捕获。
3. 策略元数据 StrategyMeta(保存泛型信息 + 实例)
关键:因为Java泛型擦除,运行时拿不到接口泛型I/O,两种方案:
方案A(推荐,简单):注解不存泛型,使用者调用时自己保证类型匹配;类型不匹配抛类型转换异常。适合大多数业务。
方案B(增强):在注解增加inputClass()、outputClass()属性,显式声明入参出参Class,注册时校验类型安全。下面直接用方案B,强类型校验。
StrategyMeta.java
package com.xxx.strategy.registry;
import com.xxx.strategy.AbstractStrategy;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class StrategyMeta {
/** 策略编码 */
private String code;
/** 描述 */
private String desc;
/** 是否启用 */
private boolean enable;
/** 入参类型 */
private Class<?> inputClass;
/** 出参类型 */
private Class<?> outputClass;
/** 策略实例Bean */
private AbstractStrategy<?, ?> instance;
}
修改注解 @Strategy,增加入参出参Class
package com.xxx.strategy.annotation;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Strategy {
String code();
String desc() default "";
boolean enable() default true;
/** 策略入参Class */
Class<?> inputClass();
/** 策略出参Class */
Class<?> outputClass();
}
4. 策略实现示例
示例1:AES加密策略 I=String, O=String
package com.xxx.strategy.impl;
import com.xxx.strategy.AbstractStrategy;
import com.xxx.strategy.annotation.Strategy;
import org.springframework.stereotype.Component;
@Component
@Strategy(
code = "AES",
desc = "AES字符串加密",
inputClass = String.class,
outputClass = String.class
)
public class AesEncryptStrategy implements AbstractStrategy<String, String> {
@Override
public String execute(String input) {
// 加密逻辑
return input + "_aes_encrypted";
}
}
示例2:文件解析策略 I=MultipartFile, O=List
package com.xxx.strategy.impl;
import com.xxx.strategy.AbstractStrategy;
import com.xxx.strategy.annotation.Strategy;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
@Component
@Strategy(
code = "CSV_PARSE",
desc = "CSV文件解析",
inputClass = MultipartFile.class,
outputClass = List.class
)
public class CsvParseStrategy implements AbstractStrategy<MultipartFile, List<String>> {
@Override
public List<String> execute(MultipartFile file) {
// 解析文件返回行列表
return List.of("line1","line2");
}
}
5. 策略注册器 StrategyRegistry【核心】
Spring启动后收集所有策略,构建 Map<String, StrategyMeta>,校验编码重复、启用标记。
package com.xxx.strategy.registry;
import com.xxx.strategy.AbstractStrategy;
import com.xxx.strategy.annotation.Strategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Component
public class StrategyRegistry {
private final Map<String, StrategyMeta> metaMap = new ConcurrentHashMap<>();
// Spring自动注入所有实现AbstractStrategy的Bean
@Autowired
private Map<String, AbstractStrategy<?, ?>> allStrategyBeans;
@PostConstruct
public void init() {
for (AbstractStrategy<?, ?> strategy : allStrategyBeans.values()) {
Class<?> clazz = strategy.getClass();
Strategy anno = clazz.getAnnotation(Strategy.class);
if (anno == null) {
log.warn("策略类 {} 缺少@Strategy注解,跳过注册", clazz.getName());
continue;
}
if (!anno.enable()) {
log.info("策略 {} 已禁用,跳过注册", anno.code());
continue;
}
String code = anno.code();
if (metaMap.containsKey(code)) {
throw new IllegalArgumentException("策略编码【" + code + "】重复冲突!类:" + clazz.getName());
}
StrategyMeta meta = StrategyMeta.builder()
.code(code)
.desc(anno.desc())
.enable(anno.enable())
.inputClass(anno.inputClass())
.outputClass(anno.outputClass())
.instance(strategy)
.build();
metaMap.put(code, meta);
log.info("注册策略 code={}, desc={}, input={}, output={}, class={}",
code, anno.desc(), anno.inputClass().getSimpleName(), anno.outputClass().getSimpleName(), clazz.getSimpleName());
}
}
/** 获取策略元信息 */
public StrategyMeta getMeta(String code) {
StrategyMeta meta = metaMap.get(code);
if (meta == null) {
throw new IllegalArgumentException("不存在策略编码:" + code);
}
return meta;
}
/** 获取策略实例,不做类型转换 */
@SuppressWarnings("unchecked")
public <I,O> AbstractStrategy<I,O> getStrategy(String code) {
StrategyMeta meta = getMeta(code);
return (AbstractStrategy<I,O>) meta.getInstance();
}
/** 获取全部策略元信息,用于前端下拉、文档 */
public Map<String, StrategyMeta> getAllMeta(){
return metaMap;
}
}
6. 策略工厂 StrategyFactory
package com.xxx.strategy;
import com.xxx.strategy.registry.StrategyRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class StrategyFactory {
@Autowired
private StrategyRegistry strategyRegistry;
public <I,O> AbstractStrategy<I,O> getStrategy(String code){
return strategyRegistry.getStrategy(code);
}
}
7. 业务门面 StrategyService
package com.xxx.strategy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class StrategyService {
@Autowired
private StrategyFactory strategyFactory;
public <I,O> O execute(String code, I input){
AbstractStrategy<I,O> strategy = strategyFactory.getStrategy(code);
return strategy.execute(input);
}
}
8. 业务调用示例
@Autowired
private StrategyService strategyService;
// 1. 字符串加密策略 String -> String
String cipher = strategyService.execute("AES", "hello world");
// 2. 文件解析策略 MultipartFile -> List<String>
MultipartFile file = ...;
List<String> rows = strategyService.execute("CSV_PARSE", file);
✅ 新增策略只需要两步
- 新建类
XxxStrategy implements AbstractStrategy<I,O>,实现 execute - 添加
@Component+@Strategy(code="XXX", desc="xxx", inputClass=xxx.class, outputClass=xxx.class)>
无需修改工厂、注册表,无需改任何已有代码
类型安全 & 坑点说明
- 泛型擦除问题
Java运行期无法从实现类直接拿到泛型参数<I,O>,所以通过注解显式声明inputClass / outputClass。>
可选增强:注册时增加类型校验方法:调用时校验入参实例是否匹配meta.inputClass,不匹配直接抛异常。
// 在StrategyRegistry增加校验
public <I,O> AbstractStrategy<I,O> getStrategy(String code, I input){
StrategyMeta meta = getMeta(code);
if(!meta.getInputClass().isInstance(input)){
throw new IllegalArgumentException("策略【"+code+"】入参类型不匹配,预期:"+meta.getInputClass().getName());
}
return (AbstractStrategy<I,O>) meta.getInstance();
}
- 类型转换警告
@SuppressWarnings("unchecked")
属于可控的类型转换,只要业务传参和注解声明保持一致就安全;可以封装在注册器内部,不扩散到业务层。 - 支持不同策略完全不同I/O,非常适合插件化处理器:
- 支付处理器:
AbstractStrategy<PayRequestDTO, PayResultDTO> - 报表导出:
AbstractStrategy<ReportQuery, byte[]> - 消息发送:
AbstractStrategy<MsgDTO, SendResult>
- 支付处理器:
可选增强:工具方法,统一类型校验
// StrategyService
public <I,O> O executeWithTypeCheck(String code, I input){
StrategyMeta meta = strategyRegistry.getMeta(code);
if (!meta.getInputClass().isInstance(input)) {
throw new IllegalArgumentException(
String.format("策略[%s]入参类型错误,预期:%s,实际:%s",
code, meta.getInputClass().getName(), input.getClass().getName()));
}
AbstractStrategy<I,O> strategy = strategyRegistry.getStrategy(code);
return strategy.execute(input);
}
可选:换回抽象类版本
如果你想继续使用 abstract class AbstractStrategy<I,O>,只需要把 interface 改成抽象类,其余注册逻辑不变。
扩展:统一异常
public class StrategyExecuteException extends RuntimeException{
public StrategyExecuteException(String message, Throwable cause) {
super(message, cause);
}
}
对比上一版加密专用策略
- 之前:固定入参出参都是String,加密场景专用
- 当前泛型版:通用策略框架,任意I/O类型;一套框架支撑多种业务策略
- 依然保留:注解驱动、自动扫描、启动校验、插件式新增策略

浙公网安备 33010602011771号