暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

SpringMvc源码分析之DispatcherServlet访问处理流程

Alleria Windrunner 2020-11-04
145
上一篇我们已经分析过了DispatcherServlet的初始化流程,那么本篇我们一起来看看DispatcherServlet的访问处理流程。
首先还是先找入口,根据之前的分析servlet的处理流程在service方法中,所以直接看servlet的service方法:
    /**
    * Called by the servlet container to allow the servlet to respond to a
    * request.
    *
    * <p>
    * This method is only called after the servlet's <code>init()</code> method
    * has completed successfully.
    *
    * <p>
    * The status code of the response always should be set for a servlet that
    * throws or sends an error.
    *
    *
    * <p>
    * Servlets typically run inside multithreaded servlet containers that can
    * handle multiple requests concurrently. Developers must be aware to
    * synchronize access to any shared resources such as files, network
    * connections, and as well as the servlet's class and instance variables.
    * More information on multithreaded programming in Java is available in <a
    * href
    * ="http://java.sun.com/Series/Tutorial/java/threads/multithreaded.html">
    * the Java tutorial on multi-threaded programming</a>.
    *
    *
    * @param req
    * the <code>ServletRequest</code> object that contains the
    * client's request
    *
    * @param res
    * the <code>ServletResponse</code> object that contains the
    * servlet's response
    *
    * @exception ServletException
    * if an exception occurs that interferes with the servlet's
    * normal operation
    *
    * @exception IOException
    * if an input or output exception occurs
    */
    public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException;


    /**
    * Called by the servlet container to allow the servlet to respond to a
    * request. See {@link Servlet#service}.
    * <p>
    * This method is declared abstract so subclasses, such as
    * <code>HttpServlet</code>, must override it.
    *
    * @param req
    * the <code>ServletRequest</code> object that contains the
    * client's request
    * @param res
    * the <code>ServletResponse</code> object that will contain the
    * servlet's response
    * @exception ServletException
    * if an exception occurs that interferes with the servlet's
    * normal operation occurred
    * @exception IOException
    * if an input or output exception occurs
    */
    @Override
    public abstract void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException;
    但是我们发现Servlet的子类GenericServlet并没有具体实现该方法,那么继续往下找:
      /**
      * Dispatches client requests to the protected
      * <code>service</code> method. There's no need to
      * override this method.
      *
      * @param req the {@link HttpServletRequest} object that
      * contains the request the client made of
      * the servlet
      *
      * @param res the {@link HttpServletResponse} object that
      * contains the response the servlet returns
      * to the client
      *
      * @exception IOException if an input or output error occurs
      * while the servlet is handling the
      * HTTP request
      *
      * @exception ServletException if the HTTP request cannot
      * be handled
      *
      * @see jakarta.servlet.Servlet#service
      */
      @Override
      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(lStrings.getString("http.non_http"));
      }
      service(request, response);
      }



      /**
      * Receives standard HTTP requests from the public
      * <code>service</code> method and dispatches
      * them to the <code>do</code><i>Method</i> methods defined in
      * this class. This method is an HTTP-specific version of the
      * {@link jakarta.servlet.Servlet#service} method. There's no
      * need to override this method.
      *
      * @param req the {@link HttpServletRequest} object that
      * contains the request the client made of
      * the servlet
      *
      * @param resp the {@link HttpServletResponse} object that
      * contains the response the servlet returns
      * to the client
      *
      * @exception IOException if an input or output error occurs
      * while the servlet is handling the
      * HTTP request
      *
      * @exception ServletException if the HTTP request
      * cannot be handled
      *
      * @see jakarta.servlet.Servlet#service
      */
      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;
      try {
      ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
      } catch (IllegalArgumentException iae) {
      // Invalid date header - proceed as if none was set
      ifModifiedSince = -1;
      }
      if (ifModifiedSince < (lastModified 1000 * 1000)) {
      // If the servlet mod time is later, call doGet()
      // Round down to the nearest second for a proper compare
      // A ifModifiedSince of -1 will always be less
      maybeSetLastModified(resp, lastModified);
      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);


      } else {
      //
      // Note that this means NO servlet supports whatever
      // method was requested, anywhere on this server.
      //


      String errMsg = lStrings.getString("http.method_not_implemented");
      Object[] errArgs = new Object[1];
      errArgs[0] = method;
      errMsg = MessageFormat.format(errMsg, errArgs);


      resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
      }
      }
      在HttpServlet中找到了具体的实现,其业务逻辑也很简单:总的来说就是去处理不同的方法类型。我们挑一个常用的doPost仔细看一看:
        protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {


        String msg = lStrings.getString("http.method_post_not_supported");
        sendMethodNotAllowed(req, resp, msg);
        }

        private void sendMethodNotAllowed(HttpServletRequest req, HttpServletResponse resp, String msg) throws IOException {
        String protocol = req.getProtocol();
        // Note: Tomcat reports "" for HTTP/0.9 although some implementations
        // may report HTTP/0.9
        if (protocol.length() == 0 || protocol.endsWith("0.9") || protocol.endsWith("1.0")) {
        resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        } else {
        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        }
        }
        最后你们会发现在HttpServlet中的doPost方法中根本没有处理相关请求的实现,所以我们可以肯定的是有子类覆盖了doPost方法:
          /**
          * Delegate POST requests to {@link #processRequest}.
          * @see #doService
          */
          @Override
          protected final void doPost(HttpServletRequest request, HttpServletResponse response)
          throws ServletException, IOException {


          processRequest(request, response);
          }


          /**
          * Process this request, publishing an event regardless of the outcome.
          * <p>The actual event handling is performed by the abstract
          * {@link #doService} template method.
          */
          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(request, response);
          }
          catch (ServletException | 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();
          }
          logResult(request, response, failureCause, asyncManager);
          publishRequestHandledEvent(request, response, startTime, failureCause);
          }
          }

          /**
          * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
          * for the actual dispatching.
          */
          @Override
          protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
          logRequest(request);


          // Keep a snapshot of the request attributes in case of an include,
          // to be able to restore the original attributes after the include.
          Map<String, Object> attributesSnapshot = null;
          if (WebUtils.isIncludeRequest(request)) {
          attributesSnapshot = new HashMap<>();
          Enumeration<?> attrNames = request.getAttributeNames();
          while (attrNames.hasMoreElements()) {
          String attrName = (String) attrNames.nextElement();
          if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
          attributesSnapshot.put(attrName, request.getAttribute(attrName));
          }
          }
          }


          // Make framework objects available to handlers and view objects.
          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());


          if (this.flashMapManager != null) {
          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);
          }
          }
          }
          }
          最后一路跟踪我们就来到了DispatcherServlet的doDispatch方法:
            /**
            * Process the actual dispatching to the handler.
            * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
            * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
            * to find the first that supports the handler class.
            * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
            * themselves to decide which methods are acceptable.
            * @param request current HTTP request
            * @param response current HTTP response
            * @throws Exception in case of any kind of processing failure
            */
            protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
            HttpServletRequest processedRequest = request;
            HandlerExecutionChain mappedHandler = null;
            boolean multipartRequestParsed = false;


            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);


            try {
            ModelAndView mv = null;
            Exception dispatchException = null;


            try {
            // 判断该请求是否是包含多部件(文件)请求
            processedRequest = checkMultipart(request);
            multipartRequestParsed = (processedRequest != request);


            // Determine handler for the current request.
            // 根据请求查找处理器
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == 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 (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);


            // 处理器拦截器在处理器方法执行后执行,可以对ModelAndView进行处理
            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);
            }
            }
            }
            }
            这个方法里面可以说基本上就是SpringMvc优化Servlet的主要逻辑了:
            1. 判断该请求是否是包含多部件(文件)请求

            2. 根据请求查找处理器

            3. 根据处理器查找对应的适配器

            4. 处理器拦截器在处理器方法执行之前进行拦截处理

            5. 处理器适配器去执行处理器的方法

            6. 处理器拦截器在处理器方法执行后执行,可以对ModelAndView进行处理

            7. 处理分发的结果

            8. 处理器拦截器在处理器方法执行后执行


            到此为止,基本上SpringMvc的访问处理流程的主线就找到了,后面的文章我们将会一个个子流程仔细分析下。

            文章转载自Alleria Windrunner,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

            评论