HandlerMapping的子类AbstractHandlerMapping

是一个抽象类基类,是SpringMVC中两大分支的父类。AbstractHandlerMapping支持排序,默认handler,处理拦截器,包括带路径映射的拦截器。

下图为HandlerMapping整个链路的类图:

HandlerMapping类图
HandlerMapping类图

我们知道HandlerMapping的唯一作用就是根据请求找到handler,所以这个接口只有一个方法,就是getHandler()

接下去看下真正的实现基类AbstractHandlerMapping

AbstractHandlerMapping

同样,我们要知道Spring很喜欢使用接口+抽象类的方式设计出整体流程,再通过模板方法由子类具体实现。HandlerMapping找到的handler不关只是handler还有Interceptor。

init

AbstractHandlerMapping继承了 WebApplicationObjectSupport,那么你可能就想知道是什么时候实现了初始化,答案是我们上文提到在初始化DispatcherServlet的过程中被初始化的,即在FrameworkServlet#configureAndRefreshWebApplicationContextrefresh方法中被初始化,这其中又是层层调用,可以idea断点进去看看。

//FrameworkServlet#configureAndRefreshWebApplicationContext
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
    ....省略.....
        wac.refresh();
    }

回到AbstractHandlerMapping#initApplicationContext

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

extendInterceptors为模板方法,主要是用于给子类提供一个添加(修改)Interceptors的入口,目前SpringMVC没有使用。

detectMappedInterceptors是一个模板方法,用于将 Spring MVC容器及父容器中的所有 Mappedlnter­ceptor 类型的 Bean 添加到 mappedlnterceptors 属性:

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

initlnterceptors 方法的作用是初始化 Interceptor, 具体内容其实是将 interceptors 属性根据对象类型添加到adaptedlnterceptors,这时候你又要有疑问了,this.interceptors是什么时候被塞进去的哇!看下这个方法上面的注释,哦!原来这个类里有方法setInterceptors,通过查看调用关系,大致可以知道是在初始化bean的时候被调用了!

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

这interceptor只支持三种:HandlerInterceptor, WebRequestInterceptorMappedInterceptor

HandlerInterceptor, WebRequestInterceptor区别:

二者都可以用于拦截请求,但是你仔细观察这两个类:

//HandlerInterceptor
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception;
void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)throws Exception;
void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception;

//WebRequestInterceptor
void preHandle(WebRequest request) throws Exception;
void postHandle(WebRequest request, ModelMap model) throws Exception;
void afterCompletion(WebRequest request, Exception ex) throws Exception;

WebRequestInterceptor是没有返回值的,不能用于请求阻断,通常用于上下文的准备,且WebRequestInterceptor使用的入参是WebRequestWebRequestInterceptor接口故意简化,以使通用请求拦截器的依赖关系尽可能地小。如果需要更改响应,则应使用HandlerIntercepter或Filters。

参见:WebRequestInterceptor与MappedInterceptor区别

MappedInterceptor的是在InterceptorRegistration#getInterceptor方法里通过判断配置的拦截器是否带pattern:

//InterceptorRegistration#getInterceptor
protected Object getInterceptor() {
   if (this.includePatterns.isEmpty() && this.excludePatterns.isEmpty()) {
      return this.interceptor;
   }

   String[] include = StringUtils.toStringArray(this.includePatterns);
   String[] exclude = StringUtils.toStringArray(this.excludePatterns);
   MappedInterceptor mappedInterceptor = new MappedInterceptor(include, exclude, this.interceptor);
   if (this.pathMatcher != null) {
      mappedInterceptor.setPathMatcher(this.pathMatcher);
   }
   return mappedInterceptor;
}

在当前类中还定义了一个拦截器List:

List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();

顾名思义,适配拦截器,在赋值进去的过程中会将如WebRequestInterceptor拦截器调整成适配类型塞进去便于将他们归集起来。

getHandler

这个方法的调用在前面分析DispatcherServlet的时候提到过DispatcherServlet#doDispatch里会被调用从而获取处理器Handler以及拦截器。

getHandler 方法的实现共分两部分, getHandlerExecutionChain 之前是找 Handler, getHandlerExecutionChain 方法用于添加拦截器。

