JavaWeb學習之——Spring篇之HandlerMapping

1.HandlerMapping

HandlerMapping接口只有一個方法app

HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

就是根據Reuqest返回HandlerExecutionChain,看名字就知道是一個處理鏈,包含多個Interceptor和一個Handler。cors

2.AbstractHandlerMapping

AbstractHandlerMapping是HanlderMapping的默認實現類。是一個模板方法(Spring 底層大代碼大量使用模板方法)。ide

2.1 初始化interceptor

@Override
protected void initApplicationContext() throws BeansException {
   extendInterceptors(this.interceptors);
   detectMappedInterceptors(this.adaptedInterceptors);
   initInterceptors();
}

deleteMappedIntercptors方法的左右就是把ApplicationContext中全部類型爲MappedInterceptor的bean加入到adaptedIntercptors這個成員變量中函數

protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
   mappedInterceptors.addAll(
         BeanFactoryUtils.beansOfTypeIncludingAncestors(
               getApplicationContext(), MappedInterceptor.class, true, false).values());
}

initInterceptors()函數的做用就是把成員變量interceptors中的interceptor加入到adaptedInterceptors成員變量中去,若是是WebRequestInterceptor則轉換爲對應的adapter類,代碼以下ui

protected void initInterceptors() {
   if (!this.interceptors.isEmpty()) {
      for (int i = 0; i < this.interceptors.size(); i++) {
         Object interceptor = this.interceptors.get(i);
         if (interceptor == null) {
            throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
         }
         this.adaptedInterceptors.add(adaptInterceptor(interceptor));
      }
   }
}
protected HandlerInterceptor adaptInterceptor(Object interceptor) {
   if (interceptor instanceof HandlerInterceptor) {
      return (HandlerInterceptor) interceptor;
   }
   else if (interceptor instanceof WebRequestInterceptor) {
      return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor);
   }
   else {
      throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());
   }
}

2.2 實現Handler接口

@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   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;
}

主要作了2件事:this

  • 1.獲取request對應的Handler,獲取handler是經過getHandlerInternal方法,這個由子類去實現 
  • 2.找出對應的Interceptors返回HandlerExecutionChain,從根據request找出合適的Interceptors加入其中(例如MappedInterceptor是根據url地址來判斷是否加入)。

AbstractHandleMapping是一個模板方法,子類須要實現getHandlerInternal。下面介紹2個主要實現類AbstractUrlHandleMapping和AbstractHandleMethodMapping。url

3. AbstractUrlHandleMapping

3.1 getHandlerInternal

從名字能夠猜到,AbstractUrlHandleMapping是經過url來匹配對應的Handler。AbstractUrlHandleMapping依然是一個抽象類。getHandlerInternal代碼:spa

/**
 * 根據request的url查詢對應的handler
 */
@Override
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
   //查找url地址,有多是servlet的url,也有多是Spring Mvc定義的url
   String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
   //經過url查找對應的Handler
   Object handler = lookupHandler(lookupPath, request);
   //若是找不到對應的handler
   if (handler == null) {
      // 特殊狀況,若是查詢地址爲"/",則判斷是否用rootHandler,若是沒有就設置爲defaultHandler
      Object rawHandler = null;
      if ("/".equals(lookupPath)) {
         rawHandler = getRootHandler();
      }
      if (rawHandler == null) {
         rawHandler = getDefaultHandler();
      }
      if (rawHandler != null) {
         // 若是是字符串,則從ApplicationConetxt中獲取對應的bean
         if (rawHandler instanceof String) {
            String handlerName = (String) rawHandler;
            rawHandler = getApplicationContext().getBean(handlerName);
         }
         //模板方法,驗證Handler
         validateHandler(rawHandler, request);
         //返回HandlerExecutionChain類型,會註冊2個interceptor
         //PathExposingHandlerInterceptor和UriTemplateVariablesHandlerInterceptor
         handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
      }
   }
   if (handler != null && logger.isDebugEnabled()) {
      logger.debug("Mapping [" + lookupPath + "] to " + handler);
   }
   else if (handler == null && logger.isTraceEnabled()) {
      logger.trace("No handler mapping found for [" + lookupPath + "]");
   }
   return handler;
}

