RequestMappingInfoHandlerMapping 在处理http请求的时候, 如果 请求url 有后缀,如果找不到精确匹配的那个@RequestMapping方法。
那么,就把后缀去掉,然后.* 去匹配,这样,一般都可以匹配。 比如有一个@RequestMapping("/rest"), 那么精确匹配的情况下, 只会匹配/rest请求。 但如果我前端发来一个 /rest.abcdef 这样的请求, 又没有配置 @RequestMapping("/rest.abcdef") 这样映射的情况下, 那么@RequestMapping("/rest") 就会生效。

原理呢?处理链是这样的:
at org.springframework.web.servlet.mvc.condition.PatternsRequestCondition.getMatchingPattern(PatternsRequestCondition.java:254)
at org.springframework.web.servlet.mvc.condition.PatternsRequestCondition.getMatchingPatterns(PatternsRequestCondition.java:230)
at org.springframework.web.servlet.mvc.condition.PatternsRequestCondition.getMatchingCondition(PatternsRequestCondition.java:210)
at org.springframework.web.servlet.mvc.method.RequestMappingInfo.getMatchingCondition(RequestMappingInfo.java:214)
at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.getMatchingMapping(RequestMappingInfoHandlerMapping.java:79)
at org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping.getMatchingMapping(RequestMappingInfoHandlerMapping.java:56)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.addMatchingMappings(AbstractHandlerMethodMapping.java:358)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.lookupHandlerMethod(AbstractHandlerMethodMapping.java:328)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:299)
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.getHandlerInternal(AbstractHandlerMethodMapping.java:57)
at org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.java:299)
at org.springframework.web.servlet.DispatcherServlet.getHandler(DispatcherServlet.java:1104)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:916)

关键是PatternsRequestCondition, 具体来说是这个方法

AbstractHandlerMethodMapping 的getHandlerInternal:
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = this.getUrlPathHelper().getLookupPathForRequest(request);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Looking up handler method for path " + lookupPath);
} HandlerMethod handlerMethod = this.lookupHandlerMethod(lookupPath, request);// 这里是关键,它去寻找,找到了就找到了,找不到就不会再去寻找了
if (this.logger.isDebugEnabled()) {
if (handlerMethod != null) {
this.logger.debug("Returning handler method [" + handlerMethod + "]");
} else {
this.logger.debug("Did not find handler method for [" + lookupPath + "]");
}
} return handlerMethod != null ? handlerMethod.createWithResolvedBean() : null;
} protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<AbstractHandlerMethodMapping<T>.Match> matches = new ArrayList();
List<T> directPathMatches = (List)this.urlMap.get(lookupPath); // directPathMatches, 直接匹配, 也可以说是 精确匹配
if (directPathMatches != null) {
this.addMatchingMappings(directPathMatches, matches, request);// 如果能够精确匹配, 就会进来这里
} if (matches.isEmpty()) {
this.addMatchingMappings(this.handlerMethods.keySet(), matches, request);// 如果无法精确匹配, 就会进来这里
} if (!matches.isEmpty()) {
Comparator<AbstractHandlerMethodMapping<T>.Match> comparator = new AbstractHandlerMethodMapping.MatchComparator(this.getMappingComparator(request));
Collections.sort(matches, comparator);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Found " + matches.size() + " matching mapping(s) for [" + lookupPath + "] : " + matches);
} AbstractHandlerMethodMapping<T>.Match bestMatch = (AbstractHandlerMethodMapping.Match)matches.get(0);
if (matches.size() > 1) {
AbstractHandlerMethodMapping<T>.Match secondBestMatch = (AbstractHandlerMethodMapping.Match)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 + "}");
}
} this.handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
} else {
return this.handleNoMatch(this.handlerMethods.keySet(), lookupPath, request);
}
} public List<String> getMatchingPatterns(String lookupPath) {
List<String> matches = new ArrayList();
Iterator var3 = this.patterns.iterator(); while(var3.hasNext()) {
String pattern = (String)var3.next(); // pattern 是 @RequestMapping 提供的映射
String match = this.getMatchingPattern(pattern, lookupPath); // lookupPath + .* 后能够匹配pattern, 那么就不为空
if (match != null) {
matches.add(match);// 对于有后缀的情况, .* 后
}
} Collections.sort(matches, this.pathMatcher.getPatternComparator(lookupPath));
return matches;
} 最关键是这里 getMatchingPatterns :
private String getMatchingPattern(String pattern, String lookupPath) {
if (pattern.equals(lookupPath)) {
return pattern;
} else {
if (this.useSuffixPatternMatch) {
if (!this.fileExtensions.isEmpty() && lookupPath.indexOf(46) != -1) {
Iterator var5 = this.fileExtensions.iterator(); while(var5.hasNext()) {
String extension = (String)var5.next();
if (this.pathMatcher.match(pattern + extension, lookupPath)) {
return pattern + extension;
}
}
} else {
boolean hasSuffix = pattern.indexOf(46) != -1;
if (!hasSuffix && this.pathMatcher.match(pattern + ".*", lookupPath)) {
return pattern + ".*"; // 关键是这里
}
}
} if (this.pathMatcher.match(pattern, lookupPath)) {
return pattern;
} else {
return this.useTrailingSlashMatch && !pattern.endsWith("/") && this.pathMatcher.match(pattern + "/", lookupPath) ? pattern + "/" : null;
}
}
}