Handler 的过程是这样的:
I) 通过 getHandlerlntemal (request) 方法获取, 这是个模板方法, 留给子类具体实现(这也是其子类主要做的事情)

2) 如果没有获取到则使用默认的 Handler, 默认的 Handler 保存存于AbstractHandler­Mapping 的一个 Object 类型的属性 defaultHandler 中, 可以在配置 HandlerMapping 时进行配置, 也可以在子类中进行设置,当然默认是null。

3) 如果找到的 Handler 是 String 类型, 则以它为名到 Spring MVC 的容器里查找相应的

@Override
@Nullable
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 = obtainApplicationContext().getBean(handlerName);
   }

   HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

   if (logger.isTraceEnabled()) {
      logger.trace("Mapped to " + handler);
   }
   else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
      logger.debug("Mapped to " + executionChain.getHandler());
   }

//跨域相关可以先跳过
   if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
      CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
      CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
      config = (config != null ? config.combine(handlerConfig) : handlerConfig);
      executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
   }

   return executionChain;
}

再看下getHandlerExecutionChain,使用 handler 创建出 HandlerExecutionChain 类型的变量, 然后将adaptedInterceptors和符合要求的 mappedlnterceptors 添加进去, 最后将其返回。

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
   HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
         (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

   String lookupPath = this.urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
   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;
}

AbstractUrlHandlerMapping系列

显而易见,这个系列是通过url来进行匹配合适的Handler。此系列大致原理是将 url 与对应的 Handler 保存在一个 Map 中, 在 getHandlerlnternal 方法中使用 url 从 Map 中获取 Handler, AbstractUrlHandlerMapping 中实现了具体用 url 从 Map 中获取 Handler 的过程而 Map的初始化则交给了具体的子孙类去完成。此处的Map即AbstractUrlHandlerMapping中定义的handlerMap。

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

那先来看下是怎么初始化这个Map的。

AbstractUrlHandlerMapping中是通过registerHandler这个方法来构建handlerMap的。AbstractUrlHandlerMapping提供了两个registerHandler方法

第一个registerHandler是将多个url注册到一个处理器。处理器用的是String类型的beanName,可以通过beanName到容器中去找到真正的处理器Bean。具体处理就是通过遍历所有的url,然后再通过调用第二个registerHandler将handler注册到handlerMap中。

protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException {
   Assert.notNull(urlPaths, "URL path array must not be null");
   for (String urlPath : urlPaths) {
      registerHandler(urlPath, beanName);
   }
}

第二个 registerHandler 方法:

1) 首先看 Map 里原来有没有传入的 url, 如果 没有就 put 进去, 如果有就看一下原来保存的和现在要注册的 Handler 是不是同一个, 如果不是同一个就有问题了, 总不能相同的 url 有两个不同的 Handler 吧(这个系列只根据 url 查找 Handler),这时就得抛异常;

2) 往 Map 里放的时候还需要看一下 url 是不是处理"/"或者"/*"' 如果是就不往 Map 里放了, 而是分别设置到 rootHandlerdefaultHandler

protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
  
   Object resolvedHandler = handler;

   //如果的handler是string类型,并且没有设置lazyInitHandlers
   //则从SpringMVC容器获取handler
   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.isTraceEnabled()) {
            logger.trace("Root mapping to " + getHandlerDescription(handler));
         }
         setRootHandler(resolvedHandler);
      }
      else if (urlPath.equals("/*")) {
         if (logger.isTraceEnabled()) {
            logger.trace("Default mapping to " + getHandlerDescription(handler));
         }
         setDefaultHandler(resolvedHandler);
      }
      else {
         this.handlerMap.put(urlPath, resolvedHandler);
         if (logger.isTraceEnabled()) {
            logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
         }
      }
   }
}

了解完Map的初始化流程之后我们回到getHandlerInternal承接上文我们知道这个方法在父类是模板方法,子类进行实现,现在来看看里面做了什么

protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
   String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
   request.setAttribute(LOOKUP_PATH, lookupPath);
   Object handler = lookupHandler(lookupPath, request);
   if (handler == null) {
      // 保存找到的原始handler
      Object rawHandler = null;
      if ("/".equals(lookupPath)) {
         rawHandler = getRootHandler();
      }
      if (rawHandler == null) {
         rawHandler = getDefaultHandler();
      }
      if (rawHandler != null) {
         // String类型的直接在容器里查找bean
         if (rawHandler instanceof String) {
            String handlerName = (String) rawHandler;
            rawHandler = obtainApplicationContext().getBean(handlerName);
         }
         //校验方法,模板方法,当前MVC没有使用
         validateHandler(rawHandler, request);
         handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
      }
   }
   return handler;
}

整个流程通俗易懂,我们主要看lookupHandler以及buildPathExposingHandler

lookupHandler 方法用于使用 lookupPath 从 Map 中查找 Handler, 不过很多时候并不能直接从 Map 中 get 到 因为很多 Handler 都是用了 Pattern 的匹配模式, 如 "/show/article/*",这里的星号*可以代表任意内容而不是真正匹配 url 中的星号, 如果Pattern 中包含 PathVariable也不能直接从 Map 中获取到。 另外一个 url 还可能跟多个 Pattern 相匹配, 这时还需要选择其中最优的

@Nullable
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
   // 直接匹配
   Object handler = this.handlerMap.get(urlPath);
   if (handler != null) {
      // String类型从容器获取
      if (handler instanceof String) {
         String handlerName = (String) handler;
         handler = obtainApplicationContext().getBean(handlerName);
      }
      validateHandler(handler, request);
      return buildPathExposingHandler(handler, urlPath, urlPath, null);
   }

   // pattern匹配,比如使用带*的模式与url匹配
   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()) {
      matchingPatterns.sort(patternComparator);
      if (logger.isTraceEnabled() && matchingPatterns.size() > 1) {
         logger.trace("Matching patterns " + 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);

      // 之前是通过sort方法进行排序, 然后拿第一个作为bestPatternMatch的不过有可能有多个Pattern的顺序相同
      // 也就是sort方法返回0, 这里就是处理这种情况
    
      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.isTraceEnabled() && uriTemplateVariables.size() > 0) {
         logger.trace("URI variables " + uriTemplateVariables);
      }
      return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
   }

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

buildPathExposingHandler 方法用于给查找到的 Handler 注册两个拦截器 PathExposingHandlerInterceptorUriTemplateVariablesHandlerInterceptor, 这是两个内部拦截器, 主要作用是将与当前 url 实际匹配的 Pattern 、匹配条件(后面介绍)和 url 模板参数等设置到 request 的属性里 而不需要再重新查找一遍。

buildPathExposingHandler 方法中给 Handler 注册两个内部拦截器 PathExposingHandler­InterceptorUriTemplateVariablesHandlerlnterceptor, 这两个拦截器分别在 preHandle 中调用(exposePathWithinMappingexposeUriTemplateVariables 方法将相应内容设置到 request 的属性。

SimpleUrlHandlerMapping

SimpleUrlHandlerMapping定义了一个Map变量(自己定义一个Map主要有两个作用,第 一是方便配置, 第二是可以在注册前做一些预处理, 如确保所有url都以 "/" 开头),将所有的url和Handler的对应关系放在里面, 最后注册到父类的Map中;而AbstractDetectingUrlHandlerMapping则是将容器中的所有bean都拿出来, 按一定规则注册到父类的Map中。 下面分别来看一下。

SimpleUrlHandlerMapping在创建时通过重写父类的initApplicationContext方法调用了registerHandlers方法完成Handler的注册,registerHandlers内部又调用了AbstractUrlHandlerMappingregisterHandler方法将我们配置的urlMap 注册到AbstractUrlHandler­Mapping的Map中(如果要使用SimpleUrlHandlerMapping 就需要在注册时给它配置urlMap):

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

/**
 * Register all handlers specified in the URL map for the corresponding paths.
 * @param urlMap a Map with URL paths as keys and handler beans or bean names as values
 * @throws BeansException if a handler couldn't be registered
 * @throws IllegalStateException if there is a conflicting handler registered
 */
protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
   if (urlMap.isEmpty()) {
      logger.trace("No patterns in " + formatMappingName());
   }
   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);
      });
      if (logger.isDebugEnabled()) {
         List<String> patterns = new ArrayList<>();
         if (getRootHandler() != null) {
            patterns.add("/");
         }
         if (getDefaultHandler() != null) {
            patterns.add("/**");
         }
         patterns.addAll(getHandlerMap().keySet());
         logger.debug("Patterns " + patterns + " in " + formatMappingName());
      }
   }
}

