一个Controller网关根据请求参数和版本号调用分发多个Service和方法
公司原有项目就是根据请求参数进行分发逻辑的,这次想着通过反射加入了版本号的分发,减轻各种版本的业务代码逻辑耦合度。
在一个项目中需要写很多的controller去调用不同的service,而写一个网关可以省去写controller层的痛苦,其实就是策略模式的体现。
下面开始介绍根据版本号version和请求参数分发不同service的逻辑。
PS:好久没写博客了_(:з」∠)_
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);
}
}