而对于AbstractUrlHandlerMapping ,匹配不上就是匹配不上, 不会进行 +.* 后在匹配。

关键方法是这个:

    protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
if (handler instanceof String) {
String handlerName = (String)handler;
handler = this.getApplicationContext().getBean(handlerName);
} this.validateHandler(handler, request);
return this.buildPathExposingHandler(handler, urlPath, urlPath, (Map)null);
} else {
List<String> matchingPatterns = new ArrayList();
Iterator var5 = this.handlerMap.keySet().iterator(); while(var5.hasNext()) {
String registeredPattern = (String)var5.next();
if (this.getPathMatcher().match(registeredPattern, urlPath)) {
matchingPatterns.add(registeredPattern);
}
} String bestPatternMatch = null;
Comparator<String> patternComparator = this.getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
Collections.sort(matchingPatterns, patternComparator);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
} bestPatternMatch = (String)matchingPatterns.get(0);
} if (bestPatternMatch != null) {
handler = this.handlerMap.get(bestPatternMatch);
String pathWithinMapping;
if (handler instanceof String) {
pathWithinMapping = (String)handler;
handler = this.getApplicationContext().getBean(pathWithinMapping);
} this.validateHandler(handler, request);
pathWithinMapping = this.getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);
Map<String, String> uriTemplateVariables = new LinkedHashMap();
Iterator var9 = matchingPatterns.iterator(); while(var9.hasNext()) {
String matchingPattern = (String)var9.next();
if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) {
Map<String, String> vars = this.getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
Map<String, String> decodedVars = this.getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
} if (this.logger.isDebugEnabled()) {
this.logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
} return this.buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
} else {
return null;
}
}
}

当然, 或许我们可以设置自定义的PathMatcher ,从而到达目的。 默认的 是AntPathMatcher 。

