美文网首页Spring源码分析
DispatcherServlet源码解析

DispatcherServlet源码解析

作者: 聂云舒 | 来源:发表于2019-12-01 19:26 被阅读0次

    前言

    一次为了解决跨域问题,采用了CORS方法。根据官方解释
    ,只需要在响应头里设置
    1、Access-Control-Allow-Origin
    2、Access-Control-Allow-Methods
    3、Access-Control-Allow-Headers
    三个值就可以了,于是想到在HandlerInterceptor#preHandle()里去拦截跨域请求(options),然后再根据自定义注解判断请求的controller是否支持跨域请求,再设置对应的响应头。(项目基于spring3.2.x)但是发现请求死活无法进入preHandle里(项目里只有一个自定义的preHandle,不存在提前被别的HandlerInterceptor返回的情况)。于是利用debug大法,发现spring获取拦截器时是根据url和请求类型进行判断的,由于跨域请类型是options,无法获取对应的handler和HandlerInterceptor,导致直接就返回了,没有进入拦截器里。(spring4.x后有个默认的handler支持处理options)。于是把debug过程中学习到的知识,下次排查问题可以更快。

    Dispathcher处理请求的流程概览

    image-20191201184710800.png
    组件 说明
    Dispatcher 负责接收用户请求,并且协调内部的各个组件完成请求的响应
    HandlerMapping 通过request获取handler和interceptors
    HandlerAdapter 处理器的适配器。Spring 中的处理器的实现多变,可以通过实现 Controller 接口,也可以用 @RequestMapping 注解将方法作为一个处理器等,这就导致调用处理器是不确定的。所以这里需要一个处理器适配器,统一调用逻辑。
    ViewResolver 解析视图,返回数据

    Dispathcer的继承图

    image-20191127200505690.png

    从继承视图可以看出,Dispatcher是Servlet的一个实现类。也就是遵循了J2EE规范的处理器。

    Servlet是一个接口,包含以下方法

    public interface Servlet {
       
        /**
        * 对配置文件(web.xml)的解析,初始化
        */
        public void init(ServletConfig config) throws ServletException;
    
        public ServletConfig getServletConfig();
    
        /**
        * 业务逻辑实现在该方法内
        * 该方法会被Web容器(如:Tomcat)调用
        */
        public void service(ServletRequest req, ServletResponse res)
                throws ServletException, IOException;
    
        public String getServletInfo();
    
        public void destroy();
    }
    
    

    HttpServlet这个类是和 HTTP 协议相关。该类的关注点在于怎么处理 HTTP 请求,比如其定义了 doGet 方法处理 GET 类型的请求,定义了 doPost 方法处理 POST 类型的请求等。我们若需要基于 Servlet 写 Web 应用,应继承该类,并覆盖指定的方法。所有的处理get请求、post请求都是由service 方法进行调用的。如下:

    public abstract class HttpServlet extends GenericServlet
            implements java.io.Serializable {
      
      /**
        *实现Servlet的service方法,并且将请求转为http请求
        *调用内部方法service(HttpServletRequest req, HttpServletResponse resp),处理http请求
        *
        */
      public void service(ServletRequest req, ServletResponse res)
                throws ServletException, IOException {
            HttpServletRequest request;
            HttpServletResponse response;
    
            try {
                request = (HttpServletRequest) req;
                response = (HttpServletResponse) res;
            } catch (ClassCastException e) {
                throw new ServletException("non-HTTP request or response");
            }
            service(request, response);
        }
      
      /**
        *http请求的分发
        */
       protected void service(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
            String method = req.getMethod();
    
            if (method.equals(METHOD_GET)) {
                long lastModified = getLastModified(req);
                if (lastModified == -1) {
                    // servlet doesn't support if-modified-since, no reason
                    // to go through further expensive logic
                    doGet(req, resp);
                } else {
                    long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                        doGet(req, resp);
                    } else {
                        resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                    }
                }
    
            } else if (method.equals(METHOD_HEAD)) {
                long lastModified = getLastModified(req);
                maybeSetLastModified(resp, lastModified);
                doHead(req, resp);
    
            } else if (method.equals(METHOD_POST)) {
                doPost(req, resp);
    
            } else if (method.equals(METHOD_PUT)) {
                doPut(req, resp);
    
            } else if (method.equals(METHOD_DELETE)) {
                doDelete(req, resp);
    
            } else if (method.equals(METHOD_OPTIONS)) {
                doOptions(req, resp);
    
            } else if (method.equals(METHOD_TRACE)) {
                doTrace(req, resp);
    
            }
        }
      
        //其他方法
    }
            
    

    Dispatcher没有直接实现servlet,而是继承了HttpServlet。对于http请求的处理流程:

    HttpServlet.service -> FrameworkServlet.service -> FrameworkServlet.processRequest -> DispatcherServlet.doService -> DispatcherServlet.doDispatch

    Dispatcher#doDispatch

    Dispatcher对请求进行处理在doDispatch方法里

    // 省略了内部实现,只看核心的地方
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
       //S1 先获取到请求的处理器Handler和拦截器interceptors
       HandlerExecutionChain mappedHandler = getHandler(processedRequest, false);
            if (mappedHandler == null || mappedHandler.getHandler() == null) {
                noHandlerFound(processedRequest, response);
                return;
            }
      /*
       * S2
       * 执行拦截器,一般自定义的HandlerInterceptor#preHandle就是在这里执行的
       * 里面也很简单,就是一个for循环,不停的执行preHandle方法,直到某个拦截器返回false
       * 或者循环结束
       */
      if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
         }
      
      //S3 获取Handler对于的HandlerAdapter,负责调用Handler获取结果
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
      
      //S4 执行handler#handle,返回ModelAndView
      ModelAndView mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
      
      //S5 同理,一个for循环执行HandlerInterceptor#postHandle
      mappedHandler.applyPostHandle(processedRequest, response, mv);
      
      //S6 解析并渲染视图
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    

    以上比较核心的三步是:

    1、获取HandlerExecutionChain,也就是处理器和拦截器

    2、获取handler的adapter

    3、执行handler#handle,返回结果

    下面分别看下三个步骤的实现

    获取HandlerExecutionChain

    //HandlerExecutionChain mappedHandler = getHandler(processedRequest, false);
    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
       for (HandlerMapping hm : this.handlerMappings) {
          if (logger.isTraceEnabled()) {
             logger.trace(
                   "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
          }
          HandlerExecutionChain handler = hm.getHandler(request);
          if (handler != null) {
             return handler;
          }
       }
       return null;
    }
    

    逻辑很简单:for循环去匹配request对应的HandlerExecutionChain,其中handlerMappings被定义为List<HandlerMapping>。HandlerMapping是一个接口,继承关系如下:


    image-20191130142505374.png

    HandlerMapping的getHandler方法如下:

    //省略内部实现
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { 
       Object handler = getHandlerInternal(request);
       return getHandlerExecutionChain(handler, request);
    }
    

    1、通过getHandlerInternal获取handler,是一个模板方法,由子类具有去实现,主要有两个实现

    1.1、AbstractHandlerMethodMapping#getHandlerInternal

    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
            String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
            HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        }
    

    1.2、AbstractUrlHandlerMapping#getHandlerInternal

    protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
            String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
            Object handler = lookupHandler(lookupPath, request);
        }
    

    寻找handler的方法都是获取request的请求url,然后根据url去获取controller了。这里也就是使用@RequestMapping注解的方法。

    以lookupHandler为例

    protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
            // 能直接匹配就返回,比如 "/test" matches "/test"
            Object handler = this.handlerMap.get(urlPath);
            if (handler != null) {
                validateHandler(handler, request);
                return buildPathExposingHandler(handler, urlPath, urlPath, null);
            }
            // "/t*" matches both "/test" and "/team"
            List<String> matchingPatterns = new ArrayList<String>();
            for (String registeredPattern : this.handlerMap.keySet()) {
                if (getPathMatcher().match(registeredPattern, urlPath)) {
                    matchingPatterns.add(registeredPattern);
                }
            }
        // spring官方解释,按照最长路径进行匹配
            String bestPatternMatch = null;
            Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
            if (!matchingPatterns.isEmpty()) {
                Collections.sort(matchingPatterns, patternComparator);
                if (logger.isDebugEnabled()) {
                    logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
                }
                bestPatternMatch = matchingPatterns.get(0);
            }
            if (bestPatternMatch != null) {
                handler = this.handlerMap.get(bestPatternMatch);
                validateHandler(handler, request);
        }
    

    这里的核心是this.handlerMap.get(urlPath),所以的操作都是为了从map从获取数据。map是怎么被初始化的呢?

    map是通过registerHandler方法初始化的,每个子类都可以覆盖该方法,实现自己的数据初始化,但是最终的匹配handler过程是由父类统一实现的。实现了数据和操作的分离。

    registerHandler也很简单,先根据url从map中取handler,如果存在多个handler则报错(一个url无法对应多个handler)。

    没有则存入handler。

    2、找到拦截器,将处理器和拦截器封装后返回。

    /**
     * 获取拦截器的逻辑比较简单,也是url匹配
     */
    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
            HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
                    (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
            chain.addInterceptors(getAdaptedInterceptors());
    
            String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
            for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) {
                if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                    chain.addInterceptor(mappedInterceptor.getInterceptor());
                }
            }
    
            return chain;
        }
    
    public class HandlerExecutionChain {
    
        private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
    
        private final Object handler;
    
        private HandlerInterceptor[] interceptors;
    
        private List<HandlerInterceptor> interceptorList;
    
        private int interceptorIndex = -1;
    }
    

    这里不太理解为什么同时需要interceptors 和 interceptorList,都是同样的类型。

    获取HandlerAdapter

    image-20191130173648691.png
    public interface HandlerAdapter {
        boolean supports(Object handler);
    
        ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
    
        long getLastModified(HttpServletRequest request, Object handler);
    
    }
    

    HandlerAdapter接口定义很简单,通过support判断是否支持该handler,通过handler执行handler的方法。

    @ResponseBody 和@RequestBody的使用

    ​ 一般controller的入参和出参都是json的形式,只需要使用注解@ResponseBody 和 @RequestBody就可以完成http请求报文和pojo对象之间的转化。消息的转化都是通过HttpMessageConverter实现的。

    public interface HttpMessageConverter<T> {
    
     // 当前转换器是否能将对象类型转换为HTTP报文
        boolean canWrite(Class<?> clazz, MediaType mediaType);
    
        // 转换器能支持的HTTP媒体类型
        List<MediaType> getSupportedMediaTypes();
    
        // 转换HTTP报文为特定类型
        T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
                throws IOException, HttpMessageNotReadableException;
    
        // 将特定类型对象转换为HTTP报文
        void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
                throws IOException, HttpMessageNotWritableException;
    
    }
    
    

    read方法即是读取HTTP请求转换为参数对象,write方法即是将返回值对象转换为HTTP响应报文。Spring定义了参数解析器HandlerMethodArgumentResolver返回值处理器HandlerMethodReturnValueHandler统一处理。

    // 参数解析器接口
    public interface HandlerMethodArgumentResolver {
    
        // 解析器是否支持方法参数
        boolean supportsParameter(MethodParameter parameter);
    
        // 解析HTTP报文中对应的方法参数
        Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;
    
    }
    
    // 返回值处理器接口
    public interface HandlerMethodReturnValueHandler {
    
        // 处理器是否支持返回值类型
        boolean supportsReturnType(MethodParameter returnType);
    
        // 将返回值解析为HTTP响应报文
        void handleReturnValue(Object returnValue, MethodParameter returnType,
                ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
    
    }
    

    整体的处理流程:


    image-20191201173401005.png

    而对于@ResponseBody和@RequestBody都由RequestResponseBodyMethodProcessor统一进行处理。也就是RequestResponseBodyMethodProcessor即实现了HandlerMethodArgumentResolver 也实现了 HandlerMethodReturnValueHandler 。

    public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
      // 支持RequestBody注解参数
      @Override
        public boolean supportsParameter(MethodParameter parameter) {
            return parameter.hasParameterAnnotation(RequestBody.class);
        }
    
      // 支持ResponseBody注解返回值
        @Override
        public boolean supportsReturnType(MethodParameter returnType) {
            return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
                    returnType.hasMethodAnnotation(ResponseBody.class));
        }
      
      //解析参数
      @Override
        protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
                Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
            Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
            return arg;
        }
    
      // 解析返回值
        @Override
        public void handleReturnValue(Object returnValue, MethodParameter returnType,
                ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
                throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
            writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
        }
    }
    

    相关文章

      网友评论

        本文标题:DispatcherServlet源码解析

        本文链接:https://www.haomeiwen.com/subject/rwctgctx.html