美文网首页
springMVC(4) HandlerMapping实现之Si

springMVC(4) HandlerMapping实现之Si

作者: 谷和阿秋 | 来源:发表于2018-01-12 21:12 被阅读0次

HandlerMapping在springMVC中有多种实现,SimpleUrlHandlerMapping就是其中的实现方式之一。

配置文件

首先我们来看一下SimpleUrlHandlerMapping的配置方式,它有如下两种配置方式:

<beans ...>
 
    <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
       <property name="mappings">
        <props>
           <prop key="/welcome.htm">welcomeController</prop>
           <prop key="/*/welcome.htm">welcomeController</prop>
           <prop key="/helloGuest.htm">helloGuestController</prop>
         </props>
       </property>
    </bean>
    
    <bean id="welcomeController" 
        class="com.mkyong.common.controller.WelcomeController" />
        
    <bean id="helloGuestController" 
        class="com.mkyong.common.controller.HelloGuestController" />
        
</beans>
<beans ...>
    
    <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
       <property name="mappings">
        <value>
           /welcome.htm=welcomeController
           /*/welcome.htm=welcomeController
           /helloGuest.htm=helloGuestController
        </value>
       </property>
    </bean>
    
    <bean id="welcomeController" 
        class="com.mkyong.common.controller.WelcomeController" />
        
    <bean id="helloGuestController" 
        class="com.mkyong.common.controller.HelloGuestController" />
        
</beans>

SimpleUrlHandlerMapping类

public class SimpleUrlHandlerMapping extends AbstractUrlHandlerMapping {

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

   public void setMappings(Properties mappings) {
      CollectionUtils.mergePropertiesIntoMap(mappings, this.urlMap);
   }

   public void setUrlMap(Map<String, ?> urlMap) {
      this.urlMap.putAll(urlMap);
   }

   public Map<String, ?> getUrlMap() {
      return this.urlMap;
   }

   @Override
   public void initApplicationContext() throws BeansException {
      super.initApplicationContext();
      registerHandlers(this.urlMap);
   }

   protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
      if (urlMap.isEmpty()) {
         logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
      }
      else {
         urlMap.forEach((url, handler) -> {
            // 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);
         });
      }
   }

}

从源码中我们可以看出配置文件中的mappings最终都存储到了urlMap中了。

initApplicationContext()的作用就是在父类initApplicationContext的基础上再执行registerHandlers操作。

registerHandlers()作用是:1、将所有url全部前面补全/,2、对每个url及对应的handler执行registerHandler方法。

接下来我们继续看SimpleUrlHandlerMapping调用的registerHandler方法。

AbstractUrlHandlerMapping类

registerHandler是在SimpleUrlHandlerMapping的父类AbstractUrlHandlerMapping中定义的。

protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
   Assert.notNull(urlPath, "URL path must not be null");
   Assert.notNull(handler, "Handler object must not be null");
   Object resolvedHandler = handler;

   // Eagerly resolve handler if referencing singleton via name.
   if (!this.lazyInitHandlers && handler instanceof String) {
      String handlerName = (String) handler;
      ApplicationContext applicationContext = obtainApplicationContext();
      if (applicationContext.isSingleton(handlerName)) {
         resolvedHandler = applicationContext.getBean(handlerName);
      }
   }

   Object mappedHandler = this.handlerMap.get(urlPath);
   if (mappedHandler != null) {
      if (mappedHandler != resolvedHandler) {
         throw new IllegalStateException(
               "Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
               "]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
      }
   }
   else {
      if (urlPath.equals("/")) {
         if (logger.isInfoEnabled()) {
            logger.info("Root mapping to " + getHandlerDescription(handler));
         }
         setRootHandler(resolvedHandler);
      }
      else if (urlPath.equals("/*")) {
         if (logger.isInfoEnabled()) {
            logger.info("Default mapping to " + getHandlerDescription(handler));
         }
         setDefaultHandler(resolvedHandler);
      }
      else {
         this.handlerMap.put(urlPath, resolvedHandler);
         if (logger.isInfoEnabled()) {
            logger.info("Mapped URL path [" + urlPath + "] onto " + getHandlerDescription(handler));
         }
      }
   }
}

这部分代码主要做了如下一些事。

  1. 对于非懒加载且单例的方式进行了处理。
  2. 判断之前是否已经存在过处理该url的handler,对于冲突会抛出异常。
  3. 如果url为"/",则设置为rootHandler;如果url为"/*",则设置为defaultHandler;否则作为普通handler进行注册。

关于SimpleUrlHandlerMapping的register部分先到这里,接下来我们来看看里面我们最需要关注的getHandlerInternal函数,这个函数也是在AbstractUrlHandlerMapping类实现的。

protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
   String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
   Object handler = lookupHandler(lookupPath, request);
   if (handler == null) {
      // We need to care for the default handler directly, since we need to
      // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
      Object rawHandler = null;
      if ("/".equals(lookupPath)) {
         rawHandler = getRootHandler();
      }
      if (rawHandler == null) {
         rawHandler = getDefaultHandler();
      }
      if (rawHandler != null) {
         // Bean name or resolved handler?
         if (rawHandler instanceof String) {
            String handlerName = (String) rawHandler;
            rawHandler = obtainApplicationContext().getBean(handlerName);
         }
         validateHandler(rawHandler, request);
         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;
}

从这个函数中我们可以发现整个函数的匹配过程,首先对request进行一次普通匹配,若普通匹配不到,则判断是否为根路径,否则匹配默认handler。

我们会发现我们关于普通匹配还没有进行深入地剖析。

普通匹配的实现为lookupHandler函数。

protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
   // Direct match?
   Object handler = this.handlerMap.get(urlPath);
   if (handler != null) {
      // Bean name or resolved handler?
      if (handler instanceof String) {
         String handlerName = (String) handler;
         handler = obtainApplicationContext().getBean(handlerName);
      }
      validateHandler(handler, request);
      return buildPathExposingHandler(handler, urlPath, urlPath, null);
   }

   // Pattern match?
   List<String> matchingPatterns = new ArrayList<>();
   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 +"/");
         }
      }
   }

   String bestMatch = 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);
      }
      bestMatch = matchingPatterns.get(0);
   }
   if (bestMatch != null) {
      handler = this.handlerMap.get(bestMatch);
      if (handler == null) {
         if (bestMatch.endsWith("/")) {
            handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
         }
         if (handler == null) {
            throw new IllegalStateException(
                  "Could not find handler for best pattern match [" + bestMatch + "]");
         }
      }
      // Bean name or resolved handler?
      if (handler instanceof String) {
         String handlerName = (String) handler;
         handler = obtainApplicationContext().getBean(handlerName);
      }
      validateHandler(handler, request);
      String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);

      // There might be multiple 'best patterns', let's make sure we have the correct URI template variables
      // for all of them
      Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
      for (String matchingPattern : matchingPatterns) {
         if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
            Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
            Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
            uriTemplateVariables.putAll(decodedVars);
         }
      }
      if (logger.isDebugEnabled()) {
         logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
      }
      return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
   }

   // No handler found...
   return null;
}

从这函数中我们可以看出对于普通匹配,会优先判断是否由精确匹配,若无法精确匹配再进行模式串匹配。模式串匹配的优先级按照AntPattern的规则,取出其中的最优匹配。

总结

SimpleUrlHandlerMapping通过LinkedHashMap的方式来存储url与handler的映射关系。

整个匹配流程如下:

  1. 进行精确路径的匹配
  2. 进行模式串匹配,按照AntPattern的规则找出其中最优的匹配
  3. 进行根路径匹配
  4. 使用默认handler

相关文章

网友评论

      本文标题:springMVC(4) HandlerMapping实现之Si

      本文链接:https://www.haomeiwen.com/subject/pkkqoxtx.html