关于springmvc中自定义servlet以及约定大于配置,生成约定好的api地址,无需使用注解

前言

关于自己写一个自定义的servlet的初衷,首先由于使用springboot写controller接口时需要使用许多的注解例如@GetMapping("/add"),@RequestMapping("/user"),@RequestBody 还需要想很多接口uri,有的时候还遇见接口uri重复,忘记写接口参数的注解`@RequestParam(value = “name”),结合在之前公司使用改造的webx的miniwebx的基础上我重新将它撸了一遍以及改造。

github源代码地址

特点

  1. 约定大于配置,弱化配置,按照约定简单使用

  2. 解决使用接口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;
        }
    }
  1. 解决接口参数使用注解的问题,约定所有的接口任何请求都可以请求但是传参一定要按格式传

    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
    
    1. 无需配置生成接口文档(还没实现)

    2. 通过自己配置的拦截器bean进行拦截处理

    3. 返回值处理(json格式)

      {
          "code": 0,
          "data": "11"
      }
          /**
           * code码
           * <pre>
           * 0:		操作成功
           * 404 	接口不存在
           * 500 	后台业务异常(已捕获)
           * 501		后台系统异常(未捕获)
           * 601 	未登陆
           * </pre>
      */
      
  2. 同时保留了springboot中dispatchservlet使用方式兼顾之间

准备工作

  1. 需要从spring容器中获取controller层的bean,将该bean中的public方法和生成约定的接口uri保存到map集合中
  2. 需要自定义一个servlet继承HttpServlet,重写get,post等方法
  3. 处理请求通过接口uri查找map集合中的方法对象去调用方法执行
  4. 处理接口中的请求参数对应map中对应方法对象的参数进行处理
  5. 配置拦截器,由于无法获取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

生成接口文档

后续开发

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值