SpringMVC那些事-请求映射匹配-处理器匹配

1.概述

2.主要过程

3.分析

4.相关类

5.部分源码注释


1.概述
根据MVC的概念,我们知道,请求到服务器后都需要经过控制器.这就需要一种机制把请求准确的调用控制器,
也就是需要明确哪个请求要调用哪个处理器.一般的MVC都有自己处理请求和控制器之间的关系映射的方法.

2.主要工作过程
A.根据hm中的request(url,method,header等)根据urlMap和handlerMethods查找合适的HandlerMethod(处理器)
B.然后找到HandlerMethod之后,生成一个HandlerExecutionChain,遍历拦截器,把处理器需要使用的拦截器放入.


3.概述
Nodejs中一般把这种映射关系叫路由router,大多数仅仅支持url的匹配
在SpringMVC, 使用@RequestMapping注解, 支持url, header, method等参数组合匹配, 这样会达到精确的匹配效果.
例如:@RequestMapping(value = "methods", method = { RequestMethod.GET, RequestMethod.POST }, params = { "id=007", "name" })

SpringMVC在把Controller注入容器的时候,会把使用@RequestMapping注解的信息解析,保存到RequestMappingInfo这个类中,
也就是RequestMappingInfo表示一个@RequestMapping.

在SpringMVC中, 前面已经说过,使用HandlerMethod表示处理器.

另一个问题来了,我们是在处理器方法上使用@RequestMapping注解的,也就是默认了@RequestMapping注解的配置信息和当前的处理器方法是
绑定的,例如:
@RequestMapping(value = "params", params = { "id=007", "name" })
public String paramsMapping(HttpServletRequest req, Model model) {
}

最终的表现形式是:RequestMappingInfo -> HandlerMethod
也就是说, 我们需要这样: 找到RequestMappingInfo,就能找到对应的HandlerMethod.

所以我们需要在RequestMappingInfo和HandlerMethod之间建立一种关系,说白了就是key-value的形式, OK, 没错用map保存就可以了.

在AbstractHandlerMethodMapping中有这两个属性:LinkedHashMap handlerMethods和LinkedMultiValueMap urlMap
查找最合适的处理器将使用这两个重要的参数.urlMap直接拿当前的路径直接匹配(这一,一个key对应多个value),这是最好的情况,当匹配不到的时候会遍历所有的映射.

RequestMappingHandlerMapping继承了前者,T 为 RequestMappingInfo.

过程解析(使用的是RequestMappingHandlerMapping)
根据request(url,method,header等)和urlMap和handlerMethods查找目标HandlerMethod(处理器)
然后找到处理器之后,生成一个HandlerExecutionChain,遍历所有的拦截器,把处理器需要使用的拦截器放入.
  ->dispatcherservlet.getHandler
  ->hm.getHandler
  ->AbstractHandlerMapping.getHandler
  ->AbstractHandlerMethodMapping.getHandlerInternal/AbstractHandlerMapping.getDefaultHandler //如果找不到合适的处理器,就返回默认的处理器
  ->AbstractHandlerMethodMapping.lookupHandlerMethod//匹配合适的处理器(关键部分,使用urlMap和handlerMethods查找)
  ->AbstractHandlerMapping.getHandlerExecutionChain(handler, request)//遍历注册的拦截器,根据lookupPath和拦截器路径匹配,合适的就加进去(注意一些内置的拦截器)

4.相关类
RequestMappingInfo @RequestMapping注解配置参数的封装
  PatternsRequestCondition patternsCondition;//value = {"path"}:路径正则匹配,路径配置可以使用{id}这样的restful风格
  RequestMethodsRequestCondition methodsCondition;//例如method = {RequestMethod.GET}
  ParamsRequestCondition paramsCondition;//例如params = {"name=weber"}
  HeadersRequestCondition headersCondition;//例如headers={"content-type=text/*"}
  ConsumesRequestCondition consumesCondition;//例如consumes = {"text/plain", "application/json"}消费者,其实就是请求需要的类型,例如发送json数据到服务器
  ProducesRequestCondition producesCondition;//例如produces = {"text/plain", "application/json"}生产者,相应的类型,例如返回json数据给客户端
  RequestConditionHolder customConditionHolder;//

