SpringMVC常见组件之HandlerMapping分析

简介: SpringMVC常见组件之HandlerMapping分析

在Spring MVC 3.1 之前的版本中,Spring默认使用 DefaultAnnotationHandlerMapping,AnnotationMethodHandlerAdapter来处理 @RequestMapping注解和请求方法调用,而从3.1开始提供了一组新的API,RequestMappingHandlerMapping和RequestMappingHandlerAdapter完成这些工作。


HandlerMapping 叫做处理器映射器,它的作用就是根据当前 request 找到对应的 Handler 和 Interceptor,然后封装成一个 HandlerExecutionChain 对象返回。


其提供了一个方法HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;供子类实现。 我们这里先看下关于请求映射处理的家族树图示:


从上图可以看到,请求映射的家族从该类主要分为三支:根据方法进行匹配的AbstractHandlerMethodMapping、RouterFunctionMapping以及根据请求URL进行批的AbstractUrlHandlerMapping。其中RouterFunctionMapping是5.2版本后引入的,这里暂不分析。

【1】 AbstractHandlerMapping

AbstractHandlerMapping如下所示,其提供了系列模板方法以及抽象方法。

//  返回HandlerInterceptor数组
protected final HandlerInterceptor[] getAdaptedInterceptors() {}
// 返回 HandlerExecutionChain 其包括handler HandlerInterceptor
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {}
// 返回所有配置的MappedInterceptor
protected final MappedInterceptor[] getMappedInterceptors() {}
// 抽象方法供子类实现 ,返回一个handler
protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;

AbstractHandlerMapping 间接继承自 ApplicationObjectSupport,并重写了 initApplicationContext 方法(其实该方法也是一个模版方法),这也是 AbstractHandlerMapping 的初始化入口方法,该方法主要就是扫描容器中的拦截器然后放到全局的private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();变量中。


因为ApplicationObjectSupport实现了ApplicationContextAware接口,故而在bean的实例化过程中,凡是实现了ApplicationContextAware接口的类都会被调用其setApplicationContext方法。ApplicationObjectSupport的setApplicationContext又提供了initApplicationContext方法供子类实现。也就是说ApplicationObjectSupport的子类实例化时会被调用initApplicationContext方法。


如下方法会在AbstractHandlerMapping的子类如SimpleUrlHandlerMapping、BeanNameUrlHandlerMapping、RequestMappingHandlerMapping等实例化时被调用。

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

① 核心方法getHandler

再看下核心方法public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {}

@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);
  }
  // 根据handler request获取HandlerExecutionChain 
  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 = getCorsConfiguration(handler, request);
    if (getCorsConfigurationSource() != null) {
      CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
      config = (globalConfig != null ? globalConfig.combine(config) : config);
    }
    if (config != null) {
      config.validateAllowCredentials();
    }
    executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
  }
  return executionChain;
}

方法解释如下:


① 获取handler ;

② 如果① 获取的handler为null,则获取一个默认的handler也就是defaultHandler;

③ 如果②仍旧没有获取到handler,则返回null;

④ 根据handler获取HandlerExecutionChain,其包括了handler和拦截器;

⑤ 如果进行了跨域配置,则获取CorsHandlerExecutionChain

② 核心方法getHandlerExecutionChain

我们看内部的核心方法getHandlerExecutionChain

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
  HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
      (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
  for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
    if (interceptor instanceof MappedInterceptor) {
      MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
      if (mappedInterceptor.matches(request)) {
        chain.addInterceptor(mappedInterceptor.getInterceptor());
      }
    }
    else {
      chain.addInterceptor(interceptor);
    }
  }
  return chain;
}

方法解释如下:


① 处理获取一个HandlerExecutionChain ;

② 遍历adaptedInterceptors

③ 如果interceptor 是 MappedInterceptor则判断是否匹配当前请求,匹配则添加到当前处理器链中

④ 如果interceptor 是普通拦截器(没有指定映射URL则默认拦截所有),则直接添加到当前处理器链中

【2】 AbstractUrlHandlerMapping


该分支下是根据URL进行处理器映射匹配查找。常见实现子类有SimpleUrlHandlerMapping和BeanNameUrlHandlerMapping。其覆盖了父类AbstractHandlerMapping的getHandlerInternal方法。


如下所示,其内部维护了两个final map:handlerMap 维护了一个URL与handler的映射关系;pathPatternHandlerMap 维护了一个PathPattern与handler的映射关系。当获取handler时就从这两个map中进行遍历查找。

// URL模式--handler
private final Map<String, Object> handlerMap = new LinkedHashMap<>();
// PathPattern模式--handler
private final Map<PathPattern, Object> pathPatternHandlerMap = new LinkedHashMap<>();

① 核心方法getHandlerInternal

getHandlerInternal方法会根据request获取一个handler。

protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
  String lookupPath = initLookupPath(request);
  Object handler;
  if (usesPathPatterns()) {
    RequestPath path = ServletRequestPathUtils.getParsedRequestPath(request);
    handler = lookupHandler(path, lookupPath, request);
  }
  else {
    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 (StringUtils.matchesCharacter(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);
    }
  }
  return handler;
}


方法解释如下:


① 根据request获取lookupPath;

② 如果patternParser不为null,调用lookupHandler(path, lookupPath, request)也就是从pathPatternHandlerMap寻找handler;

③ 如果patternParser为null,则调用lookupHandler(lookupPath, request)也就是从handlerMap进行查找;

