一个Controller网关根据请求参数和版本号调用分发多个Service和方法
一个Controller网关根据请求参数和版本号分发Service
公司原有项目就是根据请求参数进行分发逻辑的,这次想着通过反射加入了版本号的分发,减轻各种版本的业务代码逻辑耦合度。
在一个项目中需要写很多的controller去调用不同的service,而写一个网关可以省去写controller层的痛苦,其实就是策略模式的体现。
下面开始介绍根据版本号version和请求参数分发不同service的逻辑。
1.获取bean
因为service在项目启动时就已全部注入到spring容器中,所以我们需要写一个工具类,可以从spring上下文(applicationContext)中获取到对应service
@Component
public class SpringUtil implements ApplicationContextAware {
@Autowired
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (SpringUtil.applicationContext == null) {
SpringUtil.applicationContext = applicationContext;
}
}
//获取applicationContext
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
//通过name获取 Bean.
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
//通过class获取Bean.
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
//通过name,以及Clazz返回指定的Bean
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
}
2.基准Service接口
接下来的具体业务的sevice只需要实现这个接口,CommonResponse为公共响应参数。写这么多个执行方法,是为了以后的版本做准备,我暂时没想到其他的好方法,先使用这种。
public interface RootService {
CommonResponse execute(Integer version, Map<String,Object> requestParams);
default CommonResponse execute1(Integer version, Map<String,Object> requestParams){
return new CommonResponse();
}
default CommonResponse execute2(Integer version, Map<String,Object> requestParams){
return new CommonResponse();
}
default CommonResponse execute3(Integer version, Map<String,Object> requestParams){
return new CommonResponse();
}
default CommonResponse execute4(Integer version, Map<String,Object> requestParams){
return new CommonResponse();
}
default CommonResponse execute5(Integer version, Map<String,Object> requestParams){
return new CommonResponse();
}
default CommonResponse execute6(Integer version, Map<String,Object> requestParams){
return new CommonResponse();
}
default CommonResponse execute7(Integer version, Object... objects){
return new CommonResponse();
}
default CommonResponse execute8(Integer version, Object... objects){
return new CommonResponse();
}
}
3.版本号控制的注解
这里的思路主要是通过不同的注解,调用一个service里不同的method
注解
/**
* @author xucl
* @version 1.0
* @date 2020/9/6 10:08
* @description 在什么之后
*/
@Target({ElementType.METHOD}) //声明自定义的注解使用在方法上
@Retention(RetentionPolicy.RUNTIME)//注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
@Documented
public @interface VersionAfter {
/**
* 版本号
* @return
*/
int no() default 0;
/**
* 都满足大于此版本时的执行顺序 0优先级最高
* @return
*/
int order() default 0;
/**
* 大于此版本
* @return
*/
CompareEnum action() default CompareEnum.AFTER;
}
/**
* @author xucl
* @version 1.0
* @date 2020/9/6 10:15
* @description 在什么之前
*/
@Target({ElementType.METHOD}) //声明自定义的注解使用在方法上
@Retention(RetentionPolicy.RUNTIME)//注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
@Documented
public @interface VersionBefore {
/**
* 版本号
* @return
*/
int no() default 0;
/**
* 都满足小于此版本时的执行顺序 0优先级最高
* @return
*/
int order() default 0;
/**
* 小于此版本
* @return
*/
CompareEnum action() default CompareEnum.BEFORE;
}
/**
* @author xucl
* @version 1.0
* @date 2020/9/6 12:58
* @description 两者之间,包含
*/
@Target({ElementType.METHOD}) //声明自定义的注解使用在方法上
@Retention(RetentionPolicy.RUNTIME)//注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
@Documented
public @interface VersionBetween {
/**
* 版本号1
* @return
*/
int no() default 0;
/**
* 版本号2
* @return
*/
int no2() default 0;
/**
* 都满足小于此版本时的执行顺序 0优先级最高
* @return
*/
int order() default 0;
/**
* 小于此版本
* @return
*/
CompareEnum action() default CompareEnum.BETWEEN;
}
/**
* @author xucl
* @version 1.0
* @date 2020/9/6 10:19
* @description
*/
@Target({ElementType.METHOD}) //声明自定义的注解使用在方法上
@Retention(RetentionPolicy.RUNTIME)//注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
@Documented
public @interface VersionEqual {
/**
* 版本号
* @return
*/
int no() default 0;
/**
* 都满足等于此版本时的执行顺序 0优先级最高
* @return
*/
int order() default 0;
/**
* 等于此版本
* @return
*/
CompareEnum action() default CompareEnum.EQUAL;
}
@Target({ElementType.TYPE}) //声明自定义的注解使用在类上
@Retention(RetentionPolicy.RUNTIME)//注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
@Documented
public @interface VersionController {
}
常量
public enum CompareEnum {
AFTER(1,">"),
BEFORE(0,"<"),
EQUAL(2,"="),
BETWEEN(3,"no<=,<=no2"),
;
private int id;
private String value;
CompareEnum(int id, String value) {
this.id = id;
this.value = value;
}
}
4.网关
/**
* @author xucl
* @version 1.0
* @date 2020/9/6 9:52
* @description 网关
*/
@Slf4j
@Controller
@RequestMapping("/A")
public class GetewayController {
@ResponseBody
@RequestMapping(value = "/Android",produces = {"application/json;charset=UTF-8"},method = RequestMethod.POST)
public Object rootAndroid(@RequestBody Map<String,Object> map, @RequestParam Map<String,Object> params,
HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse){
String service = (String) map.get("service");
Integer version = (Integer)map.get("version");
String serviceBean = service.concat("Service");
RootService rootService = (RootService) SpringUtil.getBean(serviceBean);
if (null != version){
Object o = gateWay(map, rootService.getClass(), rootService,version);
if (null != o){
return o;
}
}
return rootService.execute(version,map);
}
@ResponseBody
@RequestMapping(value = "/IOS",produces = {"application/json;charset=UTF-8"},method = RequestMethod.POST)
public Object rootIOS(@RequestBody Map<String,Object> map, @RequestParam Map<String,Object> params,
HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse){
String service = (String) map.get("service");
Integer version = (Integer)map.get("version");
String serviceBean = service.concat("Service");
RootService rootService = (RootService) SpringUtil.getBean(serviceBean);
if (null != version){
long start = System.currentTimeMillis();
Object o = gateWay(map, rootService.getClass(),rootService, version);
long end = System.currentTimeMillis();
log.error("反射执行时间{}ms",(end-start));
if (null != o){
return o;
}
}
return rootService.execute(version,map);
}
/**
* 反射使用版本注解
*
* @param map
* @param clazz
* @param service
* @param version
* @return
*/
public Object gateWay(Map<String,Object> map,Class clazz,RootService service,Integer version) {
//包名且不可忘记,不然扫描全部项目包,包括引用的jar
//Reflections reflections = new Reflections("com.xu.test.service");
// 判断服务方法上是否有version注解
boolean annotationPresent = clazz.isAnnotationPresent(VersionController.class);
if (annotationPresent) {
// 获取这个类的所有方法
List<MethodVersionVO> methodVersionVOS = new ArrayList<>();
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
// 在什么之前 旧
if (method.isAnnotationPresent(VersionBefore.class)) {
VersionBefore versionBefore = method.getAnnotation(VersionBefore.class);
MethodVersionVO vo = new MethodVersionVO(method, versionBefore.order(), versionBefore.action(), versionBefore.no());
methodVersionVOS.add(vo);
}
// 在什么之后 新
if (method.isAnnotationPresent(VersionAfter.class)) {
VersionAfter versionAfter = method.getAnnotation(VersionAfter.class);
MethodVersionVO vo = new MethodVersionVO(method, versionAfter.order(), versionAfter.action(), versionAfter.no());
methodVersionVOS.add(vo);
}
// 相等
if (method.isAnnotationPresent(VersionEqual.class)) {
VersionEqual versionEqual = method.getAnnotation(VersionEqual.class);
MethodVersionVO vo = new MethodVersionVO(method, versionEqual.order(), versionEqual.action(), versionEqual.no());
methodVersionVOS.add(vo);
}
// 两者间,包含
if (method.isAnnotationPresent(VersionBetween.class)) {
VersionBetween versionBetween = method.getAnnotation(VersionBetween.class);
MethodVersionVO vo = new MethodVersionVO(method,versionBetween.order(),versionBetween.action(),versionBetween.no(),
versionBetween.no2());
methodVersionVOS.add(vo);
}
}
if (CollectionUtils.isNotEmpty(methodVersionVOS)) {
// 优先匹配两者间,包含
MethodVersionVO vo0 = methodVersionVOS.stream().sorted(Comparator.comparingInt(MethodVersionVO::getOrder))
.filter(vo -> version <= vo.getVersion2() && version >= vo.getVersion() &&
CompareEnum.BETWEEN.equals(vo.getCompare()))
.findFirst().orElse(null);
if (null != vo0) {
try {
return (CommonResponse) vo0.getMethod().invoke(service, version, map);
} catch (Exception e) {
e.printStackTrace();
}
}
// 匹配相等此version
MethodVersionVO vo2 = methodVersionVOS.stream().sorted(Comparator.comparingInt(MethodVersionVO::getOrder))
.filter(vo -> vo.getVersion() == version && CompareEnum.EQUAL.equals(vo.getCompare()))
.findFirst().orElse(null);
if (null != vo2) {
try {
return (CommonResponse) vo2.getMethod().invoke(service, version, map);
} catch (Exception e) {
e.printStackTrace();
}
}
// 匹配大于此version
MethodVersionVO vo1 = methodVersionVOS.stream().sorted(Comparator.comparingInt(MethodVersionVO::getOrder))
.filter(vo -> vo.getVersion() > version && CompareEnum.AFTER.equals(vo.getCompare()))
.findFirst().orElse(null);
if (null != vo1) {
try {
return (CommonResponse) vo1.getMethod().invoke(service, version, map);
} catch (Exception e) {
e.printStackTrace();
}
}
// 最后兼容旧版本
MethodVersionVO vo3 = methodVersionVOS.stream().sorted(Comparator.comparingInt(MethodVersionVO::getOrder))
.filter(vo -> vo.getVersion() < version && CompareEnum.BEFORE.equals(vo.getCompare()))
.findFirst().orElse(null);
if (null != vo3) {
try {
return (CommonResponse) vo3.getMethod().invoke(service, version, map);
} catch (Exception e) {
e.printStackTrace();
}
}
}
return null;
}
return null;
}
}
@Getter
@Setter
public class MethodVersionVO {
private Method method;
private int order;
private CompareEnum compare;
private int version;
private int version2;
public MethodVersionVO(Method method, int order, CompareEnum compare, int version) {
this.method = method;
this.order = order;
this.compare = compare;
this.version = version;
}
public MethodVersionVO(Method method, int order, CompareEnum compare, int version, int version2) {
this.method = method;
this.order = order;
this.compare = compare;
this.version = version;
this.version2 = version2;
}
}
5.服务实现
@VersionController
@Service("A110Service")
public class A110Service implements RootService {
@VersionEqual(no = 700,order = 0)
@Override
public CommonResponse execute(Integer version, Map<String, Object> requestParams) {
Map<String,Object> map = new LinkedHashMap<>();
map.put("name","小王");
map.put("age",17);
map.put("action","equal 0");
return CommonResponse.successResponse(map);
}
@VersionEqual(no = 700,order = 1)
@Override
public CommonResponse execute1(Integer version, Map<String, Object> requestParams) {
Map<String,Object> map = new LinkedHashMap<>();
map.put("name","小王");
map.put("age",88);
map.put("action","equal 1");
return CommonResponse.successResponse(map);
}
@VersionAfter(no = 710,order = 1)
@Override
public CommonResponse execute2(Integer version, Map<String, Object> requestParams) {
Map<String,Object> map = new LinkedHashMap<>();
map.put("name","小王");
map.put("age",770);
map.put("action","在什么之后 > 1");
return CommonResponse.successResponse(map);
}
@VersionBefore(no = 710,order = 1)
@Override
public CommonResponse execute3(Integer version, Map<String, Object> requestParams) {
Map<String,Object> map = new LinkedHashMap<>();
map.put("name","小王");
map.put("age",38);
map.put("action","在什么之前 < 1");
return CommonResponse.successResponse(map);
}
@VersionEqual(no = 710,order = 0)
@Override
public CommonResponse execute4(Integer version, Map<String, Object> requestParams) {
Map<String,Object> map = new LinkedHashMap<>();
map.put("name","小王");
map.put("age",48);
map.put("action","equal 0");
return CommonResponse.successResponse(map);
}
@VersionBetween(no = 100,no2 = 690,order = 0)
@Override
public CommonResponse execute5(Integer version, Map<String, Object> requestParams) {
Map<String,Object> map = new LinkedHashMap<>();
map.put("name","小55王");
map.put("age",69);
map.put("action","between 0");
return CommonResponse.successResponse(map);
}
}
6.调用结果

