优点
- 不用考虑业务多或者同一个业务中创建很多类,导致后面的人一时看不懂
- 可扩展性强
- 可通过注解命名来约束多级路由,一个类即可管理一个大的业务接口
疑点
- 至于控制层中的method参数是怎么实现多级路由的,可看我的上一篇(SpringBoot实现过滤器更改请求地址)
- 比如:http://应用服务地址:8080/api/test/xxx/sss/ddssa/fsa/d表示d是方法名而xxxSssDdssaFsa(驼峰命名)可表示一个大的业务类的名称,最终更改请求路径:http://应用服务地址:8080/api/test/xxx$sss$ddssa$fsa$d
- 将xxx$sss$ddssa$fsa$d变成一个字符串(也就是method参数),具体怎么改路径,从哪开始截取看业务场合需要
控制层
@PostMapping("{method}") public Object gateway(@PathVariable String method, HttpServletRequest request) { JSONObject reqParamJSON = OpenApiUtil.getReqParamJSON(request); Object o; try { if (gatewayService.isSupport(业务应用)) { o = gatewayService.execute(method, reqParamJSON); } else { throw BizException.FORMAT_PARAMETER_ERROR; } } catch (Exception e) { } return o; }
本文重点讲解execute方法,实现思想,一些基础入口代码怎么实现都行
接口层
public interface IApiGatewayService { /** * 前置校验 */ boolean isSupport(AcApp app); /** * 业务逻辑执行方法 */ Object execute(String method, JSONObject jsonObject) throws Exception; }
前置校验方法可选择不看,执行方法里有异常抛出、入参的json、返回的实体对象
核心代码
- 继承该抽象类
- 编写自己的业务逻辑方法(方法名需要跟路由的最后名称相同)
- 如果出现多级路由,加上@Service(name="xxxSssDdssaFsa")
public abstract class AbstractApiGatewayService implements IApiGatewayService { protected Logger log = LoggerFactory.getLogger(this.getClass()); @Override public boolean isSupport(AcApp app) { return true; } @Override public Object execute(String method, JSONObject jsonObject) throws Exception { Pair<Class<?>, String> ret = findClass(method); Class<?> c = ret.getLeft(); String newMethod = ret.getRight(); Method methods = c.getDeclaredMethod(newMethod, JSONObject.class); methods.setAccessible(true); return methods.invoke(SpringContextHolder.getBean(c), jsonObject); } /** * 根据路由名称获取对应的实现类 */ private Pair<Class<?>, String> findClass(String method) { List<Class<?>> ret = ListUtil.newArrayList(); Map<String, IApiGatewayService> beans = SpringContextHolder.getBeansOfType(IApiGatewayService.class); List<IApiGatewayService> serviceList = ListUtil.newArrayList(beans.values()); // 查找方法名称 String selectMethod = method; // 是否存在多级路由 if (CharSequenceUtil.contains(method, StringPool.DOLLAR)) { String[] split = method.split("\\" + StringPool.DOLLAR); List<String> methodAllList = ListUtil.newArrayList(Arrays.asList(split)); selectMethod = ListUtil.getLast(methodAllList); methodAllList.remove(methodAllList.size() - 1); // 得到beanName(驼峰命名),不连续名称不进行转换,比如:(多个)user,name => userName | (单个)user => user String beanName = CharSequenceUtil.toCamelCase(String.join(StringPool.UNDERSCORE, methodAllList)); // 过滤 serviceList = serviceList.stream() .filter(item -> { Class<? extends IApiGatewayService> aClass = item.getClass(); Service annotation = aClass.getAnnotation(Service.class); return null != annotation && CharSequenceUtil.isNotBlank(annotation.value()) && CharSequenceUtil.equals(annotation.value(), beanName); }) .collect(Collectors.toList()); } String finalSelectMethod = selectMethod; serviceList.forEach(item -> { Method[] methods = item.getClass().getDeclaredMethods(); for (Method items : methods) { if (CharSequenceUtil.equals(items.getName(), finalSelectMethod)) { ret.add(items.getDeclaringClass()); } } }); return Pair.of(ListUtil.getFirst(ret), finalSelectMethod); } }
前提是服务层的类都需要注入到Spring容器中,不然通过注解找不到类,就会报错
服务层
@Service @Primary public class ApiCommonServiceImpl extends AbstractApiGatewayService { private Object count(JSONObject reqParamJSON) { return 66666; } }
表示请求路径:http://应用服务地址:8080/api/test/count
如果多个业务方法方法名称保持跟路径最后相同即可(解决了策略模式需要建很多类)