④ 如果②③之后handler还为null,则尝试获取RootHandler、DefaultHandler。

⑤ 如果找到的 Handler 是一个 String,则去 Spring 容器中查找该 String 对应的 Bean;

⑥ 调用 validateHandler 方法来校验找到的 handler 和 request 是否匹配,这是一个空方法,可以忽略。

⑦ 通过 buildPathExposingHandler 方法将找到的handler包装为HandlerExecutionChain然后添加PathExposingHandlerInterceptor拦截器,并根据uriTemplateVariables是否为空添加UriTemplateVariablesHandlerInterceptor拦截器。

② 核心方法lookupHandler(String lookupPath, HttpServletRequest request)

也就是根据URL获取一个handler。

@Nullable
protected Object lookupHandler(String lookupPath, HttpServletRequest request) throws Exception {
  Object handler = getDirectMatch(lookupPath, request);
  if (handler != null) {
    return handler;
  }
  // Pattern match? 也就是通配符匹配
  List<String> matchingPatterns = new ArrayList<>();
  for (String registeredPattern : this.handlerMap.keySet()) {
    if (getPathMatcher().match(registeredPattern, lookupPath)) {
      matchingPatterns.add(registeredPattern);
    }
    else if (useTrailingSlashMatch()) {
      if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", lookupPath)) {
        matchingPatterns.add(registeredPattern + "/");
      }
    }
  }
  String bestMatch = null;
  // AntPathMatcher.AntPatternComparator
  Comparator<String> patternComparator = getPathMatcher().getPatternComparator(lookupPath);
  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, lookupPath);
    // 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, lookupPath);
        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;
}

方法解释如下:


① 根据lookupPath从handlerMap中获取对应的handler,如果存在则直接返回。注意,这里是直接匹配,也就是没有通配符。

② 遍历handlerMap:

③ 如果当前key与lookupPath匹配(由AntPathMatcher决定),则添加当前key到List<String> matchingPatterns中;

④ 如果当前key+"/" 与lookupPath匹配(由AntPathMatcher决定),则添加当前key+"/"到List<String> matchingPatterns中;

⑤ 根据PathMatcher获取PatternComparator,默认是AntPatternComparator;

⑥ 如果matchingPatterns不为空,则根据PatternComparator进行排序然后获取第一个元素赋予bestMatch;

⑦ 如果获取的bestMatch不为空则从handlerMap中获取对应的handler;

⑧ 如果⑦获取的handler为空且bestMatch.endsWith("/"),则bestMatch去掉"/"再次从handlerMap获取handler;

⑨ 如果⑧获取的handler仍旧为null,则抛出异常。

(10) 如果找到的 handler 是 String 类型的,则再去 Spring 容器中查找对应的 Bean,接下来再调用 validateHandler 方法进行验证;