一个Controller网关根据请求参数和版本号调用分发多个Service和方法的更多相关文章
- 拙见--springMVC的controller接受的请求参数
1-这种是最常用的表单参数提交,ContentType指定为application/x-www-form-urlencoded,也就是会进行URL编码. 1.1-对象类型实体Bean接收请求参数(表单 ...
- Spring 请求方法的调用原理(Controller)和请求参数的获取的原理
1.请求映射原理 所有的请求都会经过DispatcherServlet这个类,先了解它的继承树 本质还是httpServlet 原理图 测试 request请求携带的参数 从requestMapp ...
- Spring注解之Controller中获取请求参数及验证使用
1.处理request的uri部分的参数:@PathVariable. 2.处理request header部分的参数:@RequestHeader,@CookieValue@RequestHeade ...
- 动态修改HttpServletRequest的Post请求参数
需求场景: 公司对APP调用的后台接口有个公用格式如下,外层包含了一些设备.版本.签名信息,主要的业务参数是在body里,外层信息都是在网关解决,验证签名后,在转发body到后台服务. { " ...
- SpringMVC 获取请求参数
1.获取Request response对象 在SpringMVC的注解开发中,可以选择性的接收Request和Response对象来使用 2.获取request对象请求参数 a.通过request对 ...
- springboot如何在拦截器中拦截post请求参数以及解决文件类型上传问题
我们经常有这样一个场景,比如:在springboot拦截器中想截取post请求的body参数做一些中间处理,或者用到自定义注解,想拦截一些特定post请求的方法的参数,记录一些请求日志. 想到了使 ...
- struts2视频学习笔记 11-12(动态方法调用,接收请求参数)
课时11 动态方法调用 如果Action中存在多个方法时,可以使用!+方法名调用指定方法.(不推荐使用) public String execute(){ setMsg("execute&q ...
- action接收请求参数
一.采用基本类型接收请求参数(get/post)在Action类中定义与请求参数同名的属性,struts2便能接收自动接收请求参数并赋给同名属性. action的代码: public class Pa ...
- Struts2接受请求参数三种常用方法
一 接受请求参数主要有三种:属性驱动 对象驱动 模型驱动<model Driven> 方式1:在Action中接收请求参数不需要使用request对象,在Action中定义与请求参数相同名 ...
- 【SpringMVC】获取请求参数
通过ServletAPI获取 test.html <a th:href="@{/testServletAPI(username='admin',password=123456)}&qu ...
随机推荐
- Git的一些基本用法
本文分享自天翼云开发者社区<Git的一些基本用法>,作者:l****n 基本操作 git branch 查看当前分支 git branch -a 查看所有分支 git pull 更新当前分 ...
- [记录点滴] OpenResty中Redis操作总结
[记录点滴] OpenResty中Redis操作总结 0x00 摘要 本文总结了在OpenResty中的操作,与大家分享,涉及知识点为Openresty, Lua, Redis. 0x01 操作记录 ...
- [记录点滴] 一个解决Lua 随机数生成问题的办法
[记录点滴] 一个解决Lua 随机数生成问题的办法 0x00 摘要 本文是开发中的简略记录,具体涉及知识点有:Lua,随机数. 0x01 背景 Lua语言生成随机数需要用到两个函数: math.ran ...
- mysql之数据连接池
数据库连接池 C3P0: 配置文件 <?xml version="1.0" encoding="UTF-8"?> <c3p0-config&g ...
- win10安装MongoDB 5.0
1.首先去官网下载安装包:https://www.mongodb.com/try?tck=docs_navbar 2.安装过程一路下一步就行,选择complete安装,可以勾选安装Compass工具 ...
- Springboot 3.x 集成Knife4j [踩坑日记]
之前项目用的是SpringBoot2.x 新项目用了SpringBoot3.x版本,引入Knife4j 报错java.lang.TypeNotPresentException: Type javax. ...
- C# List应用 Lambda 表达式
参考链接 : https://blog.csdn.net/wori/article/details/113144580 首先 => 翻译为{ } 然后没有然后 主要基于我工作中常用的几种情况,写 ...
- Processing 使用pixels[]像素数组绘制矩形rect和圆形ellipse
余温 两次绘制了棋盘格,有了一些经验了,顺着学习态势,我们再接再厉,挖一些技巧.这一次要使用pixels[]数组绘制矩形rect和圆形ellipse,也就是代替rect()和ellipse()两个函数 ...
- 一个nginx + vue下二级路径版本化方案
PS: 尽量不要做版本化!尽量不要做版本化!尽量不要做版本化! 过程说明: 1.arg_appver表示读取url上appver参数 2.对appver参数做变量映射得到alias_party1_te ...
- 微信小程序云函数
小程序开发云环境设置 注意事项 每一个云函数都是一个独立的 nodeJS 项目.所以每个云函数下都有 package.json 文件 错误 前端操作数据库 1 指引 2 新建集合 3 新增记录 4 查 ...