基于Spring实现策略模式
背景:
看多很多策略模式,总结下来实现原理大体都差不多,在这里主要是讲解下自己基于Spring更优雅的实现方案;这个方案主要是看了一些开源rpc和Spring相关源码后的一些思路,所以在此进行总结
首先看下比较常见的策略模式写法
- 一个接口或者抽象类,里面两个方法(一个方法匹配类型,一个可替换的逻辑实现方法)
- 不同策略的差异化实现(就是说,不同策略的实现类)
- 使用策略模式
1.3.1 一个接口,两个方法
public interface IFileStrategy {
//属于哪种文件解析类型
FileTypeResolveEnum gainFileType();
//封装的公用算法(具体的解析方法)
void resolve(Object objectparam);
}
1.3.2 不同策略的差异化实现
A 类型策略具体实现
@Component
public class AFileResolve implements IFileStrategy { @Override
public FileTypeResolveEnum gainFileType() {
return FileTypeResolveEnum.File_A_RESOLVE;
} @Override
public void resolve(Object objectparam) {
logger.info("A 类型解析文件,参数:{}",objectparam);
//A类型解析具体逻辑
}
}
B 类型策略具体实现
@Component
public class BFileResolve implements IFileStrategy { @Override
public FileTypeResolveEnum gainFileType() {
return FileTypeResolveEnum.File_B_RESOLVE;
} @Override
public void resolve(Object objectparam) {
logger.info("B 类型解析文件,参数:{}",objectparam);
//B类型解析具体逻辑
}
}
默认类型策略具体实现
@Component
public class DefaultFileResolve implements IFileStrategy { @Override
public FileTypeResolveEnum gainFileType() {
return FileTypeResolveEnum.File_DEFAULT_RESOLVE;
} @Override
public void resolve(Object objectparam) {
logger.info("默认类型解析文件,参数:{}",objectparam);
//默认类型解析具体逻辑
}
}
1.3.3 使用策略模式
如何使用呢?我们借助spring的生命周期,使用ApplicationContextAware接口,把对用的策略,初始化到map里面。然后对外提供resolveFile方法即可。
/**
*
*/
@Component
public class StrategyUseService implements ApplicationContextAware{ private Map<FileTypeResolveEnum, IFileStrategy> iFileStrategyMap = new ConcurrentHashMap<>(); public void resolveFile(FileTypeResolveEnum fileTypeResolveEnum, Object objectParam) {
IFileStrategy iFileStrategy = iFileStrategyMap.get(fileTypeResolveEnum);
if (iFileStrategy != null) {
iFileStrategy.resolve(objectParam);
}
} //把不同策略放到map
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, IFileStrategy> tmepMap = applicationContext.getBeansOfType(IFileStrategy.class);
tmepMap.values().forEach(strategyService -> iFileStrategyMap.put(strategyService.gainFileType(), strategyService));
}
}
基于Spring服务策略实现
稍微了解过Spring源码都知道,在Spring里面我们定义好的bean被@Autowired修饰后,实际这个bean是被Spring进行了统一管理,当需要调用的时候实际是从Spring工厂里拿到这个bean;所以大致思路就是在如何拿到bean之前注入一个代理类,让代理类根据元数据的一些自定义规则后去组装成一个能从Spring里拿到实际的bean元素;基于以上的思路进行编码如下
注解定义
- 自定义一个@RouteBizService注解(作用可以理解为@Autowired)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RouteBizService {
String serviceName();
}
- 自定义一个@RouteBizParam参数注解,用于给代理类组装实际beanName
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface RouteBizParam {
}
- 定义一个代理类:RouteServiceProxy