(11) extractPathWithinPattern 方法提取出匹配部分pattern-matched路径。例如定义的接口规则是 myroot/*.html,请求路径是 myroot/myfile.html,那么最终获取到的就是 myfile.html。

(12) 遍历matchingPatterns,然后patternComparator.compare(bestMatch, matchingPattern) == 0 也就是判断当前matchingPattern是否与bestMatch等价。然后根据每一个matchingPattern与lookupPath尝试解析path里面变量放入Map<String, String> uriTemplateVariables;

(13) 调用buildPathExposingHandler,这里不再赘述。

(14) 如果获取的bestMatch为空,则直接返回null。

由于一个请求 可能会和定义的多个接口匹配上(接口定义的URL带有通配符),所以 matchingPatterns 变量是一个数组。接下来就要对 matchingPatterns 进行排序,排序完成后,选择排序后的第一项作为最佳选项赋值给 bestMatch 变量。默认的排序规则是 AntPatternComparator,当然开发者也可以自定义。AntPatternComparator 中定义的优先级如下:

URL 优先级
不含任何特殊符号的路径,如:/a/b/c 第一优先级
带有{}的路径,如:/a/{b}/c 第二优先级
带有正则的路径,如:/a/{regex:\d{3}}/c 第三优先级
带有*的路径,如:/a/b/* 第四优先级
带有**的路径,如:/a/b/** 第五优先级
最模糊的匹配:/** 最低优先级

③ 核心方法registerHandler

该方法就是扫描并注册handler,在bean实例化时被调用,如下所示。其中detectHandlers会调用registerHandler(urls, beanName)进行遍历循环注册。

@Override
public void initApplicationContext() throws ApplicationContextException {
  super.initApplicationContext();
  detectHandlers();
}

这里我们直接看registerHandler(urlPath, beanName)方法,其尝试将urlPath = resolvedHandler放入handlerMap中。

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.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 (getPatternParser() != null) {
          this.pathPatternHandlerMap.put(getPatternParser().parse(urlPath), resolvedHandler);
        }
        if (logger.isTraceEnabled()) {
          logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
        }
      }
    }
  }

方法源码解释如下:


① Object resolvedHandler = handler,如果!this.lazyInitHandlers && handler instanceof String,则尝试从Spring容器中获取resolvedHandler;

② 根据urlPath尝试从handlerMap获取handler,如果handler不为空且不等于入参中的handler则抛出异常。如果不为空且等于入参中的handler,则表示已经存在不做处理。

③ 如果从handlerMap获取不到handler:

④ 如果urlPath.equals("/"),则将resolvedHandler设置为RootHandler;

⑤ urlPath.equals("/*"),则将resolvedHandler设置为DefaultHandler;

⑥ this.handlerMap.put(urlPath, resolvedHandler);将其放入handlerMap。

【3】SimpleUrlHandlerMapping

顾名思义,就是在URL与实例bean(或bean 名称)维护映射关系。其内部维护了一个final mapprivate final Map<String, Object> urlMap = new LinkedHashMap<>();。

其构造方法如下所示:

private final Map<String, Object> urlMap = new LinkedHashMap<>();
// 空的构造函数
public SimpleUrlHandlerMapping() {
}
//根据urlMap 初始化  final Map<String, Object> urlMap
public SimpleUrlHandlerMapping(Map<String, ?> urlMap) {
  setUrlMap(urlMap);
}
// 在前者的基础上并设置当前handleMapping的order
public SimpleUrlHandlerMapping(Map<String, ?> urlMap, int order) {
  setUrlMap(urlMap);
  setOrder(order);
}

其重写了父类的initApplicationContext方法,在该方法中调用了父类initApplicationContext方法,并调用了registerHandlers(this.urlMap);

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

① 核心方法registerHandlers

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

方法解释如下:


① 如果urlMap为空,则直接返回;

② 遍历循环urlMap,如果当前URL不是以"/"开头,则拼接前缀"/";如果handler是string,则进行trim;然后调用父类AbstractUrlHandlerMapping的registerHandler方法。

② SimpleUrlHandlerMapping的使用

一个应用实例是如下配置,则这里urlMap就维护了映射关系。

<mvc:view-controller path="/" view-name="index"></mvc:view-controller>

也就是说,如果你想使用SimpleUrlHandlerMapping,那么请自行配置映射关系。如下xml配置:

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
     <property name="urlMap">
         <map>
             <entry key="/hello" value="helloController"/>
         </map>
     </property>
     <!--或者如下配置-->
     <property name="mappings">
        <props>
             <prop key="/hello">helloController</prop>
         </props>
     </property>
     <!--或者如下配置-->
     <property name="mappings">
        <value>
       /hello=helloController
    </value>
     </property>
 </bean>

helloController是controller的名字,需要实现Controller接口,如下所示:

@org.springframework.stereotype.Controller
public class HelloController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println(request.getRequestURI());
        System.out.println(this.getClass().getName());
        ModelAndView modelAndView=new ModelAndView();
        modelAndView.setViewName("success");
        return modelAndView;
    }
}

如果启用了defaultServletHandler那么SimpleUrlHandlerMappingurlMap中还有如下配置:

/**=org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler#0

【4】AbstractDetectingUrlHandlerMapping


这个简单来讲,就是扫描spring容器中注册的bean。如果bean name或bean的别名是以"/"开头,那么就将bean name或bean的别名作为URL,调用父类AbstractUrlHandlerMapping的registerHandler(String[] urlPaths, String beanName)方法最后注册到handlerMap中。


我们同样从initApplicationContext方法看起,其调用了父类的AbstractHandlerMapping的initApplicationContext方法并调用detectHandlers来检测handler。

@Override
public void initApplicationContext() throws ApplicationContextException {
  super.initApplicationContext();
  detectHandlers();
}

① 核心方法detectHandlers

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);
    }
  }
// ... 这里是无需关注的日志打印
}
prote

方法解释如下:

  • ① 获取ApplicationContext;
  • ② 获取容器中的beanNames ,这里得到一个数组;
  • ③ 遍历beanNames ,检测其URL。如果URL不为空则注册到handlerMap。


这里determineUrlsForHandler是个抽象方法,供子类实现。其只有一个子类BeanNameUrlHandlerMapping,我们看下其实现determineUrlsForHandler方法。

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

方法很简单,判断beanName是否以"/"开头,是的话放入urls数组中。然后获取beanName的别名,对别名数组进行遍历判断是否以否以"/"开头,是的话放入urls数组中。

也就是说,上面我们的HelloController可以指定名字如下所示:

@org.springframework.stereotype.Controller("/helloController")
public class HelloController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println(request.getRequestURI());
        System.out.println(this.getClass().getName());
        ModelAndView modelAndView=new ModelAndView();
        modelAndView.setViewName("success");
        return modelAndView;
    }
}

【5】AbstractHandlerMethodMapping

在前面 AbstractUrlHandlerMapping 体系下,一个 Handler 一般就是一个类(显示Controller如HelloController或者隐式controller如ParameterizableViewController)。但是在 AbstractHandlerMethodMapping 体系下,一个 Handler 通常指的是一个 Mehtod,是HandlerMethod类型。


AbstractHandlerMethodMapping主要维护了请求和handlerMethod之间的映射关系。其覆盖了父类AbstractHandlerMapping如下三个方法:

// 核心方法,获取内部handler。该体系下返回handlerMethod
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {}
//如下两个方法和跨域配置有关
protected CorsConfiguration getCorsConfiguration(Object handler, HttpServletRequest request) {}
protected boolean hasCorsConfigurationSource(Object handler) {}

其提供了如下抽象方法供子类实现

//RequestMappingInfoHandlerMapping实现
protected abstract T getMatchingMapping(T mapping, HttpServletRequest request);
//RequestMappingInfoHandlerMapping实现
protected abstract Comparator<T> getMappingComparator(HttpServletRequest request);
//RequestMappingHandlerMapping实现
protected abstract boolean isHandler(Class<?> beanType);
//RequestMappingHandlerMapping实现
protected abstract T getMappingForMethod(Method method, Class<?> handlerType);

核心变量private final MappingRegistry mappingRegistry = new MappingRegistry(); 为什么说是核心变量?我们可以看下mappingRegistry究竟是个什么?如下所示其registry 维护了所有的RequestMappingInfo实例与MappingRegistration映射关系。pathLookup 维护了directPath与List<RequestMappingInfo>实例关系。当处理请求获取handlerMethod时,qi

private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

我们看一下该体系下的家族树图示

需要注意的是AbstractHandlerMethodMapping其实现了InitializingBean接口,重写了afterPropertiesSet方法。为什么这里特意提一下实现了InitializingBean接口,那是因为这个接口定义了其实现Bean在容器完成属性设置后可以执行自定义初始化操作,我们的AbstractHandlerMethodMapping便实现了这个接口,并且定义了一组自定义操作,就是用来检测处理我们的@RequestMapping注解。

① afterPropertiesSet

如下图所示,如何bean实现了InitializingBean接口,那么在初始化过程中一定会调用其afterPropertiesSet方法。



这里我们看一下RequestMappingHandlerMapping.afterPropertiesSet方法。

@Override
@SuppressWarnings("deprecation")
public void afterPropertiesSet() {
  this.config = new RequestMappingInfo.BuilderConfiguration();
  this.config.setTrailingSlashMatch(useTrailingSlashMatch());
  this.config.setContentNegotiationManager(getContentNegotiationManager());
  if (getPatternParser() != null) {
    this.config.setPatternParser(getPatternParser());
    Assert.isTrue(!this.useSuffixPatternMatch && !this.useRegisteredSuffixPatternMatch,
        "Suffix pattern matching not supported with PathPatternParser.");
  }
  else {
    this.config.setSuffixPatternMatch(useSuffixPatternMatch());
    this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
    this.config.setPathMatcher(getPathMatcher());
  }
  super.afterPropertiesSet();
}

可以看到其主要做了一些配置如后缀模式匹配,然后调用父类AbstractHandlerMethodMappingafterPropertiesSet方法。其源码如下所示-在初始化过程中检测处理器方法。

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

简单直接调用了AbstractHandlerMethodMapping#initHandlerMethods方法。如下所示父类直接调用initHandlerMethods方法进行handlerMethod的初始化注册。

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

① initHandlerMethods

initHandlerMethods方法源码如下:

① initHandlerMethods
initHandlerMethods方法源码如下:
// 在Ap

这里我们着重分析processCandidateBean(beanName);getHandlerMethods()方法。

② processCandidateBean


processCandidateBean(beanName);方法的作用是什么呢?判断其是否为一个handler(也就是是否拥有@Controller注解或@RequestMapping注解)。如果是一个handler,则检测并注册其内部方法。

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);
    }
  }
  if (beanType != null && isHandler(beanType)) {
      // 注意,这里是核心
    detectHandlerMethods(beanName);
  }
}

isHandler(beanType)是一个抽象方法,在子类RequestMappingHandlerMapping中实现。

我们看下isHandler(beanType)是如何判断当前bean是否为一个handler的?

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

判断类上面的注解是否是(或拥有)@Controller或者@RequestMapping(或者复合注解如@RestController、@PostMapping)。OK,我们继续看detectHandlerMethods方法如何扫描并注册。


③ detectHandlerMethods

扫描handler内部的handler method然后注册到AbstractHandlerMethodMapping内部的常量private final MappingRegistry mappingRegistry = new MappingRegistry();中。

protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = (handler instanceof String ?
      obtainApplicationContext().getType((String) handler) : handler.getClass());
  if (handlerType != null) {
    Class<?> userType = ClassUtils.getUserClass(handlerType);
    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 invocableMethod = AopUtils.selectInvocableMethod(method, userType);
      registerHandlerMethod(handler, invocableMethod, mapping);
    });
  }
}

这里我们暂不展开分析,放在第三部分中展开。我们只需要知道在RequestMappingHandlerMapping 初始化过程中,该方法将会扫描容器中那些Controller及内部method。获取handler、method、mapping,也就是处理器、处理器方法与请求映射信息(包括@RequestMapping标注的那些参数)。


扫描并注册完之后,到了handlerMethodsInitialized(getHandlerMethods());。这里我们看下getHandlerMethods。


④ getHandlerMethods

AbstractHandlerMethodMapping#getHandlerMethods方法如下:

 // 返回只读的map  mapping :HandlerMethod
public Map<T, HandlerMethod> getHandlerMethods() {
 // private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
  this.mappingRegistry.acquireReadLock();
  try {
    return Collections.unmodifiableMap(
        this.mappingRegistry.getRegistrations().entrySet().stream()
            .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().handlerMethod)));
  }
  finally {
    this.mappingRegistry.releaseReadLock();
  }
}

方法源码解释如下:


获取mappingRegistry中的读锁然后加锁;

遍历MappingRegistry.registry,返回一个只读Map;

释放锁

handlerMethodsInitialized(getHandlerMethods());方法则更简单,只是简单打印告诉系统已经已经有多少个mappings被扫描注册。

【6】detectHandlerMethods详细分析


detectHandlerMethods方法是该组件的核心方法,将会扫描@RequestMapping注解的方法并创建RequestMappingInfo实例。然后将将RequestMappingInfo实例以及处理器方法注册到AbstractHandlerMethodMapping的内部类MappingRegistry中常量Mapprivate final Map<T, MappingRegistration<T>> registry = new HashMap<>();。

protected void detectHandlerMethods(Object handler) {
  Class<?> handlerType = (handler instanceof String ?
      obtainApplicationContext().getType((String) handler) : handler.getClass());
  if (handlerType != null) {
    Class<?> userType = ClassUtils.getUserClass(handlerType);
    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 invocableMethod = AopUtils.selectInvocableMethod(method, userType);
      registerHandlerMethod(handler, invocableMethod, mapping);
    });
  }
}

代码解释如下:

  • getMappingForMethod方法创建RequestMappingInfo实例;
  • methods.forEach方法循环遍历并注册handlerinvocableMethodmapping到内存中。


这里我们可以先看下method、mapping的实例


如上图所示,method记录了方法详细信息诸如声明类、名字、返回结果类型、参数类型、异常类型、修饰符、注解、参数、是否覆盖等等。mapping则记录了@RequestMapping注解里面的信息如URL、请求方法、参数限制、header、consume、produce以及自定义限制条件等。

① getMappingForMethod

@Override
@Nullable
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);
    }
    String prefix = getPathPrefix(handlerType);
    if (prefix != null) {
      info = RequestMappingInfo.paths(prefix).options(this.config).build().combine(info);
    }
  }
  return info;
}


代码解释如下:


① 根据method创建RequestMappingInfo 实例info ;

② 根据handlerType创建RequestMappingInfo 实例实例typeInfo ;

③ 如果②中创建的实例typeInfo 不为null,则info = typeInfo.combine(info)进行合并;

④ 根据handlerType获取类上面定义的路径前缀,如果前缀不为空则对info进行合并。

② 创建RequestMappingInfo实例

RequestMappingHandlerMapping#createRequestMappingInfo源码如下所示:

@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
// //获取方法method上的@RequestMapping实例。
  RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
// 这里始终为null  
  RequestCondition<?> condition = (element instanceof Class ?
      getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
  return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}


然后我们继续看createRequestMappingInfo(requestMapping, condition)方法。

protected RequestMappingInfo createRequestMappingInfo(
    RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
  RequestMappingInfo.Builder builder = RequestMappingInfo
      .paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
      .methods(requestMapping.method())
      .params(requestMapping.params())
      .headers(requestMapping.headers())
      .consumes(requestMapping.consumes())
      .produces(requestMapping.produces())
      .mappingName(requestMapping.name());
  if (customCondition != null) {
    builder.customCondition(customCondition);
  }
  return builder.options(this.config).build();
}


看着是不是会熟悉?其实就是获取@RequestMapping注解的url、params、headers、consumes、produces以及name最后进行创建RequestMappingInfo实例。


关于@RequestMapping注解的url、params、headers、consumes、produces以及name这些本文不做赘述,

@RequestMapping注解信息实例

@org.s@org.springframework.web.bind.annotation.RequestMapping(consumes=[], headers=[], method=[], name=, params=[], path=[/**/testInterceptor], produces=[], value=[/**/testInterceptor])