AbstractDetectingUrlHandlerMapping

bstractDetectingUrlHandlerMapping也是通过重写initApplicationContext来注册Handler的,里面调用了detectHandlers方法, 在detectHandlers中根据配置的detectHandlersInAncestorContexts参数决定是从Spring MVC容器还是Spring MVC及其父容器中找到所有bean的 beanName, 然后用determineUrlsForHandler方法对每个beanName解析出对应的urls, 如果解析结果不为空则将解析出的urls和beanName (作为Handler)注册到父类的Map, 注册方法依然是调用AbstractUrlHandlerMappingregisterHandler方法。 使用beanName解析urls的determineUrlsForHandler方法是模板方法, 交给具体子类实现。

protected void detectHandlers() throws BeansException {
   ApplicationContext applicationContext = obtainApplicationContext();
   String[] beanNames = (this.detectHandlersInAncestorContexts ?
         BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
         applicationContext.getBeanNamesForType(Object.class));

   // Take any bean name that we can determine URLs for.
   for (String beanName : beanNames) {
      String[] urls = determineUrlsForHandler(beanName);
      if (!ObjectUtils.isEmpty(urls)) {
         // URL paths found: Let's consider it a handler.
         registerHandler(urls, beanName);
      }
   }

   if ((logger.isDebugEnabled() && !getHandlerMap().isEmpty()) || logger.isTraceEnabled()) {
      logger.debug("Detected " + getHandlerMap().size() + " mappings in " + formatMappingName());
   }
}

BeanNameUrlHandlerMapping

BeanNameUrlHandlerMapping继承自AbstractDetectingUrlHandlerMapping,实现了模板方法determineUrlsForHandler

检查 beanName 和 alias 是不是以 "/" 开头, 如果是则将其作为 url

@Override
protected String[] determineUrlsForHandler(String beanName) {
   List<String> urls = new ArrayList<>();
   if (beanName.startsWith("/")) {
      urls.add(beanName);
   }
   String[] aliases = obtainApplicationContext().getAliases(beanName);
   for (String alias : aliases) {
      if (alias.startsWith("/")) {
         urls.add(alias);
      }
   }
   return StringUtils.toStringArray(urls);
}

关于BeanNameUrlHandlerMapping/SimpleUrlHandlerMapping的使用做了个小demo进行示例:demo

AbstractHandlerMethodMapping

AbstractHandlerMapping的另一个重要子类AbstractHandlerMethodMapping。这条链路下的子类RequestMappingHandlerMappingRequestMappingInfoHandlerMapping应该是我们最熟悉的handlerMapping了!我们使用@RequestMapping注解的方法即是使用这种方式,他又有专门的HandlerMethod,即是Method类型Handler。

首先我们很清楚地知道,HandlerMapping的唯一目的就是找到handler,所以这个类必不可少的功能就是找到handler,找到之前就需要知道有哪些handler可以用来匹配,所以这个类就整个了MappingRegistry内部类将全部的Controller里面的是处理器的方法收集到几个Map中,到时候再去匹配合适的处理器。

初始化

先看下这个MappingRegistry:

private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();

private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();

private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();

先看这几个关键性的Map,我们发现这里用了泛型,官方对这个泛型的解释:

<T> – the mapping for a HandlerMethod containing the conditions needed to match the handler method to an incoming request.

也就是用来代表匹配Handler的条件专门使用的一种类,这里的条件就不只是url了, 还可以有很多其他条件, 如request的类型 (Get、Post等)、请求的参数、Header等都可以作为匹配HandlerMetbod的条件。 默认使用的是RequestMappingInfo,可以在RequestMappingInfoHandlerMapping类的定义看到:

public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> 

