聊聊SpringMVC(2)---SpringMVC之請求過程

1.HttpServletBean

HttpServletBean主要參與建立過程,並無涉及請求的處理。ios

2.FrameworkServlet

FrameworkServlet重寫了service,doGet,doPost,doPut,doDelete,doOptions,doTrace方法(除了doHead的全部處理請求的方法),在service方法中增長了對PATCH類型請求的處理,其餘類型交給父類進行處理。doGet,doPost,doPut,doDelete都是本身處理,全部須要本身處理的請求都交給了processRequest方法進行統一處理。web

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

   HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
   if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
      processRequest(request, response);
   }
   else {
      super.service(request, response);
   }
}
@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);
}
@Override
protected final void doPut(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
   processRequest(request, response);
}
@Override
protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
   processRequest(request, response);
}

processRequest方法是FrameworkServlet中最核心的方法spring

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

   long startTime = System.currentTimeMillis();
   Throwable failureCause = null;
   //獲取LocaleContextHolder中原來保存的LocaleContext
   LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
   // 獲取當前請求的LocaleContext
   LocaleContext localeContext = buildLocaleContext(request);
   //獲取RequestContextHolder中原來保存的RequestAttributes
   RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
   //獲取當前請求的RequestAttributes
   ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
   //異步處理管理器設置攔截器,異步請求後面介紹
   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
   asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
   //將當前請求的LocaleContext,ServletRequestAttributes設置到LocaleContextHolder和RequestContextHolder
   initContextHolders(request, localeContext, requestAttributes);

   try {
      // 實際處理入口,模板方法在DispatcherServlet中具體實現
      doService(request, response);
   }
   catch (ServletException | 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()) {
         boolean isRequestDispatch = request.getDispatcherType().equals(DispatcherType.REQUEST);
         String dispatchType = request.getDispatcherType().name();
         if (failureCause != null) {
            if (!isRequestDispatch) {
               logger.debug("Unresolved failure from \"" + dispatchType + "\" dispatch: " + failureCause);
            }
            else if (logger.isTraceEnabled()) {
               logger.trace("Failed to complete request", failureCause);
            }
            else {
               logger.debug("Failed to complete request: " + failureCause);
            }
         }
         else {
            if (asyncManager.isConcurrentHandlingStarted()) {
               logger.debug("Exiting but response remains open for further handling");
            }
            else {
               int status = response.getStatus();
               if (!isRequestDispatch) {
                  logger.debug("Exiting from \"" + dispatchType + "\" dispatch (status " + status + ")");
               }
               else {
                  HttpStatus httpStatus = HttpStatus.resolve(status);
                  logger.debug("Completed " + (httpStatus != null ? httpStatus : status));
               }
            }
         }
      }
      //處理完後發佈ServletRequestHandledEvent消息
      publishRequestHandledEvent(request, response, startTime, failureCause);
   }
}

processRequest本身主要作了兩件事情:session

一、對LocaleContext和RequestAttributes的設置和恢復,LocaleContext裏面存放着本地化信息,好比zh-cn等。RequestAttributes是spring的一個藉口,經過它能夠get/set/removeAttrbute,根據scope參數判斷操做是request仍是session。具體實現是ServletRequestAttributes,看看它的數據結構和方法數據結構

private final HttpServletRequest request;

@Nullable
private HttpServletResponse response;

@Nullable
private volatile HttpSession session;

private final Map<String, Object> sessionAttributesToUpdate = new ConcurrentHashMap<>(1);
@Override
public Object getAttribute(String name, int scope) {
   if (scope == SCOPE_REQUEST) {
      if (!isRequestActive()) {
         throw new IllegalStateException(
               "Cannot ask for request attribute - request is not active anymore!");
      }
      return this.request.getAttribute(name);
   }
   else {
      HttpSession session = getSession(false);
      if (session != null) {
         try {
            Object value = session.getAttribute(name);
            if (value != null) {
               this.sessionAttributesToUpdate.put(name, value);
            }
            return value;
         }
         catch (IllegalStateException ex) {
            // Session invalidated - shouldn't usually happen.
         }
      }
      return null;
   }
}

