天天看點

SpringMVC源碼分析(一)前言源碼分析總結

前言

      上一節,我們隻是從總體上分析了springMVC的請求處理流程,但針對每一步的細節,具體還有哪些操作并沒有說明,這一節,就請求過程中的很一步處理給出相應的分析說明。

源碼分析

      假設在伺服器部署了一個工程project,其中有一個業務是根據使用者ID擷取使用者的詳細資訊,GET請求的URL是:http://localhost:8080/project/userController/getUserInfo?id=10001(不考慮有其它伺服器轉發),當伺服器接收到浏覽器發送的這個請求後,按httpServlet的規範,首先會找到DispatcherServlet中的doGet方法,發現此方法在DispatcherServlet的父類FrameworkServlet中

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

   processRequest(request, response);
}
           

發現它直接委托給了processRequest去執行(其它的方法,如:doPost,doPut等也都是委托給這個方法去處理),我們來看看processRequest方法内容

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

   long startTime = System.currentTimeMillis();
   Throwable failureCause = null;
   //先取上一次的本地上下文,其實就是取Locale的資訊,比如:國家,語言等等
   //主要是緩存,以便後面的恢複
   LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
   //建立一個LocaleContext
   LocaleContext localeContext = buildLocaleContext(request);
    //取上一個請求的RequestAttributes
   RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
   //建立一個RequestAttributes
   ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
   //異步管理類,這個是内部使用的類,在這裡不做分析
   WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
   asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
    /**初始化ContextHolder,其實就是把目前的requestAttributes放到
    ThreadLocal中,友善在一些沒有request作為參數的方法中,
    可以直接使用 RequestContextHolder.currentRequestAttributes()
    取到目前的request或response對象
    */
   initContextHolders(request, localeContext, requestAttributes);

   try {
       //重點,真正的處理交給了這個方法去執行
      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,發現它是一個FrameworkServlet的抽象方法,也就是說它希望子類DispatcherServlet去實作具體的處理過程

protected abstract void doService(HttpServletRequest request,
                                HttpServletResponse response) throws Exception;

           

在DispatcherServlet中的doService方法實作

@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
   if (logger.isDebugEnabled()) {
      String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
      logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
            " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
   }

   // Keep a snapshot of the request attributes in case of an include,
   // to be able to restore the original attributes after the include.
   //如果此次請求中包含一個uri請求,就儲存request的一個快照
   //友善恢複request的原始屬性
   Map<String, Object> attributesSnapshot = null;
   if (WebUtils.isIncludeRequest(request)) {
      attributesSnapshot = new HashMap<String, Object>();
      Enumeration<?> attrNames = request.getAttributeNames();
      while (attrNames.hasMoreElements()) {
         String attrName = (String) attrNames.nextElement();
         if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
            attributesSnapshot.put(attrName, request.getAttribute(attrName));
         }
      }
   }

   // Make framework objects available to handlers and view objects.
   //設定一些全局參數給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方法,此時才是直接的開始

/**
 * 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 {
          //檢查是否是Multipart請求,比如:檔案上傳就是一個Multipart請求
          //否則傳回原來的request
         processedRequest = checkMultipart(request);
         multipartRequestParsed = (processedRequest != request);

         // Determine handler for the current request.
         //根據request去handlerMapping映射中到對應的HandlerExecutionChain
         //
         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.
         //交給擴充卡去解析參數等等并執行對應的方法,傳回
         //ModelAndView
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

         if (asyncManager.isConcurrentHandlingStarted()) {
            return;
         }
         //如果沒有view就給它一個預設的
         applyDefaultViewName(processedRequest, mv);
         //執行攔截器的postHandle
         mappedHandler.applyPostHandle(processedRequest, response, mv);
      }
      catch (Exception ex) {
         dispatchException = ex;
      }
      //處理傳回結果,這裡就是把Model填充到view中
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
   catch (Exception ex) {
      triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
   }
   catch (Error err) {
      triggerAfterCompletionWithError(processedRequest, response, mappedHandler, 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);
         }
      }
   }
}
           

checkMultipart處理的内容很簡單,上面已經說明,我們來看看 getHandler(processedRequest)做了哪些事件?主要是周遊handlerMappings找到比對的執行鍊

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   for (HandlerMapping hm : this.handlerMappings) {
      if (logger.isTraceEnabled()) {
         logger.trace(
               "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
      }
      HandlerExecutionChain handler = hm.getHandler(request);
      if (handler != null) {
         return handler;
      }
   }
   return null;
}
           

HandlerMapping中的getHandler方法

/**
 * Look up a handler for the given request, falling back to the default
 * handler if no specific one is found.
 * @param request current HTTP request
 * @return the corresponding handler instance, or the default handler
 * @see #getHandlerInternal
 */
@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   //委托給此方法去查找handler
   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);
   }

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

進入到AbstractHandlerMethodMapping的getHandlerInternal方法,先從request擷取到lookupPath,相當于上面的(userControoler/getUserInfo)