RequestMappinglnfo实现了RequestCondition接口, 此接口专门用于保存从request提取出的用于匹配Handler的条件,抽象实现AbstractRequestCondition中重写了equals、hashCode和toString三个方法,它有8个子类,除了CompositeRequestCondition 外每一个子类表示一种匹配条件。

RequestCondition类图
RequestCondition类图

RequestCondition的另一个实现就是这里要用的RequestMappinglnfo, 它里面其实是用七个变量保存了七个RequestCondition, 在匹配时使用那七个变量进行匹配,这也就是可以在@RequestMapping中给处理器指定多种匹配方式的原因。

mappingLookup:保存着匹配条件(也就是RequestCondition)和HandlerMethod的对应关系。

urlLookup:保存着url与匹配条件(也就是RequestCondition)的对应关系, 当然这里的url是Pattern式的,可以使用通配符。 另外,这里使用的Map并不是普通的Map, 而是 MultiValueMap, 这是一种 一个key对应多个值的Map, 其实它的value是一个List类型的值 看MultiValueMap的定义就能明白,MultiValueMap 定义如下:

MultiValueMap<K, V> extends Map<K, List<V>>

由于RequestCondition可以同时使用多种不同的匹配方式而不只是url 一种,所以反过来说同一个url就可能有多个RequestCondtion与之对应 。这里的RequestCondition其实就是在@RequestMapping中注释的内容。

nameLookup:保存着name与HandlerMethod的对应关系,它使用的也是MultiValueMap类型的Map, 也就是说一个name可以有多个HandlerMethod。 这里的name是使用HandlerMethodMappingNamingStrategy策略的实现类从Hand­lerMethod中解析出来的,默认使用RequestMappinginfoHandlerMethodMappingNamingStrategy
实现类,解析规则是:类名里的大写字母组合+ "#" +方法名。 这个Map 在正常的匹配过程并不需要使用,它主要用在 MvcUriComponentsBuilder里面,可以用来根据name获取相应的url。

AbstractHandlerMethodMapping实现了InitializingBean接口, 所以spring容器会自动调用其afterPropertiesSet方法,afterProperties又调用initHandlerMethods方法。

@Override
public void afterPropertiesSet() {
   initHandlerMethods();
}

/**
 * Scan beans in the ApplicationContext, detect and register handler methods.
 * @see #getCandidateBeanNames()
 * @see #processCandidateBean
 * @see #handlerMethodsInitialized
 */
protected void initHandlerMethods() {
//从容器中获取所有的候选bean
   for (String beanName : getCandidateBeanNames()) {
      if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
         processCandidateBean(beanName);
      }
   }
   handlerMethodsInitialized(getHandlerMethods());
}
protected void processCandidateBean(String beanName) {
   Class<?> beanType = null;
   try {
      beanType = obtainApplicationContext().getType(beanName);
   }
   catch (Throwable ex) {
      // An unresolvable bean type, probably from a lazy bean - let's ignore it.
      if (logger.isTraceEnabled()) {
         logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
      }
   }
   //判断是不是一个handler,模板方法,子类实现
   if (beanType != null && isHandler(beanType)) {
      detectHandlerMethods(beanName);
   }
}

isHandler检查类前是否有@Controller或者@RequestMapping注解修饰:

//RequestMappingHandlerMapping
@Override
protected boolean isHandler(Class<?> beanType) {
    return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
            AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
protected void detectHandlerMethods(Object handler) {
//这步其实没啥必要,目前来看就一个地方调用,且类型一定是String
   Class<?> handlerType = (handler instanceof String ?
         obtainApplicationContext().getType((String) handler) : handler.getClass());

   if (handlerType != null) {
   //如果是cglib代理的子对象类型, 则返回父类型, 否则直接返回传入的类型
      Class<?> userType = ClassUtils.getUserClass(handlerType);
      //获取当前bean里所有符合Handler要求的Method
      Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
            (MethodIntrospector.MetadataLookup<T>) method -> {
               try {
               //模板方法,子类实现
                  return getMappingForMethod(method, userType);
               }
               catch (Throwable ex) {
                  throw new IllegalStateException("Invalid mapping on handler class [" +
                        userType.getName() + "]: " + method, ex);
               }
            });
      if (logger.isTraceEnabled()) {
         logger.trace(formatMappings(userType, methods));
      }
      methods.forEach((method, mapping) -> {
          // 判断Method是否可调用(不是private方法),这里返回的是目标类型可调用的方法
         Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
         //注册符合要求的Method, 也就是保存到三个Map中
         registerHandlerMethod(handler, invocableMethod, mapping);
      });
   }
}