/**
*
*/
package com.gitee.adapter.proxy; import org.springframework.context.ApplicationContext; import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; public class RouteServiceProxy<T> implements InvocationHandler{
private String serviceName;
private ApplicationContext context; public RouteServiceProxy(String serviceName, ApplicationContext context) {
this.serviceName = serviceName;
this.context = context;
} @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String routeCode = null;
Annotation[ /* 参数个数索引 */][ /* 注解个数索引 */ ] paramsAnno = method.getParameterAnnotations();
if (paramsAnno != null) {
for (int i = 0; i < paramsAnno.length; i++) {
if (paramsAnno[i].length > 0) {
routeCode = (String) args[i]; // 获取到路由的参数值
break;
}
}
} return method.invoke(context.getBean(genBeanName(routeCode, serviceName)), args);
} /**
*
* @param sellerCode 用于区分是哪个Service 编码
* @param interfaceSimpleName 服务接口
* @return
*/
private String genBeanName(String sellerCode, String interfaceSimpleName) {
return new StringBuilder(sellerCode.toLowerCase()).append(interfaceSimpleName).toString();
} }
- 基于BeanFactoryPostProcessor 定义一个用于扫描 @RouteBizService修饰的实现类,该类的作用是为了注入代理类

package com.gitee.adapter.spring; import com.gitee.adapter.annation.RouteBizService;
import com.gitee.adapter.proxy.RouteServiceProxy;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils; import java.lang.reflect.Proxy; /**
* @Classname BizRouteServiceProcessor
* @Description bean 后置处理器 获取所有bean
* 判断bean字段是否被 {@link com.gitee.adapter.annation.RouteBizService } 注解修饰
*/
public class BizRouteServiceProcessor implements BeanFactoryPostProcessor, ApplicationContextAware { private ApplicationContext applicationContext; @Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanDefinitionName);
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null) {
Class<?> clazz = ClassUtils.resolveClassName(beanClassName, this.getClass().getClassLoader());
ReflectionUtils.doWithFields(clazz, field -> {
RouteBizService routeBizService = AnnotationUtils.getAnnotation(field, RouteBizService.class);
if (routeBizService != null) {
Object bean = applicationContext.getBean(clazz);
field.setAccessible(true);
// 修改为代理对象
ReflectionUtils.setField(field, bean,
Proxy.newProxyInstance(field.getType().getClassLoader(), new Class[] { field.getType() }, new RouteServiceProxy(routeBizService.serviceName(),this.applicationContext)));
}
});
}
}
} @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
测试