@Override
public void setAttribute(String name, Object value, int scope) {
   if (scope == SCOPE_REQUEST) {
      if (!isRequestActive()) {
         throw new IllegalStateException(
               "Cannot set request attribute - request is not active anymore!");
      }
      this.request.setAttribute(name, value);
   }
   else {
      HttpSession session = obtainSession();
      this.sessionAttributesToUpdate.remove(name);
      session.setAttribute(name, value);
   }
}

@Override
public void removeAttribute(String name, int scope) {
   if (scope == SCOPE_REQUEST) {
      if (isRequestActive()) {
         this.request.removeAttribute(name);
         removeRequestDestructionCallback(name);
      }
   }
   else {
      HttpSession session = getSession(false);
      if (session != null) {
         this.sessionAttributesToUpdate.remove(name);
         try {
            session.removeAttribute(name);
            // Remove any registered destruction callback as well.
            session.removeAttribute(DESTRUCTION_CALLBACK_NAME_PREFIX + name);
         }
         catch (IllegalStateException ex) {
            // Session invalidated - shouldn't usually happen.
         }
      }
   }
}

2.處理完後發佈ServletRequestHandledEvent消息app

private void publishRequestHandledEvent(HttpServletRequest request, HttpServletResponse response,
      long startTime, @Nullable Throwable failureCause) {
   //publishEvents能夠在配置Servlet時設置,默認爲true
   if (this.publishEvents && this.webApplicationContext != null) {
      // 不管請求是否執行成功都會發布消息
      long processingTime = System.currentTimeMillis() - startTime;
      this.webApplicationContext.publishEvent(
            new ServletRequestHandledEvent(this,
                  request.getRequestURI(), request.getRemoteAddr(),
                  request.getMethod(), getServletConfig().getServletName(),
                  WebUtils.getSessionId(request), getUsernameForRequest(request),
                  processingTime, failureCause, response.getStatus()));
   }
}

寫一個簡單的日誌請求監聽器,只要繼承ApplicationListener監聽ServletRequestHandledEvent事件就好了。異步

@Component
public class ServletRequestHandleEventListener implements
        ApplicationListener<ServletRequestHandledEvent> {
    private static Logger logger = Logger.getLogger("request process log");

    public void onApplicationEvent(ServletRequestHandledEvent event){
        logger.info(event.getDescription());
    }
}

打印日誌以下async

FrameworkServlet的一個處理任務到這就結束了,接下來應該是DispatcherServlet的表演時刻ide

3.DispatcherServlet

DispatcherServlet是SpringMVC最核心的處理類,整個處理過程的頂層設計都在這個裏面,因此咱們要把這個類完全弄明白了。經過以前的分析咱們知道DispatcherServlet的入口是doService()工具

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
   logRequest(request);//日誌
   Map<String, Object> attributesSnapshot = null;//快照備份記錄用的
   
   if (WebUtils.isIncludeRequest(request)) {// 當include請求是對request的Attribute作快照備份
      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));
         }
      }
   }

   // 對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());

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

doService主要是對request設置了一些屬性,核心入口是doDispatch方法,doDispatch方法也很是的簡潔,最主要的任務是:

1根據request找到Handler;

2.根據Handler找到對應的HandlerAdapter;

3.用HandlerAdapter處理Handler,

4調用processDispatchResult方法處理上面處理後的結果

Handler:也就是處理器,它直接對應着Controller中@RequestMapping的全部方法均可以當作一個Handler,只要能夠實際處理請求的就能夠是Handler

HandlerMappering:用來查找Handler的,在SpringMVC中會有不少處理請求,每一個請求都須要一個Handler來處理,具體接收到一個請求後倒地使用哪一個Handler來處理呢,這就是HandlerMapping要作的事情。