这段代码有点不太好理解,但是我们只要知道目的是为了捞到所有method以及RequestMappinglnfo(从子类角度来看)

public static <T> Map<Method, T> selectMethods(Class<?> targetType, final MetadataLookup<T> metadataLookup) {
   final Map<Method, T> methodMap = new LinkedHashMap<>();
   Set<Class<?>> handlerTypes = new LinkedHashSet<>();
   Class<?> specificHandlerType = null;

   if (!Proxy.isProxyClass(targetType)) {
      specificHandlerType = ClassUtils.getUserClass(targetType);
      handlerTypes.add(specificHandlerType);
   }
   handlerTypes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetType));

   for (Class<?> currentHandlerType : handlerTypes) {
      final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
    //循环currentHandlerType类的所有方法
    //每获取一个方法的时候,就会回调函数式方法
      ReflectionUtils.doWithMethods(currentHandlerType, method -> {
         Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
         //判断方法上面是否有@RequestMapping注解,如果有封装对象返回
         //会调用函数式方法 getMappingForMethod
         T result = metadataLookup.inspect(specificMethod);
         if (result != null) {
            Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
            if (bridgedMethod == specificMethod || metadataLookup.inspect(bridgedMethod) == null) {
               methodMap.put(specificMethod, result);
            }
         }
      }, ReflectionUtils.USER_DECLARED_METHODS);
   }

   return methodMap;
}

getMappingForMethod方法里首先拿到方法的RequestMappingInfo,但是方法级别的比如url显然可能不是完整的,需要再与类上的url进行拼接,所以就有了第二步操作,如下:

//RequestMappingHandlerMapping
@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
    //1.根据方法上的@RequestMapping创建RequestMappingInfo
    //寻找有@RequestMapping注解的方法,然后注解里面的内容封装成对象
   RequestMappingInfo info = createRequestMappingInfo(method);
   if (info != null) {
        //2.查找类上是否有@RequestMapping,如果有则和方法上的组合
     //类上面的@RequestMapping注解也封装成对象
     // 这个类型信息,是解析类上的RequestMapping注解信息,然后跟方法的进行拼接组合
      RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
      if (typeInfo != null) {
          //把方法上面的注解属性结合到类上面的RequestMappingInfo对象中
         info = typeInfo.combine(info);
      }
      String prefix = getPathPrefix(handlerType);
      if (prefix != null) {
         info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
      }
   }
   return info;
}
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
//将查询出的多个annotationType类型注解属性合并到查询的第一个注解中,因为存在@GetMapping
    RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
    RequestCondition<?> condition = (element instanceof Class ?
            getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
    //根据RequestMapping注解的属性来创建RequestMappingInfo
    return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
    }

接下去我们看一下是是如何将找到的HandlerMethod注册,通过定义的内部类MappingRegistry将handler注册进三个Map之中。

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
   this.mappingRegistry.register(mapping, handler, method);
}
public void register(T mapping, Object handler, Method method) {
   // Assert that the handler method is not a suspending one.
   if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
      throw new IllegalStateException("Unsupported suspending handler method detected: " + method);
   }
   this.readWriteLock.writeLock().lock();
   try {
   // 这里会创建一个HandlerMethod实例
      HandlerMethod handlerMethod = createHandlerMethod(handler, method);
      // 校验mappingLookup已经存在mapping信息这里会抛出异常
      validateMethodMapping(handlerMethod, mapping);
      //保存url对象和handlerMethod的映射关系
      // Key: RequestMappingInfo和Value: HandlerMethod
      this.mappingLookup.put(mapping, handlerMethod);
     // 一个mapping是允许存在多条路径的
      List<String> directUrls = getDirectUrls(mapping);
      for (String url : directUrls) {
      //建立url和RequestMappingInfo映射关系
    //urlLookup是个LinkedMultiValueMap,也就是说一个路径可以有多个mapping
    // 在进行匹配的时候,就是先根据url找到合适的mapping,然后根据找到的mapping再去找到HandlerMethod
                    
         this.urlLookup.add(url, mapping);
      }

      String name = null;
      if (getNamingStrategy() != null) {
      // 大写类名#方法名
         name = getNamingStrategy().getName(handlerMethod, mapping);
         addMappingName(name, handlerMethod);
      }
//跨域访问相关
      CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
      if (corsConfig != null) {
         this.corsLookup.put(handlerMethod, corsConfig);
      }

      this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
   }
   finally {
      this.readWriteLock.writeLock().unlock();
   }
}

