SpringMVC源码研读(三)DispatcherServlet处理请求过程

本文分析了DispatcherServlet对于请求路径的分发与处理的主要流程。
由于DispatcherServlet的核心本质是一个Servlet,所以在研究DispatcherServlet处理请求过程之前,先看下Servlet的是怎么接收请求的。

(一)Servlet的执行过程

Tomcat的web容器在接收到http请求时,主调度线程会从事先定义好的线程池中分配一个当前工作线程,将请求分配给当前的工作线程。该线程会创建一个封装了http请求消息的HttpServletRequest对象和一个代表http响应消息的HttpServletResponse对象,根据web.xml文件中的servlet-mapping中的url-pattern确定请求对应的处理Servlet,然后调用Servlet的service()方法,并将请求和响应对象作为参数传递进去。
HttpServlet继承于Servlet,对service方法做了一些预处理:

    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);
    }

HttpServlet中的service方法就不去细看了,无非就是根据request的method分发给不同的方法执行请求。http get方法会调用doGet方法,http post方法调用doPost方法,etc.

(二)FrameworkServlet对请求的预处理

FrameworkServlet继承了HttpServlet,并重写了其中一系列对于请求进行处理的方法,如

    @Override
    protected final void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        processRequest(request, response);
    }
    @Override
    protected final void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        processRequest(request, response);
    }

主要逻辑在processRequest方法中,下面对其关键操作做了注释:

    protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;
    
        //从当前请求线程本地变量中获取LocaleContext
        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        //构造新的LocaleContext
        LocaleContext localeContext = buildLocaleContext(request);
        //从当前请求线程本地变量中获取ServletRequestAttributes
        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        //构造新的ServletRequestAttributes
        ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
        //将新建的LocaleContext与ServletRequestAttributes设置进线程本地变量
        initContextHolders(request, localeContext, requestAttributes);

        try {
            //抽象方法,由子类DispatcherServlet实现
            doService(request, response);
        }
        catch (ServletException ex) {
            failureCause = ex;
            throw ex;
        }
        catch (IOException ex) {
            failureCause = ex;
            throw ex;
        }
        catch (Throwable ex) {
            failureCause = ex;
            throw new NestedServletException("Request processing failed", ex);
        }

        finally {
            //重置LocaleContext与ServletRequestAttributes对象,解除与请求线程的绑定
            resetContextHolders(request, previousLocaleContext, previousAttributes);
            if (requestAttributes != null) {
                requestAttributes.requestCompleted();
            }

            if (logger.isDebugEnabled()) {
                if (failureCause != null) {
                    this.logger.debug("Could not complete request", failureCause);
                }
                else {
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        logger.debug("Leaving response open for concurrent processing");
                    }
                    else {
                        this.logger.debug("Successfully completed request");
                    }
                }
            }
            //发布RequestHandledEvent事件,通知监听器
            publishRequestHandledEvent(request, response, startTime, failureCause);
        }
    }

(三)DispatcherServlet处理请求过程

DispatcherServlet继承了FrameworkServlet,并实现了其中的抽象方法doService,是处理请求的具体实现。其中最关键的处理逻辑就是doDispatch方法。doDispatch主要分为以下几个部分:

  1. 根据请求的路径找到用于处理请求的HandlerExecutionChain;
    //使用配置中的multipartResolver预处理多媒体请求,把request转换为MultipartHttpServletRequest
    processedRequest = checkMultipart(request);
    multipartRequestParsed = (processedRequest != request);

    // 通过getHandler方法决定处理请求的handler(具体逻辑在下一节)
    mappedHandler = getHandler(processedRequest);
    if (mappedHandler == null || mappedHandler.getHandler() == null) {
        //如果找不到handler,则会报404错误
        noHandlerFound(processedRequest, response);
        return;
    }
  1. 获取HandlerExecutionChain对应的HandlerAdapter;
    // Determine handler adapter for the current request.
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
  1. 获取当前请求的最后更改时间,判断浏览器是否可以直接使用之前缓存的结果,如果缓存未过期,直接返回304状态码;
    String method = request.getMethod();
    boolean isGet = "GET".equals(method);
    if (isGet || "HEAD".equals(method)) {
        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
        if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
            return;
        }
    }

4.按顺序调用拦截器的preHandle方法,全部通过以后,使用对应的HandlerAdapter处理HandlerExecutionChain,得到ModelAndView对象;

//调用interceptor链预处理请求,若不通过,直接返回
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    return;
}
// 调用handle方法得到ModelAndView对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
applyDefaultViewName(processedRequest, mv);
//在渲染视图之前,按顺序调用interceptor链的postHandle请求
mappedHandler.applyPostHandle(processedRequest, response, mv);
//使用ViewResolver渲染视图
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

(四)路径映射查找

下面进入到DispatcherServlet的getHandler方法中,分析一下路径对应的HandlerExecutionChain是怎么获得的。

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        for (HandlerMapping hm : this.handlerMappings) {
            HandlerExecutionChain handler = hm.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
        return null;
    }

