[SpringMVC源码学习]请求如何处理(五)

90 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第14天,点击查看活动详情

5. 对应请求如何处理

DispatcherServlet大致接收请求的时序图如下:

img

Servlet实例化,调用init方法和销毁在整个生命周期中只会出现一次,当每次发出请求的时候都会调用service方法【分发请求给对应请求方式对应处理方法】,首先我们找到他的顶级接口GenericServlet,我们很明显的能找到一个service方法。但是这个方法并没有实现,显然是留给子类实现的。

public abstract void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException;

他有这么两个实现类重写了这个方法,我们需要的显然是HttpServlet

image-20220806225156200

public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException
{
    HttpServletRequest  request;
    HttpServletResponse response;
    
    if (!(req instanceof HttpServletRequest &&
            res instanceof HttpServletResponse)) {
        throw new ServletException("non-HTTP request or response");
    }
​
    request = (HttpServletRequest) req;
    response = (HttpServletResponse) res;
​
    service(request, response);
}

将拿到的ServletRequestServletResponse转为HttpServletRequestHttpServletResponse,调用调用本类的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 = req.getDateHeader(HEADER_IFMODSINCE);
            if (ifModifiedSince < lastModified) {
                // 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);
    }
}

代码比较长,但是并不难理解。首先拿到请求方法的类型,是get还是post等等。我们以get为例。他会直接去调用doGet方法。

protected void doGet(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
{
    String protocol = req.getProtocol();
    String msg = lStrings.getString("http.method_get_not_supported");
    if (protocol.endsWith("1.1")) {
        resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
    } else {
        resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
    }
}

这里其实就是判断一下协议类型支不支持,不支持就会报错。并没有做其他操作。那么到底是怎么回事呢,其实这个类也有他的子类FrameworkServlet重写了这个方法。

@Override
protected final void doGet(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 previousLocaleContext = LocaleContextHolder.getLocaleContext();
   LocaleContext localeContext = buildLocaleContext(request);
​
   // 构建一个RequestAttributes
   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);
   }
}

首先获取一下启动时间,然后为了线程安全去拿一个线程副本变量,拿不到在去创建。然后为了将数据存储到request中,去构建一个RequestAttributes,然后将他暴露在我们的上下文对象当中。再去调用doService方法。最后再去清理上下文中的request中的数据。我们来看doService这个方法。

// 公开 DispatcherServlet 特定的请求属性并委托给 doDispatch 以进行实际调度。
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);
   }
​
   RequestPath previousRequestPath = null;
   if (this.parseRequestPath) {
      previousRequestPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
      ServletRequestPathUtils.parseAndCache(request);
   }
​
   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);
         }
      }
      if (this.parseRequestPath) {
         ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request);
      }
   }
}

此时就已经进入了DispatcherServlet当中,DispatcherServlet实现了这个方法。比较重要的其实就是doDispatch这个方法。前面只是给request当中设置一些可用对象而已。然后为了重定向之后能获取到上一个页面,给request当中设置一些属性。

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);
​
         // 根据当前request对象找到对应处理对象
         // 获取合适的HandlerExecutionChain
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
         }
​
         // 准备参数解析器和返回值解析器
         // RequestMappingHandlerAdapter =>判断请求处理方法
         // Controller  simplecontro1lerHandlerAdapter
         // servlet   HttpRequestHandlerAdapter
         // HandlerMethod   RequestMappingHandlerAdapter
          
         // # 获取合适的HandlerAdapter 
         // 找到的适配对象就是RequestMappingHandlerAdapter,因为是一个HandlerMethod对象
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
​
         // Process last-modified header, if supported by the handler.
         String method = request.getMethod();
         boolean isGet = HttpMethod.GET.matches(method);
         if (isGet || HttpMethod.HEAD.matches(method)) {
            long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
            if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
               return;
            }
         }
         // 拦截器的PreHandle方法如果返回true,才会继续执行,如果返回false,就返回,不执行请求
         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }
​
         // handle执行调用(核心)
         // 如果这里我们是传的json,返回的是null
         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);
      }
      // 处理请求返回的ModelAndView对象【视图渲染工作】 就是视图解析并渲染到页面
      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);
         }
      }
   }
}

核心代码就是长,进入这个方法,首先定义了一些变量,然后去检测是不是一个文件上传请求。再然后就是去拿到对应的处理器执行链,在getHandle中。

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

其实就是去遍历所有的handlerMapping调用getHandler方法去获得执行链。


明天继续!