到这里我们才刚刚讲完初始化流程接下去分析getHandlerlntenal方法获取处理器的过程。

getHandlerInternal

@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
   String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
   request.setAttribute(LOOKUP_PATH, lookupPath);
   this.mappingRegistry.acquireReadLock();
   try {
      HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
      return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
   }
   finally {
      this.mappingRegistry.releaseReadLock();
   }
}

1)根据request获取 lookupPath, 可以简单地理解为请求的url;

2)使用lookupHandlerMethod方法通过lookup­PathrequesthandlerMethod ;

3)如果可以找到 handlerMethod则调用它的creat­eWithResolvedBean方法创建新的HandlerMethod并返回。

createWitbResolvedBean的作用是判断handlerMethod里的handler是不是String类型, 如果是则改为将其作为beanName 从容器中所取到的bean, 不过HandlerMetbod里的属性都是final类型的, 不可以修改, 所以在createWithResolvedBean方法中又用原来的属性和修改后的handler新建了一个HandlerMetbod

直接看下关键的lookupHandlerMethod方法

@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    //Match是内部类 保存匹配条件和Handler
   List<Match> matches = new ArrayList<>();
   //首先根据lookupPath获取到匹配条件,以子类视角即RequestMappingInfo
   List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
   if (directPathMatches != null) {
   // 将找到的匹配条件添加到matches
      addMatchingMappings(directPathMatches, matches, request);
   }
   if (matches.isEmpty()) {
      // 如果不能直接使用lookupPath得到匹配条件,则将所有匹配条件加入matches
      addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
   }
//将包含匹配条件和Handler的matches排序,并取第一个作为bestMatch, 如果前面两个排序相同则抛出异常
   if (!matches.isEmpty()) {
      Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
      matches.sort(comparator);
      Match bestMatch = matches.get(0);
      if (matches.size() > 1) {
         if (logger.isTraceEnabled()) {
            logger.trace(matches.size() + " matching mappings: " + matches);
         }
         if (CorsUtils.isPreFlightRequest(request)) {
            return PREFLIGHT_AMBIGUOUS_MATCH;
         }
         Match secondBestMatch = matches.get(1);
         //如果两个RequestMappinginfo什么都相同,报错
         if (comparator.compare(bestMatch, secondBestMatch) == 0) {
            Method m1 = bestMatch.handlerMethod.getMethod();
            Method m2 = secondBestMatch.handlerMethod.getMethod();
            String uri = request.getRequestURI();
            throw new IllegalStateException(
                  "Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
         }
      }
      request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
      handleMatch(bestMatch.mapping, lookupPath, request);
      return bestMatch.handlerMethod;
   }
   else {
      return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
   }
}

小结

分析了 Spring MVC 中的 HandlerMapping 的各种具体实现方式。 HandlerMapping 的整体结构在 AbstractHandlerMapping 中设计, 简单来说其功能就是根据 request 找到 Handler 和 Interceptors, 组合成 HandlerExecutionChain 类型并返回。 找 Handler 的过程通过模板方法 getHandlerInternal 留给子类实现。查找 Interceptors 则是 AbstractHandlerMapping 自已完成的,查找的具体方法是通过几个与Interceptor相关的Map完成的 在初始化过程中对那些Map进行了初始化。

AbstractHandlerMapping的子类分了两个系列: AbstractUrlHandlerMapping系列和Abstract­HandlerMethodMapping系列, 前者是通过url匹配的, 后者匹配的内容比较多, 而且是直接匹配到Method, 这也是现在使用最多的一种方式。

参考

《看透 spring­Mvc 源代码分析与实践》