RequestMappingInfo实例

③ 类与方法上面的请求映射实例合并typeInfo.combine(info)


这里我们着重看一下根据类创建的RequestMappingInfo实例与根据方法创建的RequestMappingInfo实例合并过程。

@Override
public RequestMappingInfo combine(RequestMappingInfo other) {
  String name = combineNames(other);
  PathPatternsRequestCondition pathPatterns =
      (this.pathPatternsCondition != null && other.pathPatternsCondition != null ?
          this.pathPatternsCondition.combine(other.pathPatternsCondition) : null);
  PatternsRequestCondition patterns =
      (this.patternsCondition != null && other.patternsCondition != null ?
          this.patternsCondition.combine(other.patternsCondition) : null);
  RequestMethodsRequestCondition methods = this.methodsCondition.combine(other.methodsCondition);
  ParamsRequestCondition params = this.paramsCondition.combine(other.paramsCondition);
  HeadersRequestCondition headers = this.headersCondition.combine(other.headersCondition);
  ConsumesRequestCondition consumes = this.consumesCondition.combine(other.consumesCondition);
  ProducesRequestCondition produces = this.producesCondition.combine(other.producesCondition);
  RequestConditionHolder custom = this.customConditionHolder.combine(other.customConditionHolder);
  return new RequestMappingInfo(
      name, pathPatterns, patterns, methods, params, headers, consumes, produces, custom);
}