HandlerMethod 表示一个类函数的封装类,包括函数的Method和所属的类,和函数的参数等.例如Controller中的每一个处理器可以用它表示,请看hm中和ha具体使用
  Object bean;//控制器
  BeanFactory beanFactory;//XMLApplicationContext
  Method method;//处理器
  bridgedMethod;//桥接处理器
  MethodParameter[] parameters;//处理器参数

Match AbstractHandlerMethodMapping内部类,保存匹配的映射和处理器
  T mapping

  HandlerMethod handlerMethod


部分源码

AbstractHandlerMapping


/**
 * Abstract base class for {@link org.springframework.web.servlet.HandlerMapping}
 * implementations. Supports ordering, a default handler, handler interceptors,
 * including handler interceptors mapped by path patterns.
 *
 * 

Note: This base class does not support exposure of the * {@link #PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE}. Support for this attribute * is up to concrete subclasses, typically based on request URL mappings. * * @author Juergen Hoeller * @author Rossen Stoyanchev * @since 07.04.2003 * @see #getHandlerInternal * @see #setDefaultHandler * @see #setAlwaysUseFullPath * @see #setUrlDecode * @see org.springframework.util.AntPathMatcher * @see #setInterceptors * @see org.springframework.web.servlet.HandlerInterceptor */ public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {//注意这个order,说明hm也是按照order顺序优先级匹配的 //实现了order接口,在dispacherServlet中初始化的时候,我们会看到,会对它们进行排序 private int order = Integer.MAX_VALUE; // default: same as non-Ordered private Object defaultHandler;//默认处理器 private UrlPathHelper urlPathHelper = new UrlPathHelper();//URL匹配工具 private PathMatcher pathMatcher = new AntPathMatcher();//路径匹配器 private final List interceptors = new ArrayList(); private final List adaptedInterceptors = new ArrayList(); private final List mappedInterceptors = new ArrayList();//当前hm配置的所有拦截器,用来和处理器封装处理器链 /** * Specify the order value for this HandlerMapping bean. *

Default value is {@code Integer.MAX_VALUE}, meaning that it's non-ordered. * @see org.springframework.core.Ordered#getOrder() */ public final void setOrder(int order) { this.order = order; } @Override public final int getOrder() { return this.order; } /** * Set the default handler for this handler mapping. * This handler will be returned if no specific mapping was found. *

Default is {@code null}, indicating no default handler. */ public void setDefaultHandler(Object defaultHandler) { this.defaultHandler = defaultHandler; } /** * Return the default handler for this handler mapping, * or {@code null} if none. */ public Object getDefaultHandler() { return this.defaultHandler; } /** * Set if URL lookup should always use the full path within the current servlet * context. Else, the path within the current servlet mapping is used if applicable * (that is, in the case of a ".../*" servlet mapping in web.xml). *

Default is "false". * @see org.springframework.web.util.UrlPathHelper#setAlwaysUseFullPath */ public void setAlwaysUseFullPath(boolean alwaysUseFullPath) { this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath); } /** * Set if context path and request URI should be URL-decoded. Both are returned * undecoded by the Servlet API, in contrast to the servlet path. *

Uses either the request encoding or the default encoding according * to the Servlet spec (ISO-8859-1). * @see org.springframework.web.util.UrlPathHelper#setUrlDecode */ public void setUrlDecode(boolean urlDecode) { this.urlPathHelper.setUrlDecode(urlDecode); } /** * Set if ";" (semicolon) content should be stripped from the request URI. *

The default value is {@code true}. * @see org.springframework.web.util.UrlPathHelper#setRemoveSemicolonContent(boolean) */ public void setRemoveSemicolonContent(boolean removeSemicolonContent) { this.urlPathHelper.setRemoveSemicolonContent(removeSemicolonContent); } /** * Set the UrlPathHelper to use for resolution of lookup paths. *

Use this to override the default UrlPathHelper with a custom subclass, * or to share common UrlPathHelper settings across multiple HandlerMappings * and MethodNameResolvers. */ public void setUrlPathHelper(UrlPathHelper urlPathHelper) { Assert.notNull(urlPathHelper, "UrlPathHelper must not be null"); this.urlPathHelper = urlPathHelper; } /** * Return the UrlPathHelper implementation to use for resolution of lookup paths. */ public UrlPathHelper getUrlPathHelper() { return urlPathHelper; } /** * Set the PathMatcher implementation to use for matching URL paths * against registered URL patterns. Default is AntPathMatcher. * @see org.springframework.util.AntPathMatcher */ public void setPathMatcher(PathMatcher pathMatcher) { Assert.notNull(pathMatcher, "PathMatcher must not be null"); this.pathMatcher = pathMatcher; } /** * Return the PathMatcher implementation to use for matching URL paths * against registered URL patterns. */ public PathMatcher getPathMatcher() { return this.pathMatcher; } /** * Set the interceptors to apply for all handlers mapped by this handler mapping. *

Supported interceptor types are HandlerInterceptor, WebRequestInterceptor, and MappedInterceptor. * Mapped interceptors apply only to request URLs that match its path patterns. * Mapped interceptor beans are also detected by type during initialization. * @param interceptors array of handler interceptors, or {@code null} if none * @see #adaptInterceptor * @see org.springframework.web.servlet.HandlerInterceptor * @see org.springframework.web.context.request.WebRequestInterceptor */ public void setInterceptors(Object[] interceptors) { this.interceptors.addAll(Arrays.asList(interceptors)); } /** * Initializes the interceptors. * @see #extendInterceptors(java.util.List) * @see #initInterceptors() */ @Override protected void initApplicationContext() throws BeansException { extendInterceptors(this.interceptors); detectMappedInterceptors(this.mappedInterceptors); initInterceptors(); } /** * Extension hook that subclasses can override to register additional interceptors, * given the configured interceptors (see {@link #setInterceptors}). *

Will be invoked before {@link #initInterceptors()} adapts the specified * interceptors into {@link HandlerInterceptor} instances. *

The default implementation is empty. * @param interceptors the configured interceptor List (never {@code null}), allowing * to add further interceptors before as well as after the existing interceptors */ protected void extendInterceptors(List interceptors) { } /** * Detect beans of type {@link MappedInterceptor} and add them to the list of mapped interceptors. *

This is called in addition to any {@link MappedInterceptor}s that may have been provided * via {@link #setInterceptors}, by default adding all beans of type {@link MappedInterceptor} * from the current context and its ancestors. Subclasses can override and refine this policy. * @param mappedInterceptors an empty list to add {@link MappedInterceptor} instances to */ protected void detectMappedInterceptors(List mappedInterceptors) { mappedInterceptors.addAll( BeanFactoryUtils.beansOfTypeIncludingAncestors( getApplicationContext(), MappedInterceptor.class, true, false).values()); } /** * Initialize the specified interceptors, checking for {@link MappedInterceptor}s and * adapting {@link HandlerInterceptor}s and {@link WebRequestInterceptor}s if necessary. * @see #setInterceptors * @see #adaptInterceptor */ 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"); } if (interceptor instanceof MappedInterceptor) { this.mappedInterceptors.add((MappedInterceptor) interceptor); } else { this.adaptedInterceptors.add(adaptInterceptor(interceptor)); } } } } /** * Adapt the given interceptor object to the {@link HandlerInterceptor} interface. *