主要作了一下幾件事:debug

  1. 根據request生成查詢handler的URL
  2. 根據url獲取對應的handler
  3. 若是handler沒有在map中查到,則使用默認的handler。

3.2 lookupHandler

方法目的就是根據url 查找對應的Handler,可是在url匹配的時候每每會有"/test/*"或者Path變量「/test/{id}」這樣的狀況,這樣就沒法經過url地址直接從map中獲取對應的handler。code

protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
   // 可否直接找到
   Object handler = this.handlerMap.get(urlPath);
   if (handler != null) {
      // 若是是字符串從ApplicationContext中直接獲取
      if (handler instanceof String) {
         String handlerName = (String) handler;
         handler = getApplicationContext().getBean(handlerName);
      }
      // 模板方法,驗證handler
      validateHandler(handler, request);
      //返回HandlerExecutionChain類型,會註冊2個interceptor
      //PathExposingHandlerInterceptor和UriTemplateVariablesHandlerInterceptor
      return buildPathExposingHandler(handler, urlPath, urlPath, null);
   }
   // 模板匹配
   List<String> matchingPatterns = new ArrayList<String>();
   for (String registeredPattern : this.handlerMap.keySet()) {
      if (getPathMatcher().match(registeredPattern, urlPath)) {
         matchingPatterns.add(registeredPattern);
      }
      else if (useTrailingSlashMatch()) {
         if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
            matchingPatterns.add(registeredPattern +"/");
         }
      }
   }
   //根據order找出優先級坐高的
   String bestPatternMatch = null;
   Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
   if (!matchingPatterns.isEmpty()) {
      Collections.sort(matchingPatterns, patternComparator);
      if (logger.isDebugEnabled()) {
         logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
      }
      bestPatternMatch = matchingPatterns.get(0);
   }
   if (bestPatternMatch != null) {
      handler = this.handlerMap.get(bestPatternMatch);
      if (handler == null) {
         Assert.isTrue(bestPatternMatch.endsWith("/"));
         handler = this.handlerMap.get(bestPatternMatch.substring(0, bestPatternMatch.length() - 1));
      }
      
      if (handler instanceof String) {
         String handlerName = (String) handler;
         handler = getApplicationContext().getBean(handlerName);
      }
      validateHandler(handler, request);
      String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);

      // 這裏是處理當有多個url和最優的path是同一優先級的,那麼從裏面獲取地址變量,加入到uriTemplateVariables中
      Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
      for (String matchingPattern : matchingPatterns) {
         if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) {
            Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
            Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
            uriTemplateVariables.putAll(decodedVars);
         }
      }
      return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
   }
   //找不到對應的handler
   return null;
}

這個函數主要完成了幾下幾件事

  1. url能直接從map中獲取的則直接獲取
  2. 不然經過路徑pattern去匹配url
  3. 從全部匹配到handler查找出優先級最高的
  4. 若是有多個同最優先級的,則查看裏面是否有pathVariable
  5. 找到任意的Handler則加入2個特殊的interceptor

3.3 buildPathExposingHandler 

上面全部的能成功找到對應的handler後,都會調用這個方法。這兒方法就是在handler的基礎上,加入2個interceptor,返回一個HandlerExecutionChain

protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
      String pathWithinMapping, Map<String, String> uriTemplateVariables) {

   HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
   chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
   if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
      chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
   }
   return chain;
}

這2個interceptor的做用就是當前匹配的url、匹配條件和url模板參數設置到request的attribute裏面

3.4 Map初始化

上面一直說的經過url從map中獲取對應的Handler,這個map是成員變量

private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();

這個map的初始化是在registerHandler方法,這個方法比較功能比較簡單,參數分別是url和handler,而後把url和handler放入map中,再作一些重複檢查和特殊handler(roothandler)的設置。url和handler從哪裏來,則又是子類提供處理具體的邏輯來調用這個方法。

到這裏,關於怎麼經過url來獲取handler已經解決了,下面就是url和handler是怎麼註冊到handlermap中的,其中大部分子類都被@Deprecated標記,