这里涉及到了几个类,我们大致了解下含义:


PatternRequestCondition 它其实就是URL模式的封装,它包含了一个URL模式的Set集合。其实就是@RequestMapping注解中的value值得封装。


RequestMethodRequestCondition 它是@RequestMapping 注解中method属性的封装


ParamsRequestCondition 它是@RequestMapping注解中params属性的封装


等等,依次类推。因此RequestMappingInfo其实就是对@RquestMapping 的封装。这里关于method、params、heade等的合并我们暂时不理会(其实你对框架应用熟悉就应该能想到这些合并就是做条件与),着重看下patternsCondition的合并。

@Override
public PatternsRequestCondition combine(PatternsRequestCondition other) {
  if (isEmptyPathMapping() && other.isEmptyPathMapping()) {
    return this;
  }
  else if (other.isEmptyPathMapping()) {
    return this;
  }
  else if (isEmptyPathMapping()) {
    return other;
  }
  Set<String> result = new LinkedHashSet<>();
  if (!this.patterns.isEmpty() && !other.patterns.isEmpty()) {
    for (String pattern1 : this.patterns) {
      for (String pattern2 : other.patterns) {
        result.add(this.pathMatcher.combine(pattern1, pattern2));
      }
    }
  }
  return new PatternsRequestCondition(result, this);
}

