一个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 ...
随机推荐
- VPC终端节点的实现架构和原理
本文分享自天翼云开发者社区<VPC终端节点的实现架构和原理>,作者:云云生息 什么是VPC终端节点? 在传统的VPC架构中,为了使VPC内的资源能够与云服务提供商的各种服务进行通信,通常需 ...
- golang轻量级版本管理工具g安装使用
使用 g 可以在 windows 上切换使用不同版本的 golang GitHub仓库地址 https://github.com/voidint/g GitHub下载连接 https://github ...
- centos7关闭不必要的服务和端口
云服务器开启很多不必要的端口,造成安全隐患 TCP 25端口:邮件服务 systemctl stop postfix 53端口 :域名解析服务 systemctl stop rhel-domainna ...
- FLink参数pipeline.operator-chaining介绍
1.当使用flink提交一个任务,没有给算子设置并行度情况下,默认所有算子会chain在一起,整个DAG图只会显示一个算子,虽然有利于数据传输,提高程序性能,但是无法看到数据的输入和疏忽,业绩反压相关 ...
- C# 获取计算机唯一标识
C# 获取计算机唯一标识 原文链接 private static string _sFingerPrint { get; set; } /// <summary> /// 计算机唯一标识 ...
- 【收藏】default.rdp配置
原文链接: https://www.cnblogs.com/vman/archive/2011/12/05/2276895.html 存储在 Default.rdp 文件中的设置 默认情况下,将在& ...
- JUC并发—7.AQS源码分析三
大纲 1.等待多线程完成的CountDownLatch介绍 2.CountDownLatch.await()方法源码 3.CountDownLatch.coutDown()方法源码 4.CountDo ...
- 牛客题解 | 单组_spj判断YES与NO
题目 题目链接 解题思路 后台有spj代码,能对同学们的输出数据进行校验,符合条件即可通过. 附赠 spj 代码 #include <iostream> #include <fstr ...
- [JOISC2019] 聚会 题解
随机化好题,但是不会证. 考虑把树看成一条链,链的每个点上缀了一棵树. 那么先随机出两个点 \(x,y\)(实际上随机一个点,另一个点固定似乎更好?),然后对于当前这棵树上的任意点 \(z\),都让他 ...
- 使用FishSpeech进行语音合成推理
部署 部署FishSpeech,优先参考github官方(https://speech.fish.audio/zh/). 注意:此网站可能需要FQ才能访问. 个人为Windows电脑,使用Wind ...