环境搭建
- 操作系统:Windows
- 集成开发工具:IntelliJ IDEA 2021
- 项目技术栈:SpringBoot 2.2.11 + JDK 1.8
- 项目依赖管理工具:Maven 4.0.0
项目代码地址
https://gitee.com/kevin_zhan/spring_strategy
作者:DDZ_YYDS
出处:https://www.cnblogs.com/zdd-java/
本文版权归作者和博客园共有,欢迎转载!但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接!
基于Spring实现策略模式的更多相关文章
- 结合Spring实现策略模式
最近系统需要对不同维度的数据进行差异化计算,也就会使用不同算法.为了以后更加容易扩展,结合Spring框架及策略模式对实现架构做了系统设计. 1. 定义策略接口(Strategy): import c ...
- Spring 实现策略模式--自定义注解方式解耦if...else
策略模式 定义 定义一簇算法类,将每个算法分别封装起来,让他们可以互相替换,策略模式可以使算法的变化独立于使用它们的客户端 场景 使用策略模式,可以避免冗长的if-else 或 switch分支判断 ...
- Java程序员的日常—— 基于类的策略模式、List<?>与List、泛型编译警告、同比和环比
早晨起得太早,昨晚睡得太晚,一天都迷迷糊糊的.中午虽然睡了半个小时,可是依然没有缓过来.整个下午都在混沌中....不过今天下载了一款手游--<剑侠情缘>,感觉不错,喜欢这种类型的游戏. 今 ...
- 策略模式、策略模式与Spring的碰撞
策略模式是GoF23种设计模式中比较简单的了,也是常用的设计模式之一,今天我们就来看看策略模式. 实际案例 我工作第三年的时候,重构旅游路线的机票查询模块,旅游路线分为四种情况: 如果A地-B地往返都 ...
- 设计模式学习之策略模式(Strategy,行为型模式)(13)
转载地址:http://www.cnblogs.com/zhili/p/StragetyPattern.html 一.引言 本文要介绍的策略模式也就是对策略进行抽象,策略的意思就是方法,所以也就是对方 ...
- 基于Spring Boot和Spring Cloud实现微服务架构学习
转载自:http://blog.csdn.net/enweitech/article/details/52582918 看了几周Spring相关框架的书籍和官方demo,是时候开始总结下这中间的学习感 ...
- 基于Spring Boot和Spring Cloud实现微服务架构学习--转
原文地址:http://blog.csdn.net/enweitech/article/details/52582918 看了几周spring相关框架的书籍和官方demo,是时候开始总结下这中间的学习 ...
- 基于Spring Boot和Spring Cloud实现微服务架构
官网的技术导读真的描述的很详细,虽然对于我们看英文很费劲,但如果英文不是很差,请选择沉下心去读,你一定能收获好多.我的学习是先从Spring boot开始的,然后接触到微服务架构,当然,这一切最大的启 ...
- 基于Spring Boot+Cloud构建微云架构
前言 首先,最想说的是,当你要学习一套最新的技术时,官网的英文文档是学习的最佳渠道.因为网上流传的多数资料是官网翻译而来,很多描述的重点也都偏向于作者自身碰到的问题,这样就很容易让你理解和操作出现偏差 ...
随机推荐
- Jenkins插件维护
目录 一.简介 二.插件安装 在线安装插件 上传安装插件 从其它jenkins复制插件 配置插件加速器 一.简介 除了在线安装,还可以官网插件下载地址中进行下载安装,如果访问缓慢可以用清华镜像站. 二 ...
- 『忘了再学』Shell基础 — 1、Shell的介绍
目录 1.Shell的由来 2.Shell的两种执行指令方式 3.什么是Shell脚本 4.Shell 是一种脚本语言 1.Shell的由来 我们比较熟悉Windows系统的图形化界面,对于图形界面来 ...
- Frequency函数(Excel函数集团)
此处文章均为本妖原创,供下载.学习.探讨! 文章下载源是Office365国内版1Driver,如有链接问题请联系我. 请勿用于商业!谢谢 下载地址:https://officecommunity-m ...
- CF169A Chores 题解
Content 两兄弟要分担 \(n\) 件家务,第 \(i\) 件家务有一个复杂度 \(h_i\).兄弟俩以一个数 \(x\) 为界.所有满足复杂度 \(>x\) 的家务都给哥哥做,其余的给弟 ...
- java 多线程 发布订阅模式:发布者java.util.concurrent.SubmissionPublisher;订阅者java.util.concurrent.Flow.Subscriber
1,什么是发布订阅模式? 在软件架构中,发布订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者).而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话 ...
- C++之递归遍历数组
倒序输出 源码 void print_arr_desc(int arr[], unsigned int len) { if (len) { std::cout << "a[&qu ...
- 【LeetCode】1021. Remove Outermost Parentheses 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 遍历 日期 题目地址:https://leetcod ...
- 【LeetCode】665. 非递减数列 Non-decreasing Array(Python & C++)
作者: 负雪明烛 id: fuxuemingzhu 公众号:每日算法题 本文关键词:数组,array,非递减,遍历,python,C++ 目录 题目描述 题目大意 解题方法 一.错误代码 二.举例分析 ...
- 1678 lyk与gcd
1678 lyk与gcd 基准时间限制:2 秒 空间限制:131072 KB 这天,lyk又和gcd杠上了.它拥有一个n个数的数列,它想实现两种操作. 1:将 ai 改为b.2:给定一个数i,求所有 ...
- 1122 机器人走方格 V4
1122 机器人走方格 V4 基准时间限制:1 秒 空间限制:131072 KB 四个机器人a b c d,在2 * 2的方格里,一开始四个机器人分别站在4个格子上,每一步机器人可以往临近的一个格子 ...