代码解释如下:

  • 如果两个path都为空,则返回this;
  • 如果其中一个为空,返回不为空的那个;
  • 如果两个都不为空,则进行笛卡尔积合并;


两个pattern如何合并的呢?url拼接是由PathMatcher来完成的,我们看下实例

pattern1 pattern2 结果
/* /hotel /hotel
/*.* /*.html /*.html
/usr /user /usr/user
/{foo} /bar /{foo}/bar
/hotels/* /booking /hotels/booking
/hotels/* booking /hotels/booking
/hotels /booking /hotels/booking
/hotels booking /hotels/booking
/*.html /hotel /hotel.html
/*.html /hotel.* /hotel.html

④ 注册HandlerMethod

从上面看到这里会进行循环然后进行注册,如下所示:

methods.forEach((method, mapping) -> {
  Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
  registerHandlerMethod(handler, invocableMethod, mapping);
});

AbstractHandlerMethodMapping#registerHandlerMethod方法如下:

protected void registerHandlerMethod(Object handler, Method method, T mapping) {
  this.mappingRegistry.register(mapping, handler, method);
}

继续跟进AbstractHandlerMethodMapping.MappingRegistry#register

public void register(T mapping, Object handler, Method method) {
  this.readWriteLock.writeLock().lock();
  try {
    HandlerMethod handlerMethod = createHandlerMethod(handler, method);
    // 这里mapping就是RequestMappingInfo实例 
    validateMethodMapping(handlerMethod, mapping);
    // 获取请求URL  ,然后往pathLookup放入path与mapping
    Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
    for (String path : directPaths) {
      this.pathLookup.add(path, mapping);
    }
    // 获取name,然后往nameLookup中放入name=List<HandlerMethod>
    String name = null;
    if (getNamingStrategy() != null) {
      name = getNamingStrategy().getName(handlerMethod, mapping);
      addMappingName(name, handlerMethod);
    }
    CorsConfiguration config = initCorsConfiguration(handler, method, mapping);
    if (config != null) {
      config.validateAllowCredentials();
      this.corsLookup.put(handlerMethod, config);
    }
    this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directPaths, name));
  }
  finally {
    this.readWriteLock.writeLock().unlock();
  }
}

代码解释如下:

  • ① 获取写锁进行加锁;
  • ② 获取HandlerMethod;

③ 校验是否存在不同的HandlerMethod对应同一个RequestMappingInfo实例。简单来说就是同样的@RequestMapping注解在不同的类或者方法上。


④ 获取映射URL,然后放到AbstractHandlerMethodMapping内部类MappingRegistry的private final MultiValueMap<String, T> pathLookup中,path=RequestMappingInfo。这里放的URL是不带通配符*和?的,也就是所谓的directPath。


⑤ 获取handler method的name,然后往nameLookup中放入name=List<HandlerMethod>。这里类似于一个彩蛋,如URL/test访问也可以使用方法TC#test访问。TC就是TestController的缩写。


⑥ 判断是否设置了跨域也就是类或者方法上是否有@CrossOrigin注解。如果设置了跨域则初始化CorsConfiguration:


校验是否满足跨域条件:allowCredentials为true时allowedOrigins不能为*,不满足则会抛出异常;

如果满足条件则往corsLookup放入handlerMethod=config:this.corsLookup.put(handlerMethod, config);

⑦ this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directPaths, name));往AbstractHandlerMethodMapping的内部类MappingRegistry的private final Map<T, MappingRegistration<T>> registry = new HashMap<>();放入mapping=new MappingRegistration<>(mapping, handlerMethod, directPaths, name)


  • ⑧ 获取写锁,进行锁的释放。

【7】 查找lookupHandlerMethod

AbstractHandlerMethodMapping#lookupHandlerMethod方法主要是根据lookupPath来获取一个HandlerMethod 。


① lookupHandlerMethod

@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
  List<Match> matches = new ArrayList<>();
  // 从 MappingRegistry.pathLookup中获取 RequestMappingInfo实例集合
  List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
  if (directPathMatches != null) {
    addMatchingMappings(directPathMatches, matches, request);
  }
  if (matches.isEmpty()) {
    addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
  }
  if (!matches.isEmpty()) {
    Match bestMatch = matches.get(0);
    if (matches.size() > 1) {
      Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
      matches.sort(comparator);
      bestMatch = matches.get(0);
      if (logger.isTraceEnabled()) {
        logger.trace(matches.size() + " matching mappings: " + matches);
      }
      if (CorsUtils.isPreFlightRequest(request)) {
        return PREFLIGHT_AMBIGUOUS_MATCH;
      }
      Match secondBestMatch = matches.get(1);
      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.getRegistrations().keySet(), lookupPath, request);
  }
}

mappingRegistry实例图示

方法解释如下:


① 根据lookupPath从MappingRegistry的MultiValueMap<String, T> pathLookup获取List<T> directPathMatches。如果获取到结果不为空,则找出匹配当前请求的RequestMappingInfo实例,根据RequestMappingInfo实例实例和HandlerMethod组装Match,最后放到List<Match> matches中。这里我们可以再回顾一下pathLookup实例:


  • ② 如果①操作后的matches为空,则尝试从MappingRegistry.registry中获取所有匹配当前请求的数据集,然后封装为Match实例对象放到matches中。


③ 如果② 之后matches不为空,则判断是否只有一条。如果数据集大于一条进行排序,bestMatch = matches.get(0);。这里会额外对第一、二条数据进行比较,如果相等则抛出异常。如果数据集只有一条,则bestMatch = matches.get(0);,继续往下处理。

④ 往request中放入两个属性:

request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);

.request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath);

⑤ 返回bestMatch中的handlerMethod.

⑥ 如果② 之后matches为空,则走handleNoMatch方法。该方法为空,返回null,在子类RequestMappingInfoHandlerMapping中重写了该方法可能会抛出诸如HttpRequestMethodNotSupportedException、HttpMediaTypeNotSupportedException、HttpMediaTypeNotAcceptableException、UnsatisfiedServletRequestParameterException异常。


request中放的两个属性键是:

// org.springframework.web.servlet.HandlerMapping.bestMatchingHandler
String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";
// org.springframework.web.servlet.HandlerMapping.pathWithinHandlerMapping
String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";

上面我们看到了如果matches数据集大于一条会获取比较器然后进行排序,其会调用RequestMappingInfo#compareTo方法进行比较。


Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);

② addMatchingMappings

这里我们再看下addMatchingMappings方法。方法如下所示,会遍历mappings集合(RequestMappingInfo实例集合),从中找到匹配当前request的RequestMappingInfo,然后封装为Match对象放到List<Match> matches中。

private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
  for (T mapping : mappings) {
    T match = getMatchingMapping(mapping, request);
    if (match != null) {
      matches.add(new Match(match,
          this.mappingRegistry.getRegistrations().get(mapping).getHandlerMethod()));
    }
  }
}

③ getMatchingMapping&&getMatchingCondition

那么如何判断RequestMappingInfo与request是否匹配呢? 我们继续看下getMatchingMapping方法。该方法在RequestMappingInfoHandlerMapping子类中进行了实现。

protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
  return info.getMatchingCondition(request);
}
//RequestMappingInfo#getMatchingCondition
public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
  if (methods == null) {
    return null;
  }
  ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
  if (params == null) {
    return null;
  }
  HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
  if (headers == null) {
    return null;
  }
  ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
  if (consumes == null) {
    return null;
  }
  ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
  if (produces == null) {
    return null;
  }
  PathPatternsRequestCondition pathPatterns = null;
  if (this.pathPatternsCondition != null) {
    pathPatterns = this.pathPatternsCondition.getMatchingCondition(request);
    if (pathPatterns == null) {
      return null;
    }
  }
  PatternsRequestCondition patterns = null;
  if (this.patternsCondition != null) {
    patterns = this.patternsCondition.getMatchingCondition(request);
    if (patterns == null) {
      return null;
    }
  }
  RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
  if (custom == null) {
    return null;
  }
  return new RequestMappingInfo(
      this.name, pathPatterns, patterns, methods, params, headers, consumes, produces, custom);
}

如上所示,方法会逐个对method、param、header、consume、produce、pathPattern、pattern以及自定义condition进行匹配,只有全部满足则通过新建一个RequestMappingInfo实例返回,否则返回null。


总结


① 在bean实例化时扫描、注册请求URL(或者是RequestMappingInfo实例)与handler;

② 请求处理时,遍历查找。如果不能直接获取或者获取为多个,则会进行比较排序然后选出最优解;

③ 封装为HandlerExecutionChain返回。

【8】几个概念

RequestMappingInfo


这个类是对请求映射的一个抽象,它包含了请求路径,请求方法,请求头等信息。其实可以看做是@RequestMapping的一个对应类。其内部维护了静态内部类BuilderConfigurationDefaultBuilder以及一些封装@RequestMapping注解信息的类。


其内部常量如下所示:

// 一个逻辑分离 (' || ') 请求条件,它根据一组 URL 路径模式匹配请求。这里使用已解析的PathPatterns
private static final PathPatternsRequestCondition EMPTY_PATH_PATTERNS = new PathPatternsRequestCondition();
// Patterns 也就是请求URL,PathPatternsRequestCondition 不同,这里使用String 字符串模式
private static final PatternsRequestCondition EMPTY_PATTERNS = new PatternsRequestCondition();
// 请求限制http method
private static final RequestMethodsRequestCondition EMPTY_REQUEST_METHODS = new RequestMethodsRequestCondition();
// @RequestMapping注解中的param
private static final ParamsRequestCondition EMPTY_PARAMS = new ParamsRequestCondition();
// @RequestMapping注解中的header
private static final HeadersRequestCondition EMPTY_HEADERS = new HeadersRequestCondition();
// @RequestMapping注解中的consume
private static final ConsumesRequestCondition EMPTY_CONSUMES = new ConsumesRequestCondition();
// @RequestMapping注解中的produce
private static final ProducesRequestCondition EMPTY_PRODUCES = new ProducesRequestCondition();
// 自定义条件
private static final RequestConditionHolder EMPTY_CUSTOM = new RequestConditionHolder(null);
@Nullable
private final String name;
@Nullable
private final PathPatternsRequestCondition pathPatternsCondition;
@Nullable
private final PatternsRequestCondition patternsCondition;
private final RequestMethodsRequestCondition methodsCondition;
private final ParamsRequestCondition paramsCondition;
private final HeadersRequestCondition headersCondition;
private final ConsumesRequestCondition consumesCondition;
private final ProducesRequestCondition producesCondition;
private final RequestConditionHolder customConditionHolder;
private final int hashCode;

简单实例图示

② HandlerMethod


这个类封装了处理器实例(Controller Bean)和 处理方法实例(Method)以及方法参数数组(MethodParameter[])。其内部成员变量为:

private final Object bean;
@Nullable
private final BeanFactory beanFactory;
private final Class<?> beanType;
private final Method method;
private final Method bridgedMethod;
private final MethodParameter[] parameters;
@Nullable
private HttpStatus responseStatus;
@Nullable
private String responseStatusReason;
@Nullable
private HandlerMethod resolvedFromHandlerMethod;
@Nullable
private volatile List<Annotation[][]> interfaceParameterAnnotations;
private final String description;

HandlerMethod实例图示

其有用对Method的应用,Method示例图示如下:


③ AbstractHandlerMethodMapping#MappingRegistry

MappingRegistry是AbstractHandlerMethodMapping内部类,内部维护了系列final类型的Map。用来存储"映射与目标"之间的关系,如下所示:

class MappingRegistry {
    private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
    private final MultiValueMap<String, T> pathLookup = new LinkedMultiValueMap<>();
    private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
    private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
    private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//...
}   

其提供了核心方法register用来向pathLookup、nameLookup、corsLookup以及registry放入数据。unregister方法从pathLookup、nameLookup、corsLookup以及registry移除入数据。


我们可以通过下图直观感受其究竟是一个怎样的实例。

④ ApplicationContextAware与ApplicationObjectSupport


ApplicationContextAware接口只提供了一个方法用来为当前上下文设置ApplicationContext 。

void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

当bean实例化后,会遍历BeanPostProcessor调用其postProcessBeforeInitialization进行后置处理。如ApplicationContextAwareProcessor的postProcessBeforeInitialization会判断bean是否是EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware以及ApplicationStartupAware。如果是其中之一,则会调用对应的setXXX方法如setApplicationContext。


ApplicationObjectSupport实现了ApplicationContextAware接口setApplicationContext方法,如下图所示其提供了initApplicationContext方法供子类实现。

@Override
public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException {
  if (context == null && !isContextRequired()) {
    // Reset internal context state.
    this.applicationContext = null;
    this.messageSourceAccessor = null;
  }
  else if (this.applicationContext == null) {
    // Initialize with passed-in context.
    if (!requiredContextClass().isInstance(context)) {
      throw new ApplicationContextException(
          "Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]");
    }
    this.applicationContext = context;
    this.messageSourceAccessor = new MessageSourceAccessor(context);
    // 这里是核心
    initApplicationContext(context);
  }
  else {
    // Ignore reinitialization if same context passed in.
    if (this.applicationContext != context) {
      throw new ApplicationContextException(
          "Cannot reinitialize with different application context: current one is [" +
          this.applicationContext + "], passed-in one is [" + context + "]");
    }
  }
}

在本文HandlerMapping体系中,AbstractHandlerMapping、AbstractDetectingUrlHandlerMapping以及SimpleUrlHandlerMapping实现了这三个方法


故而当BeanNameUrlHandlerMapping或者SimpleUrlHandlerMapping实例化时,就会触发setApplicationContext方法,然后触发initApplicationContext方法。


目录
相关文章
|
容器
SpringMVC常见组件之HandlerExceptionResolver分析-2
SpringMVC常见组件之HandlerExceptionResolver分析-2
77 0
|
7月前
|
设计模式
SpringMVC常见组件之DataBinder数据绑定器分析
SpringMVC常见组件之DataBinder数据绑定器分析
333 0
|
XML 缓存 Java
SpringMVC常见组件之ViewResolver分析
本文我们尝试总结分析SpringMVC体系中的视图解析器-ViewResolver。其根据name解析视图View,通常鼓励实现类考虑国际化策略实现。
113 0
|
7月前
|
XML 缓存 前端开发
SpringMVC常见组件之HandlerAdapter分析
SpringMVC常见组件之HandlerAdapter分析
96 0
|
XML 前端开发 Java
SpringMVC常见组件之View分析
SpringMVC常见组件之View分析
101 0
|
JSON 前端开发 Java
SpringMVC常见组件之HandlerExceptionResolver分析-1
SpringMVC常见组件之HandlerExceptionResolver分析-1
212 0
java202304java学习笔记第六十二天-ssm-springMvc中组件分析
java202304java学习笔记第六十二天-ssm-springMvc中组件分析
46 0
|
存储 设计模式 前端开发
浅谈SpringMVC五大组件以及对执行原理的分析。
Spring MVC是包含在spring中的一个基于MVC设计思想的Web应用程序框架,目的是简化开发工作,提高开发效率。
浅谈SpringMVC五大组件以及对执行原理的分析。
|
前端开发 Java 程序员
CORS跨域资源共享(三):@CrossOrigin/CorsFilter处理跨域请求示例及原理分析【享学Spring MVC】(下)
CORS跨域资源共享(三):@CrossOrigin/CorsFilter处理跨域请求示例及原理分析【享学Spring MVC】(下)
CORS跨域资源共享(三):@CrossOrigin/CorsFilter处理跨域请求示例及原理分析【享学Spring MVC】(下)
|
前端开发 Java Spring
CORS跨域资源共享(三):@CrossOrigin/CorsFilter处理跨域请求示例及原理分析【享学Spring MVC】(中)
CORS跨域资源共享(三):@CrossOrigin/CorsFilter处理跨域请求示例及原理分析【享学Spring MVC】(中)
CORS跨域资源共享(三):@CrossOrigin/CorsFilter处理跨域请求示例及原理分析【享学Spring MVC】(中)