By default, the supported interceptor types are {@link HandlerInterceptor} * and {@link WebRequestInterceptor}. Each given {@link WebRequestInterceptor} * will be wrapped in a {@link WebRequestHandlerInterceptorAdapter}. * Can be overridden in subclasses. * @param interceptor the specified interceptor object * @return the interceptor wrapped as HandlerInterceptor * @see org.springframework.web.servlet.HandlerInterceptor * @see org.springframework.web.context.request.WebRequestInterceptor * @see WebRequestHandlerInterceptorAdapter */ 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()); } } /** * Return the adapted interceptors as {@link HandlerInterceptor} array. * @return the array of {@link HandlerInterceptor}s, or {@code null} if none */ protected final HandlerInterceptor[] getAdaptedInterceptors() { int count = this.adaptedInterceptors.size(); return (count > 0 ? this.adaptedInterceptors.toArray(new HandlerInterceptor[count]) : null); } /** * Return all configured {@link MappedInterceptor}s as an array. * @return the array of {@link MappedInterceptor}s, or {@code null} if none */ protected final MappedInterceptor[] getMappedInterceptors() { int count = this.mappedInterceptors.size(); return (count > 0 ? this.mappedInterceptors.toArray(new MappedInterceptor[count]) : null); } /** * Look up a handler for the given request, falling back to the default * handler if no specific one is found. * @param request current HTTP request * @return the corresponding handler instance, or the default handler * @see #getHandlerInternal */ @Override public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { //获取handler也就是处理器 Object handler = getHandlerInternal(request); if (handler == null) { handler = getDefaultHandler();//如果解析不出处理器,就使用默认处理器,默认处理器一般为null } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName);//如果解析出来的是处理器的名字,就从IOC中取出来 } return getHandlerExecutionChain(handler, request);//得到处理器后找到处理器对应的拦截器,封装成处理器连返回 } /** * Look up a handler for the given request, returning {@code null} if no * specific one is found. This method is called by {@link #getHandler}; * a {@code null} return value will lead to the default handler, if one is set. *

Note: This method may also return a pre-built {@link HandlerExecutionChain}, * combining a handler object with dynamically determined interceptors. * Statically specified interceptors will get merged into such an existing chain. * @param request current HTTP request * @return the corresponding handler instance, or {@code null} if none found * @throws Exception if there is an internal error */ protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception; /** * Build a {@link HandlerExecutionChain} for the given handler, including * applicable interceptors. *

The default implementation builds a standard {@link HandlerExecutionChain} * with the given handler, the handler mapping's common interceptors, and any * {@link MappedInterceptor}s matching to the current request URL. Subclasses * may override this in order to extend/rearrange the list of interceptors. *

NOTE: The passed-in handler object may be a raw handler or a * pre-built {@link HandlerExecutionChain}. This method should handle those * two cases explicitly, either building a new {@link HandlerExecutionChain} * or extending the existing chain. *

For simply adding an interceptor in a custom subclass, consider calling * {@code super.getHandlerExecutionChain(handler, request)} and invoking * {@link HandlerExecutionChain#addInterceptor} on the returned chain object. * @param handler the resolved handler instance (never {@code null}) * @param request current HTTP request * @return the HandlerExecutionChain (never {@code null}) * @see #getAdaptedInterceptors() */ protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); chain.addInterceptors(getAdaptedInterceptors()); //没有什么好说的,就是拿到当前请求路径的path,根据这个path匹配拦截器,我们配置拦截器的时候,指定了每一个拦截器 //拦截那些路径,需要注意的是,拦截器只能依据路径匹配, 没有RequestMapping那么精确 String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) { if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } return chain; } }
AbstractHandlerMethodMapping