HandlerAdapter:它是一個是配置用來調用handler處理事情,就是Handler用來幹活的工具

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;//封裝Model和View的容器
      Exception dispatchException = null;
      try {
         //檢測是否上傳請求
         processedRequest = checkMultipart(request);
         multipartRequestParsed = (processedRequest != request);
         // 根據request獲取Handler
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
         }
         // 根據Handler找到相應的Adapter
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
         // 處理get,head請求的last-modified
         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;
            }
         }
         // 執行相應Interceptor的preHandle
         if (!mappedHandler.applyPreHandle(processedRequest, response)) {
            return;
         }
         // 實際上調用處理程序,HandlerAdapter使用Handler處理請求
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
         //若是須要異步處理直接返回
         if (asyncManager.isConcurrentHandlingStarted()) {
            return;
         }
         //當view爲空時(好比返回值爲void),根據request設置默認的view
         applyDefaultViewName(processedRequest, mv);
         //執行相應Interceptor的postHandler
         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);
      }
      //處理返回結果。包括異常處理,渲染頁面,發出完成通知觸發Interceptor的afterCompletion
      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 {
         // 刪除上傳請求的資源
         if (multipartRequestParsed) {
            cleanupMultipart(processedRequest);
         }
      }
   }
}

一、首先檢查是否是上傳請求,若是是設置上傳的request,並設置標誌位爲true。

二、經過getHandler獲取Handler處理器鏈

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

三、接下來處理get,head請求的Last-Modified後一次調用相應Interceptor的preHandle

四、接下來最關鍵的就是讓HandlerAdapter使用Handler處理請求返回ModelAndView

五、繼續調用processDispatchResult方法處理返回的結果

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
      @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
      @Nullable Exception exception) throws Exception {

   boolean errorView = false;
   // 若是請求過程當中有異常則處理異常
   if (exception != null) {
      if (exception instanceof ModelAndViewDefiningException) {
         logger.debug("ModelAndViewDefiningException encountered", exception);
         mv = ((ModelAndViewDefiningException) exception).getModelAndView();
      }
      else {
         Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
         mv = processHandlerException(request, response, handler, exception);
         errorView = (mv != null);
      }
   }

   // 渲染頁面
   if (mv != null && !mv.wasCleared()) {
      render(mv, request, response);
      if (errorView) {
         WebUtils.clearErrorRequestAttributes(request);
      }
   }
   else {
      if (logger.isTraceEnabled()) {
         logger.trace("No view rendering, null ModelAndView returned.");
      }
   }

   if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
      // 異步請求直接返回
      return;
   }
   // 發出請求處理完成的通知,觸發Interceptor的afterCompletion
   if (mappedHandler != null) {
      mappedHandler.triggerAfterCompletion(request, response, null);
   }
}

六、最後就是觸發Interceptor的afterCompletion方法了。

    doDispatch方法就分析到這了,能夠看出它的頂層設計很簡潔,而後具體的處理交給不一樣的組件具體實現的。doDispatch處理流程圖以下(圖來源SpringMvc源碼分析與實戰)

總結:

三個Servlet的處理過程大體功能以下:

1.HttpServletBean:沒有參與實際請求的處理

2.FrameworkServlet:將不一樣類型的請求合併到processRequest方法統一處理,processRequest方法中作了三件事:

(1)調用doServeice模板方法具體處理請求

(2)將當前請求的LocalContext和ServletRequestAttributes設置到相應的Holder,並在請求處理完成後恢復。

(3)請求處理完成後發佈消息

3.DispatcherServlet:doService方法給request設置了一些屬性並將請求交給doDispatch方法處理,它使用了相應的組件完成處理請求,另外HanderMapping,Handler和HandlerAdapter這三個概念的含義以及他們之間的關係也很是重要

相關文章
相關標籤/搜索