/**
 * Look up a handler method for the given request.
 */
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
   String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
   if (logger.isDebugEnabled()) {
      logger.debug("Looking up handler method for path " + lookupPath);
   }
   /**
   這裡的mappingRegistry,其實就是一個映射關系注冊中心,維護所有mapping與handlerMethod的映射關系
   為了提供并發通路,每次都是顯示的擷取鎖,擷取後再顯示的釋放鎖
   
   */
   this.mappingRegistry.acquireReadLock();
   try {
       //擷取handlerMethod,下面進行分析
      HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
      if (logger.isDebugEnabled()) {
         if (handlerMethod != null) {
            logger.debug("Returning handler method [" + handlerMethod + "]");
         }
         else {
            logger.debug("Did not find handler method for [" + lookupPath + "]");
         }
      }
      return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
   }
   finally {
      this.mappingRegistry.releaseReadLock();
   }
}
           
/**
 * Look up the best-matching handler method for the current request.
 * If multiple matches are found, the best match is selected.
 * @param lookupPath mapping lookup path within the current servlet mapping
 * @param request the current request
 * @return the best-matching handler method, or {@code null} if no match
 * @see #handleMatch(Object, String, HttpServletRequest)
 * @see #handleNoMatch(Set, String, HttpServletRequest)
 */
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
   List<Match> matches = new ArrayList<Match>();
   //從mappingRegistry中的urllookup中擷取RequestMappingInfo集合
   //其實,每一個requestMapping都會被封裝成RequestMappingInfo
   List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
   if (directPathMatches != null) {
       //周遊所有的比對的mappings(directPathMatches)
      addMatchingMappings(directPathMatches, matches, request);
   }
   if (matches.isEmpty()) {
      // No choice but to go through all mappings...
      addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
   }

   if (!matches.isEmpty()) {
       //排序找到最比對的那個,也就是第一個
      Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
      Collections.sort(matches, comparator);
      if (logger.isTraceEnabled()) {
         logger.trace("Found " + matches.size() + " matching mapping(s) for [" +
               lookupPath + "] : " + matches);
      }
      //取最比對的match
      Match bestMatch = matches.get(0);
      if (matches.size() > 1) {
         if (CorsUtils.isPreFlightRequest(request)) {
            return PREFLIGHT_AMBIGUOUS_MATCH;
         }
         //取第二比對的
         Match secondBestMatch = matches.get(1);
         //如果兩個相同,則springmvc也不知道哪個是使用者需要的方法了
         //是以,不能存在相同的路徑對應兩個方法
         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);
      //傳回最佳比對的mapping對應的HandlerMethod
      return bestMatch.handlerMethod;
   }
   else {
      return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
   }
}
           
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
   for (T mapping : mappings) {
       //找到比對的mapping加入到matchs集合中
      T match = getMatchingMapping(mapping, request);
      if (match != null) {
         matches.add(new Match(match, this.mappingRegistry.getMappings().get(mapping)));
      }
   }
}
           

Match類的結構,它其實是AbstractHandlerMethodMapping的内部類

/**
 * A thin wrapper around a matched HandlerMethod and its mapping, for the purpose of
 * comparing the best match with a comparator in the context of the current request.
 */
private class Match {
//Match類是AbstractHandlerMethodMapping的内部類
   private final T mapping;

   private final HandlerMethod handlerMethod;

   public Match(T mapping, HandlerMethod handlerMethod) {
      this.mapping = mapping;
      this.handlerMethod = handlerMethod;
   }

   @Override
   public String toString() {
      return this.mapping.toString();
   }
}
           

再回到AbstractHandlerMethodMapping的getHandlerInternal方法中,接下來是判斷handlerMethod是否為空,如果不為空則調用createWithResolvedBean()方法,這個方法的邏輯是如果目前handlerMethod中的bean隻是名稱時,那麼通過beanFactory獲得bean的執行個體,再次封裝成HadnerMethod對象傳回。這裡的bean是指方法所在的類對象,例如:上述中的userController中的getUserInfo方法,getUserInfo就是handlerMethod而userController就是上面所說的bean。

return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
           

再回到AbstractHandlerMapping的getHandler(HttpServletRequest request)方法,接下來就是執行

HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
           

擷取執行鍊,進入此方法,

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
   //先判斷是否是執行鍊對象,如果不是就把handler封裝成一個執行鍊
   HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
         (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
//周遊所有的攔截器加入到執行鍊中
   String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
   for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
      if (interceptor instanceof MappedInterceptor) {
         MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
         if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
            chain.addInterceptor(mappedInterceptor.getInterceptor());
         }
      }
      else {
         chain.addInterceptor(interceptor);
      }
   }
   return chain;
}
           

到此時,我們已經擷取到了HandlerExecutionChain執行鍊對象,傳回到DispatcherServlet中。接下來就是需要擷取執行擴充卡(管家)

總結

       重新回顧一下,在DispatcherServlet的doDispatch方法中,第一步是判斷是否是Multipart請求,第二步就是擷取執行連結對象mappedHandler。分析到此也隻是完成這個方法的第二步,即通過請求的url找到對應的handlerMethod和bean對象,近而取得了執行鍊對象。為下一步的執行做準備。

繼續閱讀