/**
 * Abstract base class for {@link HandlerMapping} implementations that define a
 * mapping between a request and a {@link HandlerMethod}.
 *
 * 

For each registered handler method, a unique mapping is maintained with * subclasses defining the details of the mapping type {@code }. * * @param The mapping for a {@link HandlerMethod} containing the conditions * needed to match the handler method to incoming request. * * @author Arjen Poutsma * @author Rossen Stoyanchev * @author Juergen Hoeller * @since 3.1 */ public abstract class AbstractHandlerMethodMapping extends AbstractHandlerMapping implements InitializingBean { /** * Bean name prefix for target beans behind scoped proxies. Used to exclude those * targets from handler method detection, in favor of the corresponding proxies. *

We're not checking the autowire-candidate status here, which is how the * proxy target filtering problem is being handled at the autowiring level, * since autowire-candidate may have been turned to {@code false} for other * reasons, while still expecting the bean to be eligible for handler methods. *

Originally defined in {@link org.springframework.aop.scope.ScopedProxyUtils} * but duplicated here to avoid a hard dependency on the spring-aop module. */ private static final String SCOPED_TARGET_NAME_PREFIX = "scopedTarget."; private boolean detectHandlerMethodsInAncestorContexts = false; //映射信息和处理器的关系 private final Map handlerMethods = new LinkedHashMap(); //路径和映射信息的关系(注意是一对多的关系),快速匹配使用 private final MultiValueMap urlMap = new LinkedMultiValueMap(); /** * Extract and return the URL paths contained in a mapping. */ protected abstract Set getMappingPathPatterns(T mapping); /** * Invoked after all handler methods have been detected. * @param handlerMethods a read-only map with handler methods and mappings. */ protected void handlerMethodsInitialized(Map handlerMethods) { } /** * Look up a handler method for the given request. */ @Override protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception { //1.根据当前URL获取需要用来查找处理器的路径 String lookupPath = getUrlPathHelper().getLookupPathForRequest(request); if (logger.isDebugEnabled()) { logger.debug("Looking up handler method for path " + lookupPath); } //2.开始获取处理器 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); } /** * Look up the best-matching handler method for the current request. * If multiple matches are found, the best match is selected. * @param lookupPath mapping lookup path within the current servlet mapping * @param request the current request * @return the best-matching handler method, or {@code null} if no match * @see #handleMatch(Object, String, HttpServletRequest) * @see #handleNoMatch(Set, String, HttpServletRequest) */ protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception { List matches = new ArrayList(); //1.首先尝试直接拿路径匹配,这是理性的情况下,匹配速度最快 List directPathMatches = this.urlMap.get(lookupPath); if (directPathMatches != null) { addMatchingMappings(directPathMatches, matches, request); } if (matches.isEmpty()) { // No choice but to go through all mappings... //2.直接路径匹配失败,那就只能把所有注册的映射信息挨个匹配了,这样速度没有直接匹配那么快 addMatchingMappings(this.handlerMethods.keySet(), matches, request); } //可能会匹配到多个合适的映射,所以需要一种优先算法找到最佳匹配,这个过程使用了Comparator比较类,先排序,再找出最合适的, //是不是和我们的找数组最大最小值或者冒泡排序很像呢,哈哈 if (!matches.isEmpty()) { Comparator comparator = new MatchComparator(getMappingComparator(request)); Collections.sort(matches, comparator); if (logger.isTraceEnabled()) { logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches); } Match bestMatch = matches.get(0); if (matches.size() > 1) { Match secondBestMatch = matches.get(1); if (comparator.compare(bestMatch, secondBestMatch) == 0) { Method m1 = bestMatch.handlerMethod.getMethod(); Method m2 = secondBestMatch.handlerMethod.getMethod(); throw new IllegalStateException( "Ambiguous handler methods mapped for HTTP path '" + request.getRequestURL() + "': {" + m1 + ", " + m2 + "}"); } } handleMatch(bestMatch.mapping, lookupPath, request); return bestMatch.handlerMethod; } else { return handleNoMatch(handlerMethods.keySet(), lookupPath, request); } } private void addMatchingMappings(Collection mappings, List matches, HttpServletRequest request) { for (T mapping : mappings) { T match = getMatchingMapping(mapping, request); if (match != null) { matches.add(new Match(match, this.handlerMethods.get(mapping))); } } } /** * Check if a mapping matches the current request and return a (potentially * new) mapping with conditions relevant to the current request. * @param mapping the mapping to get a match for * @param request the current HTTP servlet request * @return the match, or {@code null} if the mapping doesn't match */ protected abstract T getMatchingMapping(T mapping, HttpServletRequest request); /** * Return a comparator for sorting matching mappings. * The returned comparator should sort 'better' matches higher. * @param request the current request * @return the comparator, never {@code null} */ protected abstract Comparator getMappingComparator(HttpServletRequest request); /** * Invoked when a matching mapping is found. * @param mapping the matching mapping * @param lookupPath mapping lookup path within the current servlet mapping * @param request the current request */ protected void handleMatch(T mapping, String lookupPath, HttpServletRequest request) { request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, lookupPath); } /** * A thin wrapper around a matched HandlerMethod and its mapping, for the purpose of * comparing the best match with a comparator in the context of the current request. */ private class Match { private final T mapping; private final HandlerMethod handlerMethod; public Match(T mapping, HandlerMethod handlerMethod) { this.mapping = mapping; this.handlerMethod = handlerMethod; } @Override public String toString() { return this.mapping.toString(); } } private class MatchComparator implements Comparator { private final Comparator comparator; public MatchComparator(Comparator comparator) { this.comparator = comparator; } @Override public int compare(Match match1, Match match2) { return this.comparator.compare(match1.mapping, match2.mapping); } } }


RequestMappingInfo

/**
 * Encapsulates the following request mapping conditions:
 * 
    *
  1. {@link PatternsRequestCondition} *
  2. {@link RequestMethodsRequestCondition} *
  3. {@link ParamsRequestCondition} *
  4. {@link HeadersRequestCondition} *
  5. {@link ConsumesRequestCondition} *
  6. {@link ProducesRequestCondition} *
  7. {@code RequestCondition} (optional, custom request condition) *
* * @author Arjen Poutsma * @author Rossen Stoyanchev * @since 3.1 */ public final class RequestMappingInfo implements RequestCondition { private final PatternsRequestCondition patternsCondition;//URL匹配 private final RequestMethodsRequestCondition methodsCondition;//http method匹配 private final ParamsRequestCondition paramsCondition;//请求参数匹配 private final HeadersRequestCondition headersCondition;//请求头匹配 private final ConsumesRequestCondition consumesCondition;//请求类型匹配, private final ProducesRequestCondition producesCondition;//返回类型匹配 private final RequestConditionHolder customConditionHolder;//暂时不知道什么东东 /** * Creates a new instance with the given request conditions. */ public RequestMappingInfo(PatternsRequestCondition patterns, RequestMethodsRequestCondition methods, ParamsRequestCondition params, HeadersRequestCondition headers, ConsumesRequestCondition consumes, ProducesRequestCondition produces, RequestCondition custom) { this.patternsCondition = (patterns != null ? patterns : new PatternsRequestCondition()); this.methodsCondition = (methods != null ? methods : new RequestMethodsRequestCondition()); this.paramsCondition = (params != null ? params : new ParamsRequestCondition()); this.headersCondition = (headers != null ? headers : new HeadersRequestCondition()); this.consumesCondition = (consumes != null ? consumes : new ConsumesRequestCondition()); this.producesCondition = (produces != null ? produces : new ProducesRequestCondition()); this.customConditionHolder = new RequestConditionHolder(custom); } /** * Re-create a RequestMappingInfo with the given custom request condition. */ public RequestMappingInfo(RequestMappingInfo info, RequestCondition customRequestCondition) { this(info.patternsCondition, info.methodsCondition, info.paramsCondition, info.headersCondition, info.consumesCondition, info.producesCondition, customRequestCondition); } /** * Returns the URL patterns of this {@link RequestMappingInfo}; * or instance with 0 patterns, never {@code null}. */ public PatternsRequestCondition getPatternsCondition() { return this.patternsCondition; } /** * Returns the HTTP request methods of this {@link RequestMappingInfo}; * or instance with 0 request methods, never {@code null}. */ public RequestMethodsRequestCondition getMethodsCondition() { return this.methodsCondition; } /** * Returns the "parameters" condition of this {@link RequestMappingInfo}; * or instance with 0 parameter expressions, never {@code null}. */ public ParamsRequestCondition getParamsCondition() { return this.paramsCondition; } /** * Returns the "headers" condition of this {@link RequestMappingInfo}; * or instance with 0 header expressions, never {@code null}. */ public HeadersRequestCondition getHeadersCondition() { return this.headersCondition; } /** * Returns the "consumes" condition of this {@link RequestMappingInfo}; * or instance with 0 consumes expressions, never {@code null}. */ public ConsumesRequestCondition getConsumesCondition() { return this.consumesCondition; } /** * Returns the "produces" condition of this {@link RequestMappingInfo}; * or instance with 0 produces expressions, never {@code null}. */ public ProducesRequestCondition getProducesCondition() { return this.producesCondition; } /** * Returns the "custom" condition of this {@link RequestMappingInfo}; or {@code null}. */ public RequestCondition getCustomCondition() { return this.customConditionHolder.getCondition(); } /** * Combines "this" request mapping info (i.e. the current instance) with another request mapping info instance. *

Example: combine type- and method-level request mappings. * @return a new request mapping info instance; never {@code null} */ @Override public RequestMappingInfo combine(RequestMappingInfo other) { PatternsRequestCondition patterns = this.patternsCondition.combine(other.patternsCondition); 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(patterns, methods, params, headers, consumes, produces, custom.getCondition()); } /** * Checks if all conditions in this request mapping info match the provided request and returns * a potentially new request mapping info with conditions tailored to the current request. *

For example the returned instance may contain the subset of URL patterns that match to * the current request, sorted with best matching patterns on top. * @return a new instance in case all conditions match; or {@code null} otherwise */ @Override public RequestMappingInfo getMatchingCondition(HttpServletRequest request) { RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request); ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request); HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request); ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request); ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request); if (methods == null || params == null || headers == null || consumes == null || produces == null) { return null; } PatternsRequestCondition patterns = this.patternsCondition.getMatchingCondition(request); if (patterns == null) { return null; } RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request); if (custom == null) { return null; } return new RequestMappingInfo(patterns, methods, params, headers, consumes, produces, custom.getCondition()); } /** * Compares "this" info (i.e. the current instance) with another info in the context of a request. *

Note: It is assumed both instances have been obtained via * {@link #getMatchingCondition(HttpServletRequest)} to ensure they have conditions with * content relevant to current request. */ @Override public int compareTo(RequestMappingInfo other, HttpServletRequest request) { int result = this.patternsCondition.compareTo(other.getPatternsCondition(), request); if (result != 0) { return result; } result = this.paramsCondition.compareTo(other.getParamsCondition(), request); if (result != 0) { return result; } result = this.headersCondition.compareTo(other.getHeadersCondition(), request); if (result != 0) { return result; } result = this.consumesCondition.compareTo(other.getConsumesCondition(), request); if (result != 0) { return result; } result = this.producesCondition.compareTo(other.getProducesCondition(), request); if (result != 0) { return result; } result = this.methodsCondition.compareTo(other.getMethodsCondition(), request); if (result != 0) { return result; } result = this.customConditionHolder.compareTo(other.customConditionHolder, request); if (result != 0) { return result; } return 0; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj != null && obj instanceof RequestMappingInfo) { RequestMappingInfo other = (RequestMappingInfo) obj; return (this.patternsCondition.equals(other.patternsCondition) && this.methodsCondition.equals(other.methodsCondition) && this.paramsCondition.equals(other.paramsCondition) && this.headersCondition.equals(other.headersCondition) && this.consumesCondition.equals(other.consumesCondition) && this.producesCondition.equals(other.producesCondition) && this.customConditionHolder.equals(other.customConditionHolder)); } return false; } @Override public int hashCode() { return (this.patternsCondition.hashCode() * 31 + // primary differentiation this.methodsCondition.hashCode() + this.paramsCondition.hashCode() + this.headersCondition.hashCode() + this.consumesCondition.hashCode() + this.producesCondition.hashCode() + this.customConditionHolder.hashCode()); } @Override public String toString() { StringBuilder builder = new StringBuilder("{"); builder.append(this.patternsCondition); builder.append(",methods=").append(this.methodsCondition); builder.append(",params=").append(this.paramsCondition); builder.append(",headers=").append(this.headersCondition); builder.append(",consumes=").append(this.consumesCondition); builder.append(",produces=").append(this.producesCondition); builder.append(",custom=").append(this.customConditionHolder); builder.append('}'); return builder.toString(); } }



你可能感兴趣的:(SpringMVC)