背景:策略模式+工厂+自定义注解+Spring管理。 应用于多种多策略业务场景下,无需关注创建策略对象和调用逻辑。
1.自定义策略注解,value为匹配的具体策略,order执行顺序。
/**
* 策略上使用@Strategy标记来实现扫描功能
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IStrategy {
String value();
int order() default 9999999;
}
2.扫描注解,注入具体策略到spring容器。
@Slf4j
public class StrategyBootstrap implements ImportBeanDefinitionRegistrar {
private BeanDefinitionRegistry registry;
/**
* 反射具体策略类,注入到Spring容器
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
getBeanDefinition().forEach(it -> {
Class<?> aClass = null;
try {
aClass = Class.forName(it.getBeanClassName());
} catch (ClassNotFoundException e) {
log.error("Class.forName失败",e);
}
IStrategy annotation = aClass.getAnnotation(IStrategy.class);
registry.registerBeanDefinition(annotation.value(), it);
});
}
/**
* 通过注解获扫描出所有的策略类,并且生成spring的beanDefinition
*/
protected static Set<BeanDefinition> getBeanDefinition(){
Set<BeanDefinition> beanDefinitionSets = new HashSet<>();
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
provider.addIncludeFilter(new AnnotationTypeFilter(IStrategy.class));
Set<String> scanBasePackages = MySoftManifestProvider.getEntityResources();
for (String scanBasePackage : scanBasePackages) {
Set<BeanDefinition> beanDefinitionSet = provider.findCandidateComponents(scanBasePackage);
beanDefinitionSet.forEach(it -> beanDefinitionSets.addAll(beanDefinitionSet));
}
return beanDefinitionSets;
}
}
3.申明策略工厂,隐藏创建策略细节
/**提供StrategyFactory来缓存注册信息,并获取实例执行*/
public interface StrategyFactory<T> {
/**手动注册策略*/
void register(Class<T> strategyClass);
/**获取策略*/
T getStrategy(String name);
/**获取策略名称列表*/
List<String> getStrategies(Class<T> tClass);
}
@Slf4j
public class StrategyFactoryService<T> implements StrategyFactory<T>, ImportBeanDefinitionRegistrar {
private BeanDefinitionRegistry registry;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
this.registry = registry;
}
/**
* 通过注解value, 获取单个策略对象
*/
@Override
public T getStrategy(String name) {
T strategyInstance = (T) SpringContextHolder.getBean(name);
if (strategyInstance == null) throw new IllegalArgumentException("没有对应的类型");
return strategyInstance;
}
/**
* 通过基类获取对应的所有策略注解value
*/
@Override
public List<String> getStrategies(Class<T> tClass) {
@Getter
@Setter
@AllArgsConstructor
class Strategies { int sort;String value;}
List<Strategies> strategies = new ArrayList<>();
for (BeanDefinition beanDefinition : StrategyBootstrap.getBeanDefinition()) {
Class<?> aClass = null;
try {
aClass = Class.forName(beanDefinition.getBeanClassName());
} catch (ClassNotFoundException e) {
log.error("Class.forName失败",e);
}
if(aClass.getSuperclass().equals(tClass) || Arrays.stream(aClass.getInterfaces()).filter(it -> it.equals(tClass)).collect(Collectors.toList()).size() > 0) {
IStrategy annotation = aClass.getAnnotation(IStrategy.class);
strategies.add(new Strategies(annotation.order(), annotation.value()));
}
}
return strategies.stream().sorted(Comparator.comparing(Strategies::getSort)).map(it -> it.getValue()).collect(Collectors.toList());
}
/**
* 手动将策略注册到spring容器
*/
@Override
public void register(Class<T> strategyClass) {
String name = strategyClass.getAnnotation(IStrategy.class).value();
registry.registerBeanDefinition(name, BeanDefinitionBuilder.genericBeanDefinition(strategyClass).getBeanDefinition());
}
}
4.使用示例。(以我最近编写的Excel导入为例)
1.申明Excel执行流程策略接口
/**流程扩展*/
public interface StrategyProcess {
void process(ImportContext importContext);
}
2.实现Excel导入流程
@Slf4j
@IStrategy(value = "验证数据", order =1)
public class StrategyDataValidation implements StrategyProcess {
@Override
public void process(ImportContext importContext) {
//数据验证逻辑
}
}
@Slf4j
@IStrategy(value = "导入数据", order = 2)
public class StrategyDataInit implements StrategyProcess {
@Override
public void process(ImportContext importContext) {
//数据导入逻辑
}
}
3.执行代码
@Autowired
private StrategyFactory<StrategyProcess> strategyFactory;
//getStrategies: 获取导入流程接口的所有策略,返回策略对应的执行value
List<String> strategies = strategyFactory.getStrategies(StrategyProcess.class);
//遍历执行导入流程策略
strategies.forEach(it -> {
//getStrategy: 根据策略执行的value获取具体的执行对象,比如:StrategyDataInit,StrategyDataValidation
StrategyProcess strategy = strategyFactory.getStrategy(it);
//process: 执行策略
strategy.process(importContext);
});
4.执行结果
数据初始化逻辑。。。。。
数据验证逻辑。。。。。