您的位置:首页 > 编程语言 > Java开发

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

2015-09-27 14:06 603 查看

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<T, HandlerMethod> handlerMethods和LinkedMultiValueMap<String, T> 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.
*
* <p>Note: This base class does <i>not</i> 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<Object> interceptors = new ArrayList<Object>();

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

private final List<MappedInterceptor> mappedInterceptors = new ArrayList<MappedInterceptor>();//当前hm配置的所有拦截器,用来和处理器封装处理器链

/**
* Specify the order value for this HandlerMapping bean.
* <p>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.
* <p>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).
* <p>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
* <i>undecoded</i> by the Servlet API, in contrast to the servlet path.
* <p>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.
* <p>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.
* <p>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.
* <p>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}).
* <p>Will be invoked before {@link #initInterceptors()} adapts the specified
* interceptors into {@link HandlerInterceptor} instances.
* <p>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<Object> interceptors) {
}

/**
* Detect beans of type {@link MappedInterceptor} and add them to the list of mapped interceptors.
* <p>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<MappedInterceptor> 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.
* <p>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.
* <p>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.
* <p>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.
* <p><b>NOTE:</b> 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.
* <p>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}.
*
* <p>For each registered handler method, a unique mapping is maintained with
* subclasses defining the details of the mapping type {@code <T>}.
*
* @param <T> 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<T> 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.
* <p>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.
* <p>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<T, HandlerMethod> handlerMethods = new LinkedHashMap<T, HandlerMethod>();
//路径和映射信息的关系(注意是一对多的关系),快速匹配使用
private final MultiValueMap<String, T> urlMap = new LinkedMultiValueMap<String, T>();

/**
* Extract and return the URL paths contained in a mapping.
*/
protected abstract Set<String> 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<T, HandlerMethod> 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<Match> matches = new ArrayList<Match>();
//1.首先尝试直接拿路径匹配,这是理性的情况下,匹配速度最快
List<T> 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<Match> 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<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.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<T> 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<Match> {

private final Comparator<T> comparator;

public MatchComparator(Comparator<T> 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:
* <ol>
* 	<li>{@link PatternsRequestCondition}
* 	<li>{@link RequestMethodsRequestCondition}
* 	<li>{@link ParamsRequestCondition}
* 	<li>{@link HeadersRequestCondition}
* 	<li>{@link ConsumesRequestCondition}
* 	<li>{@link ProducesRequestCondition}
* 	<li>{@code RequestCondition} (optional, custom request condition)
* </ol>
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @since 3.1
*/
public final class RequestMappingInfo implements RequestCondition<RequestMappingInfo> {

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

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: