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<Match> matches = new ArrayList<Match>();
        //以lookupPath作为key,mappingRegistry.urlLookup中获取匹配的RequestMappingInfo
        List<T> 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<Match> 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中执行后续收尾操作。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容