Spring MVC 之 请求url 带后缀的情况的更多相关文章

  1. spring MVC处理请求过程及配置详解

    本文主要梳理下Spring MVC处理http请求的过程,以及配置servlet及业务application需要的常用标签,及其包含的意义. spring MVC处理请求过程 首先看一个整体图 简单说 ...

  2. spring MVC处理请求过程

    spring MVC处理请求过程 首先看一个整体图 简单说下各步骤: handlerMapping handlerMapping将请求映射到处理器,即图中的HandlerExecutionChain. ...

  3. spring mvc get请求也可以接受DTO对象

    spring mvc get请求也可以接受DTO对象,比如:url上面你还是将参数&符号连接起来,并自动封装进一个DTO对象里. 只有@RequestBody注解spring mvc才会从ht ...

  4. 使用Spring mvc接收整个url地址及参数时注意事项

    使用Spring mvc接收整个url地址及参数时注意事项:url= http://baidu?oid=9525c1f2b2cd45019b30a37bead6ebbb&td=2015-08- ...

  5. 跟踪spring MVC的请求

    当我们点击一个超链接时,spring MVC在后台都做了些什么呢,今天就来看看后台都干了啥 首先需要在web.xml里配置一下:

  6. Spring mvc源码url路由-我们到底能走多远系列(38)

    我们到底能走多远系列38 扯淡: 马航的事,挺震惊的.还是多多珍惜身边的人吧. 主题: Spring mvc 作为表现层的框架,整个流程是比较好理解的,毕竟我们做web开发的,最早也经常接触的就是一个 ...

  7. spring mvc ajax请求

    jar包中增加 jackson-annotations-2.5.0.jar jackson-core-2.5.0.jar jackson-databind-2.5.0.jar springmvx.xm ...

  8. spring mvc get请求中文乱码问题

    使用Spring MVC进行get请求时发现get请求带上中文参数,后台收到的是乱码,即使加了encoding filter也没用. 原因是,encoding filter 是针对post请求的,to ...

  9. Spring MVC 之请求参数和路径变量

    请求参数和路径变量都可以用于发送值给服务器.二者都是URL的一部分.请求参数采用key=value形式,并用“&”分隔. 例如,下面的URL带有一个名为productId的请求参数,其值为3: ...

随机推荐

  1. fastTime从后台传过来显示格式的处理

    1.可以在控制台用syso查看 QuestionFeedbackView qfv = on_LineManageService.getBringupHead(projectNo);//qfv是一个内含 ...

  2. touchend偶尔不触发(待解决)

    新闻流,实现tab横向切换效果,出现偶尔切到一半,手指移开后,没有跳转到上一个或下一个tab,而是持续在当前切了一半的位置. 找到原因: 没有切换的时候,touchend都没有触发. 网上找到的解决办 ...

  3. 利用ASK/OOK 发射模块,实现信号重放

    本文以打开无线控制的电动车库卷帘门为目标,深入研究了ASK/OOK的编/解码,并用树莓派+五元钱的ASK/OOK 发射模块 背景 车库装了电动卷帘门,为了了解其安全性,也是为了能自主控制,研究了下其遥 ...

  4. 11--Python入门--面向对象

    面向对象是Python的特点.面向对象主要通过类class的定义来实现.类class是用来描述具有相同属性和方法的对象的集合.类定义了该集合中的每个对象的共有属性和方法可以将类理解为一个模块,模块中包 ...

  5. SpringBoot的学习【1.初学之HelloWorld】

    1.创建Maven项目. 2.引入pom依赖 在springboot官网中找到简单的依赖模板 <parent> <groupId>org.springframework.boo ...

  6. C# 图片处理方法 整理汇总

    /// <summary> /// 图片转为base64编码字符 /// </summary> /// <param name="path">图 ...

  7. Gym - 100783G:Playing With Geometry (几何 离散化 )

    pro:给定规则的多边形,规则是指顶点都在整点上,而且是相互垂直的边的交点. 现在给定两个多边形A,B,问A,B缩小,旋转后是否可以变为同一个图形. sol:缩小的话,直接离散化即可,就可以去掉没用的 ...

  8. 阿里java开发规范 强制约束

    http://news.51cto.com/art/201901/591018.htm  :阿里开发强制要求的11条索引创建规范,提高性能 https://blog.csdn.net/Lujunwei ...

  9. 全志A33 lichee 修改开机图片

    开发平台 * 芯灵思SinlinxA33开发板 淘宝店铺: https://sinlinx.taobao.com/ 嵌入式linux 开发板交流 QQ:641395230 首先要知道开机图片存在哪里? ...

  10. Go之unsafe.Pointer && uintptr 类型

    Go语言是个强类型语言.Go语言要求所有统一表达式的不同的类型之间必须做显示的类型转换.而作为Go语言鼻祖的C语言是可以直接做隐式的类型转换的. 也就是说Go对类型要求严格,不同类型不能进行赋值操作. ...