DispatchServlet请求处理流程源码解析

DispatchServlet的service()

一个请求是从一个客户端发出,经过容器然后将请求交与合适的servlet,servlet调用service()方法来处理这个请求。
如下是 DispatchServlet 继承关系图。

dispatchServlet继承关系

我们主要关心上图中各类的 service() 方法。看看 DispatchServlet 中的 service() 方法来自哪里。

  • Servlet: Servlet为接口,只是声明了 service()方法。
  • GenericServlet: 抽象类,service() 方法交由子类实现。
  • HttpServlet: 抽象类,实现了 service() 方法,根据请求方式的不同,判断调用什么方法来处理请求。如果我们不用框架的话,那么只需要继承自 HttpServlet,然后重写 doGet()或者 doPost() 方法即可,重写的代码就是处理请求的过程所要执行的代码。
  • HttpServletBean: 继承HttpServlet 的service() 方法。
  • FramworkServlet: 重写 HttpServlet 的 service() 方法,目的是为了能够处理 PATCH 请求。什么是PATCH请求
  • DispatchServlet: 继承 FramworkServlet 的 service() 方法.

总结,DispatchServlet的 service() 方法来自父类 FramworkServlet,该方法既可以处理已知的请求(如GET,POST),又可以处理 PATCH 请求,然后根据请求方式的不同执行不同的处理过程。

DispatchServlet的doGet()等方法

我们知道一个servlet要想处理请求,必须重写doGet() doPost()等方法,DispatchServlet当然也不例外,那么DispatchServlet的 doGet(),doPost() 方法重写在哪里呢?答案是在其父类 FramworkServlet 中。DispatchServlet 直接继承即可。我们来看一下 FramworkServlet 部分源码。

@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
        //增加对 PATCH 请求的处理
            if (HttpMethod.PATCH == httpMethod || httpMethod == null) {
                processRequest(request, response);
            }
            else {
                super.service(request, response);
            }
      }

//重写doGet()方法
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
      processRequest(request, response);
    }
//重写doPost()方法
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    processRequest(request, response);
}

好,通过以上代码,我们了解了无论什么样的请求方法,DispatchServlet 全部都委托给了一个 processRequest() 方法进行处理。如下是 processRequest() 方法源代码。

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

        long startTime = System.currentTimeMillis();
        Throwable failureCause = null;

        LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
        LocaleContext localeContext = buildLocaleContext(request);

        RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
        asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

        initContextHolders(request, localeContext, requestAttributes);

        try {
      //doService() FramworkServlet没有实现,只是简单定义,交由 DispatchServlet实现
            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 {
            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");
                    }
                }
            }

            publishRequestHandledEvent(request, response, startTime, failureCause);
        }
    }

深入了解 doService() 方法:

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
  ...

  //设置上下文及其他属性,方便其他方法需要时从request获取
  request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
  request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
  request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
  request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

  FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
  if (inputFlashMap != null) {
    request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
  }
  request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
  request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);

  try {
    //深入了解
    doDispatch(request, response);
  }
  finally {
    if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
      // Restore the original attribute snapshot, in case of an include.
      if (attributesSnapshot != null) {
        restoreAttributesAfterInclude(request, attributesSnapshot);
      }
    }
  }
}

继续深入了解 doDispatch() 方法

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
  HttpServletRequest processedRequest = request;
  //HandlerExecutionChain,执行链,后面会详细说明
  HandlerExecutionChain mappedHandler = null;
  boolean multipartRequestParsed = false;

  WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

  try {
    //逻辑视图对象
    ModelAndView mv = null;
    Exception dispatchException = null;

    try {
      //检查请求是否为文件上传请求,如果是使文件上传解析器可用
      processedRequest = checkMultipart(request);
      //如果是文件上传请求multipartRequestParsed值为true
      multipartRequestParsed = (processedRequest != request);

      //获取处理该请求的handler
      //深入了解
      mappedHandler = getHandler(processedRequest);
      if (mappedHandler == null || mappedHandler.getHandler() == null) {
        noHandlerFound(processedRequest, response);
        return;
      }

      // Determine handler adapter for the current request.
      HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

      // Process last-modified header, if supported by the handler.
      String method = request.getMethod();
      boolean isGet = "GET".equals(method);
      if (isGet || "HEAD".equals(method)) {
        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
        if (logger.isDebugEnabled()) {
          logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
        }
        if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
          return;
        }
      }

      if (!mappedHandler.applyPreHandle(processedRequest, response)) {
        return;
      }

      // Actually invoke the handler.
      mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

      if (asyncManager.isConcurrentHandlingStarted()) {
        return;
      }

      applyDefaultViewName(processedRequest, mv);
      mappedHandler.applyPostHandle(processedRequest, response, mv);
    }
    catch (Exception ex) {
      dispatchException = ex;
    }
    catch (Throwable err) {
      // As of 4.3, we're processing Errors thrown from handler methods as well,
      // making them available for @ExceptionHandler methods and other scenarios.
      dispatchException = new NestedServletException("Handler dispatch failed", err);
    }
    processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
  }
  catch (Exception ex) {
    triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
  }
  catch (Throwable err) {
    triggerAfterCompletion(processedRequest, response, mappedHandler,
        new NestedServletException("Handler processing failed", err));
  }
  finally {
    if (asyncManager.isConcurrentHandlingStarted()) {
      // Instead of postHandle and afterCompletion
      if (mappedHandler != null) {
        mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
      }
    }
    else {
      // Clean up any resources used by a multipart request.
      if (multipartRequestParsed) {
        cleanupMultipart(processedRequest);
      }
    }
  }
}

