前言
关于自己写一个自定义的servlet的初衷,首先由于使用springboot写controller接口时需要使用许多的注解例如
@GetMapping("/add")
,@RequestMapping("/user")
,@RequestBody
还需要想很多接口uri,有的时候还遇见接口uri重复,忘记写接口参数的注解`@RequestParam(value = “name”),结合在之前公司使用改造的webx的miniwebx的基础上我重新将它撸了一遍以及改造。
特点
约定大于配置,弱化配置,按照约定简单使用
解决使用接口uri地址注解的问题:按照约定配置获取controller层在容器中的bean,获取该bean中的公共的(public)方法,生成指定的接口uri,将uri作为map的key,而value则是MvcUriMethod对象缓存起来。
public class MvcUriMethod { private String uri; private Object handleObject; private Method handleMethod; public MvcUriMethod(String uri, Object handleObject, Method handleMethod) { super(); if (handleMethod == null) { throw new RuntimeException("handleMethod is null"); } this.uri = uri; this.handleObject = handleObject; this.handleMethod = handleMethod; } }
解决接口参数使用注解的问题,约定所有的接口任何请求都可以请求但是传参一定要按格式传
Get请求
curl http://localhost:8008/shop-start/user/UserController/string.json?name=yiran&age=20
Post,Put,Delete请求
curl -x Post -H 'Content-Type:application/json' -d '{'name':'yiran','age':20}'http://localhost:8008/shop-start/user/UserController/string.json
无需配置生成接口文档(还没实现)
通过自己配置的拦截器bean进行拦截处理
返回值处理(json格式)
{ "code": 0, "data": "11" } /** * code码 * <pre> * 0: 操作成功 * 404 接口不存在 * 500 后台业务异常(已捕获) * 501 后台系统异常(未捕获) * 601 未登陆 * </pre> */
同时保留了springboot中dispatchservlet使用方式兼顾之间
准备工作
- 需要从spring容器中获取controller层的bean,将该bean中的public方法和生成约定的接口uri保存到map集合中
- 需要自定义一个servlet继承HttpServlet,重写get,post等方法
- 处理请求通过接口uri查找map集合中的方法对象去调用方法执行
- 处理接口中的请求参数对应map中对应方法对象的参数进行处理
- 配置拦截器,由于无法获取springmvc中配置的拦截器,所以只能通过另外配置的bean去获取
加载bean生成接口uri
-
通过application上下文获取bean
ApplicationContext context = getApplicationContext(); Class<?> cls = context.getType(beanName);
-
匹配controller层的规则
前提你controller层的接口全路径要包含module以及web.rpc
// temp:*.*.module.*.web.rpc.*.java // ex:com.chegnzhi.module.user.web.rpc.UserRpc.java private final static String URI_RULE = "\\S+\\.module\\.+\\S+\\.web\\.rpc\\.\\S+"; // 判断bean是否包含符合 *.web.rpc.* 规则 if (!clsName.matches(rootPackage + ".\\S+.web.rpc.\\S+")) { return; }
-
生成uri
/contextPath/user/UserRpc/addUser.json ---->new MvcUriMethod(uriName, bean, ms[i])
for (int i = 0; i < ms.length; i++) { // 只扫描public方法 if (!Modifier.isPublic(ms[i].getModifiers())) { continue; } String methodName = ms[i].getName(); if (methodName.indexOf("$") != -1) { continue; } String uriName = new StringBuilder(contextPath + "/").append(moduleName).append("/").append(pathName.replace(".", "/")).append("/").append(methodName).append(jsonSuffix).toString(); regUrlMapper(uriName, new MvcUriMethod(uriName, bean, ms[i])); }
自定义一个servlet继承HttpServlet并注册
- 通过继承HttpServletBean重写方法
public abstract class FrameworkServlet extends HttpServletBean {
protected static ApplicationContext applicationContext;
protected static boolean applicationContextInjected;
private static Logger log = LoggerFactory.getLogger(FrameworkServlet.class);
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.processRequest(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.processRequest(req, resp);
}
@Override
protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.processRequest(req, resp);
}
@Override
protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.processRequest(req, resp);
}
protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException;
}
-
MiniMvcServletFast继承FrameworkServlet重写doService方法
public class MiniMvcServletFast extends FrameworkServlet { /** * 处理请求 * * @param req * @param resp */ @Override protected void doService(HttpServletRequest req, HttpServletResponse resp) { try { MvcUriMapperHandler.MvcUriMethod handler = mvcUriMapperHandler.getMappedHandler(uri); if (handler != null) { handlerObject = handler.getHandleObject(); handlerMethod = handler.getHandleMethod(); req.setAttribute(SPRING_CONTROLLER_INVOKE_METHOD, handler.getHandleMethod()); } // 执行拦截器 preHandle if (interceptors != null) { for (HandlerInterceptor interceptor : interceptors) { interceptorIndex++; if (!interceptor.preHandle(req, resp, handlerMethod)) { break; } } } // 执行目标方法 Object result = null; if (handler != null) { result = mvcHandlerResolver.invokeHandlerMethod(handler.getHandleMethod(), handler.getHandleObject(), req, resp); } // 执行拦截器 postHandle if (interceptors != null) { for (int i = interceptors.size() - 1; i >= 0; i--) { HandlerInterceptor interceptor = interceptors.get(i); interceptor.postHandle(req, resp, result, null); } } } catch (Throwable ex) { if (ex instanceof InvocationTargetException) { ex = ((InvocationTargetException) ex).getTargetException(); } if (ex instanceof BizException) { log.warn(ex.getClass().getName() + ":" + ex.getMessage(), ex); } else if (ex instanceof ServletException) { log.warn(ex.getClass().getName() + ":" + ex.getMessage()); } else { log.error(ex.getMessage(), ex); } String errMsg = ex.getMessage(); if (errMsg != null && errMsg.startsWith("com.yu.minimvc.exception.BizException:")) { errMsg = errMsg.substring("com.yu.minimvc.exception.BizException:".length()).trim(); if (errMsg.indexOf(N) != -1) { errMsg = errMsg.substring(0, errMsg.indexOf(N)); } ex = new BizException(errMsg); } error = new RuntimeException(ex); } finally { // 拦截器收尾 if (interceptors != null) { for (int i = interceptorIndex; i >= 0; i--) { HandlerInterceptor interceptor = interceptors.get(i); try { interceptor.afterCompletion(req, resp, handlerMethod, error); } catch (Throwable ex2) { log.error("HandlerInterceptor.afterCompletion threw exception", ex2); } } } } } }
配置拦截器
因为无法获取springmvc中的拦截器,所以通过装配一个bean来实现,MiniMvcServletFast来获取装配的mvcInterceptors进行拦截处理
@Bean("mvcInterceptors")
public List<HandlerInterceptor> miniMvcInterceptors() {
MiniMvcInterceptors miniMvcInterceptors = new MiniMvcInterceptors();
miniMvcInterceptors.addInterceptor(new MaxThreadInterceptor());
miniMvcInterceptors.addInterceptor(new PagingInterceptor());
return miniMvcInterceptors.getInterceptors();
}
配置自定义servlet
无需配置,通过自动配置加载servlet
生成接口文档
后续开发