一个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和方法的更多相关文章

  1. 拙见--springMVC的controller接受的请求参数

    1-这种是最常用的表单参数提交,ContentType指定为application/x-www-form-urlencoded,也就是会进行URL编码. 1.1-对象类型实体Bean接收请求参数(表单 ...

  2. Spring 请求方法的调用原理(Controller)和请求参数的获取的原理

    1.请求映射原理 所有的请求都会经过DispatcherServlet这个类,先了解它的继承树 本质还是httpServlet 原理图 测试 request请求携带的参数 ​ 从requestMapp ...

  3. Spring注解之Controller中获取请求参数及验证使用

    1.处理request的uri部分的参数:@PathVariable. 2.处理request header部分的参数:@RequestHeader,@CookieValue@RequestHeade ...

  4. 动态修改HttpServletRequest的Post请求参数

    需求场景: 公司对APP调用的后台接口有个公用格式如下,外层包含了一些设备.版本.签名信息,主要的业务参数是在body里,外层信息都是在网关解决,验证签名后,在转发body到后台服务. { " ...

  5. SpringMVC 获取请求参数

    1.获取Request response对象 在SpringMVC的注解开发中,可以选择性的接收Request和Response对象来使用 2.获取request对象请求参数 a.通过request对 ...

  6. springboot如何在拦截器中拦截post请求参数以及解决文件类型上传问题

      我们经常有这样一个场景,比如:在springboot拦截器中想截取post请求的body参数做一些中间处理,或者用到自定义注解,想拦截一些特定post请求的方法的参数,记录一些请求日志. 想到了使 ...

  7. struts2视频学习笔记 11-12(动态方法调用,接收请求参数)

    课时11 动态方法调用 如果Action中存在多个方法时,可以使用!+方法名调用指定方法.(不推荐使用) public String execute(){ setMsg("execute&q ...

  8. action接收请求参数

    一.采用基本类型接收请求参数(get/post)在Action类中定义与请求参数同名的属性,struts2便能接收自动接收请求参数并赋给同名属性. action的代码: public class Pa ...

  9. Struts2接受请求参数三种常用方法

    一 接受请求参数主要有三种:属性驱动 对象驱动 模型驱动<model Driven> 方式1:在Action中接收请求参数不需要使用request对象,在Action中定义与请求参数相同名 ...

  10. 【SpringMVC】获取请求参数

    通过ServletAPI获取 test.html <a th:href="@{/testServletAPI(username='admin',password=123456)}&qu ...

随机推荐

  1. AI编程助手带来的洞察和启发——程序员职业的变革

    前言 从chatgpt的横空出世到国内大模型的强势崛起, 从AI只会写诗作画到辅助编程, AI作为新质生产力的重要角色逐渐进入各行各业,为行业带来新的可能性. Cursor.通义灵码这类"A ...

  2. Nmap 概述及端口状态解析

    Nmap 概述及端口状态解析 Nmap概述 Nmap是一款功能强大的网络探测和安全扫描工具,它允许系统管理员和网络安全专家对网络进行详尽的扫描,以获取关于网络主机及其所提供服务的详细信息. Nmap不 ...

  3. Flink 部署和整体架构

    一.Flink运行部署模式和流程 部署模式: 1.Local 本地部署,直接启动进程,适合调试使用 2.Standalone Cluster集群部署,flink自带集群模式 3.On Yarn 计算资 ...

  4. 如何基于DeepSeek开展AI项目

    关注公众号回复1 获取一线.总监.高管<管理秘籍> 书接上文:DeepSeek怎么突然就比肩GPT了? 最近一直在研究DeepSeek,作为应用层的选手,自然不会傻乎乎的想要去了解底层,我 ...

  5. linux配置maven

    1.下载mavenhttps://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/ 中找到相应的版本wget https://mirrors.tun ...

  6. QT5笔记: 21. QStandardItemModel

    QStandardItemModel 存放数据 QItemSelectionModel 选择项模型 例子:本例子中QListView 没有做任何处理,只是拖放至ui文件,设置了布局 mainwindo ...

  7. Typecho自定义右键菜单美化和禁用F12

    右键美化 使用右键美化,请禁用 HoerMouse 鼠标美化插件,否则貌似没效果 Joe主题在后台-外观设置-设置外观-全局设置-自定义<body></body>标签内填入如下 ...

  8. Go实现动态开点线段树

    1.线段树介绍 线段树是一种用于高效处理区间查询和区间更新的数据结构,当我们需要解决一个频繁更新区间值的问题的时候,就可以采用线段树的结构进行解决.线段树的核心思想是将区间分为多个子区间进行管理,越往 ...

  9. Week09_day05(Hbase的介绍和工作原理)

    HBase是一个分布式的.面向列的开源数据库,该技术来源于 Fay Chang 所撰写的Google论文"Bigtable:一个结构化数据的分布式存储系统".就像Bigtable利 ...

  10. ABAQUS 中的一些约定

    目录 自由度notation Axisymmetric elements Activation of degrees of freedom Internal variables in Abaqus/S ...