HandlerExecutionChain:
包含一个处理请求的处理器(Handler),同时包含若干个对该请求实施拦截的拦截器(HandlerInterceptor),当HandlerMapping 返回 HandlerExecutionChain 后, DispatchServlet 将请求交给定义在 HandlerExecutionChain 中的拦截器和处理器一并处理。

HandlerExecutionChain

拦截器可以在三个地方对请求进行处理,分别是:

  • boolean preHandler(request,response,handler): 请求到达 adapter 之前。返回 true 继续传递请求,false 不传递请求,将请求驳回。
  • void postHandler(request,response,handler): adapter 处理完之后。
  • void afterCompletion(request,response,handler,exception): 响应已经被渲染之后。
  1. 深入了解 DispatchServlet.getHandler() 方法。

    protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        //循环遍历默认的处理器映射,如果找到handler,直接返回
            for (HandlerMapping hm : this.handlerMappings) {
                ...
                HandlerExecutionChain handler = hm.getHandler(request);
                if (handler != null) {
                    return handler;
                }
            }
            return null;
        }
    

    HandlerMapping 本身是一个接口,只有一个方法 getHandler(),本例中的 this.handlerMappings 中包含3个 HandlerMapping 的实现,他们将按照先后顺序查找该请求的处理器映射,找到即返回。

    如下是本例中包含的3个处理器映射实现类:

    1. RequestMappingHandlerMapping
    2. BeanNameUrlHandlerMapping
    3. SimpleUrlHandlerMapping

    如下是 HandlerMapping 继承关系图

    HandlerMappingInherice

    每个实现类都继承了HandlerMapping接口,故每个处理器映射都有一个用来查找handler的getHandler()方法,我们以本例的 RequestMappingHandlerMapping 类为例子来了解该类的 getHandler() 方法。

    我们由上面的继承关系图可得知:
    RequestMappingHandlerMapping 是 AbstractHandlerMapping 和 AbstractHandlerMethodMapping 的子类。
    以下是 AbstractHandlerMapping 的部分源码.

    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
      //主要调用了 getHandlerInternal() 方法
      Object handler = getHandlerInternal(request);
        if (handler == null) {
            handler = getDefaultHandler();
        }
        if (handler == null) {
            return null;
        }
        // Bean name or resolved handler?
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = getApplicationContext().getBean(handlerName);
        }
      //根据请求的路径将handler和对应的拦截器包装成一个handlerExecutionChain对象
        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
        if (CorsUtils.isCorsRequest(request)) {
            CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
            CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
            CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
        }
        return executionChain;
    }
    

    通过查看源代码可知主要调用了 getHandlerInternal() 方法,该方法由子类 AbstractHandlerMethodMapping 实现。如下是子类 AbstractHandlerMethodMapping 的部分源码。

    protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
      //获取请求的URL路径
      String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
      //获取读锁
        this.mappingRegistry.acquireReadLock();
        try {
        //根据请求路径找到对应的handler方法
            HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        //返回HandlerMethod对象
            return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
        }
        finally {
        //释放获取到的读锁
            this.mappingRegistry.releaseReadLock();
        }
    }
    

    该方法执行完毕之后,继续执行 AbstractHandlerMapping.getHandler()方法中的其余部分。其余部分所做的主要工作就是根据请求的URL路径找到拦截该路径的拦截器,然后将handler和这些拦截器封装成为一个 HandlerExecutionChain 对象返回。

    至此,DispatchServlet.doDispatch() 方法的 mappedHandler 对象初始化完毕。

  2. 深入了解 DispatchServlet.getHandlerAdapter() 方法
    如下是 DispatchServlet.getHandlerAdapter() 方法部分源码。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
  for (HandlerAdapter ha : this.handlerAdapters) {
          if (ha.supports(handler)) {
              return ha;
          }
      }
  }

HandlerAdapter 为一个接口,该接口内部定义了如下三个方法:

  • supports(): 给出一个 handler,查看该 HandlerAdapter 是否支持该 handler。
  • handle(): 使用本 HandlerAdapter 代理执行这个 handler 中的方法。
  • getLastModified(): 查看该请求的 lastModified 值

this.handlerAdapters 为 实现 HandlerAdapter 接口的实现类集合,默认的实现类有如下三个:

  • RequestMappingHandlerAdapter
  • HttpRequestHandlerAdapter
  • SimpleControllerHandlerAdapter

继承关系如下图所示:


HandlerAdapterInherice

未完待更...

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 关注的一个自媒体写手,讲述了她喜欢上一个男人追随他到几个城市又被其拒绝的经过。在文章里她详细描述了被拒绝后如何艰难...
    姑娘在梦游阅读 569评论 2 3
  • 凤凰山下古禅台, 俗客如云探看来。 不为参修非问道, 一株银杏荡秋怀。
    A_master阅读 241评论 6 4
  • 共同点:都会拦截静态资源不同点:/不会拦截.jsp/*会拦截.jsp
    听说我很强阅读 270评论 0 0
  • 昨天录制完两期节目已经晚上7点多了,简单的吃了点饭,就往火车站赶了,虽然时间还很多,但早到火车站能找地方休息。 前...
    薛善强阅读 118评论 2 0