上一篇提到过,DispatcherServlet在进行初始化的时候,会从springmvc容器中将handlerMappings实例取出,通过order进行排序后设置进属性中。
在getHandler方法中,DispatcherServlet遍历初始化过程中已经装载好的handlerMappings,按顺序查找路径对应的处理器。当上一个HandlerMapping查找不到的时候才会由下一个HandlerMapping处理。
RequestMappingHandlerMapping优先级最高,用于查找@Controller与@RequestMapping注解对应的处理器。从它入手看看具体是怎么处理的吧~
RequestMappingHandlerMapping继承了AbstractHandlerMapping,其中的getHandler主要分为两步:

    @Override
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        //1、通过子类实现获取handler
        Object handler = getHandlerInternal(request);
        //省略部分代码
        //2、根据请求与handler构建HandlerExecutionChain
        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
        //省略部分代码
        return executionChain;
    }
  1. AbstractHandlerMethodMapping实现了AbstractHandlerMapping中的getHandlerInternal抽象方法,最终实现了handler的查找,主要代码如下:
    protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List matches = new ArrayList();
        //以lookupPath作为key,mappingRegistry.urlLookup中获取匹配的RequestMappingInfo
        List directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
        if (directPathMatches != null) {
                        //若通过url能直接查找到,则在对RequestMappingInfo与request中相关属性进行比对后,
                        将符合的RequestMappingInfo与HandlerMethod对象包装为Match对象,放到matches中
            addMatchingMappings(directPathMatches, matches, request);
        }
        if (matches.isEmpty()) {
                        //没有找到的情况下只能遍历mappingRegistry.mappingLookup中的所有RequestMappingInfo实例
            addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
        }

        if (!matches.isEmpty()) {
            Comparator comparator = new MatchComparator(getMappingComparator(request));
                        //若有多个匹配项,将它们排序
            Collections.sort(matches, comparator);
                        //获取匹配率最高的
            Match bestMatch = matches.get(0);
            if (matches.size() > 1) {
                if (CorsUtils.isPreFlightRequest(request)) {
                    return PREFLIGHT_AMBIGUOUS_MATCH;
                }
                Match secondBestMatch = matches.get(1);
                if (comparator.compare(bestMatch, secondBestMatch) == 0) {
                    Method m1 = bestMatch.handlerMethod.getMethod();
                    Method m2 = secondBestMatch.handlerMethod.getMethod();
                    throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +
                            request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");
                }
            }
            handleMatch(bestMatch.mapping, lookupPath, request);
            return bestMatch.handlerMethod;
        }
        else {
                        //细化错误提示,如请求方法不适配,媒体类型不适配等
            return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
        }
    }
  1. 在获得handler后,通过getHandlerExecutionChain方法将拦截器添加上去:
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
    //如果是AbstractUrlHandlerMapping的getHandlerInternal,可能返回HandlerExecutionChain对象
    HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
            (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

    String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
    //adaptedInterceptors是在HandlerMapping初始化时注入进来的
    for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
        if (interceptor instanceof MappedInterceptor) {
            MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
            //根据mvc中interceptor的mapping配置,添加符合路径的拦截器
            if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                chain.addInterceptor(mappedInterceptor.getInterceptor());
            }
        }
        else {
            chain.addInterceptor(interceptor);
        }
    }
    return chain;
}

(五)处理请求

HandlerMapping的作用主要是根据request请求获取能够处理当前request的handler,而HandlerAdapter的作用在于将request中的各个属性,如request param适配为handler能够处理的形式。HandlerMethod类型的handler由RequestMappingHandlerAdapter进行处理。

    @Override
    public final boolean supports(Object handler) {
        // 判断当前handler是否为HandlerMethod类型,并且判断supportsInternal()方法返回值是否为true,
    // 这里supportsInternal()方法是提供给子类实现的一个方法,对于RequestMappingHandlerAdapter而言,其返回值始终是true
        return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
    }

其中最主要的处理逻辑在invokeHandlerMethod方法中,具体如下:

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
        HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    // 获取容器中全局配置的InitBinder和当前HandlerMethod所对应的Controller中
    // 配置的InitBinder,用于进行参数的绑定
    WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
      // 获取容器中全局配置的ModelAttribute和当前当前HandlerMethod所对应的Controller
    // 中配置的ModelAttribute,这些配置的方法将会在目标方法调用之前进行调用
    ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
    //将HandlerMethod封装为ServletInvocableHandlerMethod
    ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
    invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
    invocableMethod.setDataBinderFactory(binderFactory);
    invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

    ModelAndViewContainer mavContainer = new ModelAndViewContainer();
    mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
    //注入@SessionAttributes声明的参数,并在在目标Handler调用之前调用@ModelAttribute标注的方法
    modelFactory.initModel(webRequest, mavContainer, invocableMethod);
    mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
    //省略部分代码
    //使用handler处理请求,并将返回值封装为一个ModelAndView对象
    invocableMethod.invokeAndHandle(webRequest, mavContainer);
    return getModelAndView(mavContainer, modelFactory, webRequest);
}

至此,一个http请求处理完毕,返回DispatcherServlet中执行后续收尾操作。

你可能感兴趣的:(SpringMVC源码研读(三)DispatcherServlet处理请求过程)