3.5 SimpleUrlHandlerMapping

這個類很是簡單,就是給定參數map,註冊到handlermap中

protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
   if (urlMap.isEmpty()) {
      logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
   }
   else {
      for (Map.Entry<String, Object> entry : urlMap.entrySet()) {
         String url = entry.getKey();
         Object handler = entry.getValue();
         // Prepend with slash if not already present.
         if (!url.startsWith("/")) {
            url = "/" + url;
         }
         // Remove whitespace from handler bean name.
         if (handler instanceof String) {
            handler = ((String) handler).trim();
         }
         registerHandler(url, handler);
      }
   }
}

4. AbstractHandlerMethodMapping

如今來看看AbstractHandlerMapping的另一個繼承 AbstractHandlerMethodMapping

AbstractHandlerMethodMapping從名字就能夠看出,是把一個方法做爲一個handler。而且繼承體系也十分清晰。分析的順序和AbstractUrlHandleMapping相似,首先仍是先分析一下getHandlerInternal這個方法

@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);
   }
   this.mappingRegistry.acquireReadLock();
   try {
      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();
   }
}

看起來和AbstractUrlHandleMapping相似,只不過AbstractUrlHandleMapping中的handler是一個bean,而AbstractHandlerMethodMapping的handler是一個Method

初始化方法initHandlerMethods:

protected void initHandlerMethods() {
   //獲取全部beanName
   String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
         BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
         getApplicationContext().getBeanNamesForType(Object.class));

   for (String beanName : beanNames) {
      //beanName是以scopedTarget.開頭的,則不用戶Handler Method
      if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
         Class<?> beanType = null;
         try {
            beanType = getApplicationContext().getType(beanName);
         }
         catch (Throwable ex) {
            ...
         }
         // isHandler模板方法,由子類去判斷改bean是否註冊
         if (beanType != null && isHandler(beanType)) {
            //註冊到HandlerMethod
            detectHandlerMethods(beanName);
         }
      }
   }
   //初始化HandlerMethod
   handlerMethodsInitialized(getHandlerMethods());
}

detectHandlerMethods,就是註冊handlerMethod。具體說明看註解

protected void detectHandlerMethods(final Object handler) {
   Class<?> handlerType = (handler instanceof String ?
         getApplicationContext().getType((String) handler) : handler.getClass());
   final Class<?> userType = ClassUtils.getUserClass(handlerType);
  
   //查找該handler中的method及其對應的條件(參照@RequestMapping中的參數)
   Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
         new MethodIntrospector.MetadataLookup<T>() {
            @Override
            public T inspect(Method method) {
               return getMappingForMethod(method, userType);
            }
         });
   for (Map.Entry<Method, T> entry : methods.entrySet()) {
      Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
      T mapping = entry.getValue();
      registerHandlerMethod(handler, invocableMethod, mapping);
   }
}
  1.  isHandler 是一個模板方法,由子類去判斷改bean是不是一個handler。
  2. getMappingForMethod也是一個模板方法,判斷一個類中Method方法的相關條件信息
  3. detectHandlerMethods是在傳入的beanName對應的bean中查找handlerMethod,而後註冊
  4. handlerMethodsInitialized 是一個預留方法,裏面沒有作任何事,若是咱們自定義一個HandlerMapping有須要的話能夠重寫

4.1 RequestMappingHandlerMapping

RequestMappingHandlerMapping繼承AbstractHandlerMethodMapping,首先看兩個模板方法

isHandler,就是判斷是否有Controller或者RequestMapping註解。

@Override
protected boolean isHandler(Class<?> beanType) {
   return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
         AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

getMappingForMethod這個方法就是根據傳入的函數,返回一個RequestMappingInfo (也就是@RequestMapping中的參數信息)

@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
   RequestMappingInfo info = createRequestMappingInfo(method);
   if (info != null) {
      RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
      if (typeInfo != null) {
         info = typeInfo.combine(info);
      }
   }
   return info;
}

這樣,AbstractHandlerMethodMapping的就能經過一個url能夠找出對應MethodHandler,

相關文章
相關標籤/搜索