背景

spring-boot的版本是2.1.4.RELEASE,spring的版本是5.1.6.RELEASE

一个例子如下:

@Configuration
@Import(WebMvcAutoConfiguration.EnableWebMvcConfiguration.class)
@SuppressWarnings("unchecked")
public class WebConfig implements WebMvcConfigurer, WebMvcRegistrations {
@Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new RequestMappingHandlerMapping();
}
} @RestController
public class ParamController {
@GetMapping(value = "/param/{param1}")
public String param(@PathVariable("param1") String param1) {
return param1;
}
} @SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}

启动一下,访问http://127.0.0.1:8080/param/hehehttp://127.0.0.1:8080/param/hehe.hehe都返回hehe

如果访问http://127.0.0.1:8080/param/hehe.hehe.hehe,它会返回hehe.hehe

所以会发现它把最后一个小数点后面的字符给截掉了,那如果我们想要获取完整的字符串,该怎么办呢?

探索

  1. 参数怎么来的

入口在InvocableHandlerMethod.invokeForRequest,如下:

是根据PathVariableMethodArgumentResolver.resolveName得来的,如下:



HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";

什么时候放进attributes里的?,在RequestMappingInfoHandlerMapping.handleMatch,如下:

  1. 参数怎么解析的
程序定义的:/param/{param1} -> /param/{param1}.*
接口传过来的:/param/hehe.hehe 以/分割,第一个字符串param里没有参数,所以会跳过,直接看第二个字符串:
{param1}.* -> pattern=(.*)\Q.\E.*
hehe.hehe param1=hehe

我们定义的是/param/{param1},怎么就变成了/param/{param1}.*?在PatternsRequestCondition.getMatchingPattern



可以看到如果useSuffixPatternMatch为true,并且指定的url里没有.,会在后缀自动增加.*

  1. useSuffixPatternMatch是在哪里设置的?
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
implements MatchableHandlerMapping, EmbeddedValueResolverAware { private boolean useSuffixPatternMatch = true; @Override
public void afterPropertiesSet() {
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setUrlPathHelper(getUrlPathHelper());
this.config.setPathMatcher(getPathMatcher());
this.config.setSuffixPatternMatch(this.useSuffixPatternMatch); // 这里设置了
this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
this.config.setContentNegotiationManager(getContentNegotiationManager()); super.afterPropertiesSet();
}
}

解决

  1. 修改WebConfiggetRequestMappingHandlerMapping
@Configuration
@Import(WebMvcAutoConfiguration.EnableWebMvcConfiguration.class)
@SuppressWarnings("unchecked")
public class WebConfig implements WebMvcConfigurer, WebMvcRegistrations {
@Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
RequestMappingHandlerMapping requestMappingHandlerMapping = new RequestMappingHandlerMapping();
requestMappingHandlerMapping.setUseSuffixPatternMatch(false);
return requestMappingHandlerMapping;
}
}
  1. 增加configurePathMatch方法
@Configuration
@Import(WebMvcAutoConfiguration.EnableWebMvcConfiguration.class)
@SuppressWarnings("unchecked")
public class WebConfig implements WebMvcConfigurer, WebMvcRegistrations {
@Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new RequestMappingHandlerMapping();
} @Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false);
}
}
  1. url中增加点

a. 中间的参数是不受影响的

@RestController
public class ParamController {
@GetMapping(value = "/param/{param1}/{param2}")
public String param(@PathVariable("param1") String param1, @PathVariable("param2") String param2) {
return param1 + " " + param2;
}
}

访问http://127.0.0.1:8080/param/hehe.hehe/hehe.hee返回hehe.hehe hehe

b. 增加点

@RestController
public class ParamController {
//@GetMapping(value = "/param/{param1}")
//public String param(@PathVariable("param1") String param1) {
// return param1;
//} @GetMapping(value = "/param/{param1}.{param2}")
public String param(@PathVariable("param1") String param1, @PathVariable("param2") String param2) {
return param1 + " " + param2;
}
}

访问http://127.0.0.1:8080/param/hehe.hehe返回hehe hehe

注意第一个方法和第二个方法不要同时出现,如果同时出现的话,则会访问第一个方法

@RestController
public class ParamController {
@GetMapping(value = "/param/{param1}")
public String param(@PathVariable("param1") String param1) {
return param1;
} @GetMapping(value = "/param/{param1}.{param2}")
public String param(@PathVariable("param1") String param1, @PathVariable("param2") String param2) {
return param1 + " " + param2;
}
}

访问http://127.0.0.1:8080/param/hehe.heh返回hehe

  1. 修改参数
@RestController
public class ParamController {
@GetMapping(value = "/param/{param1:.+}")
public String param(@PathVariable("param1") String param1) {
return param1;
}
}

访问http://127.0.0.1:8080/param/hehe.hehe,返回hehe.hehe

原因如下:

/param/{param1:.+} -> /param/{param1:.+}
/param/hehe.hehe {param1:.+} -> pattern=(.+)
hehe.hehe param1=hehe.hehe
得到pattern以及提取参数的类 AntPathStringMatcher
protected static class AntPathStringMatcher { private static final Pattern GLOB_PATTERN = Pattern.compile("\\?|\\*|\\{((?:\\{[^/]+?\\}|[^/{}]|\\\\[{}])+?)\\}"); private static final String DEFAULT_VARIABLE_PATTERN = "(.*)"; private final Pattern pattern; private final List<String> variableNames = new LinkedList<>(); public AntPathStringMatcher(String pattern) {
this(pattern, true);
} public AntPathStringMatcher(String pattern, boolean caseSensitive) {
StringBuilder patternBuilder = new StringBuilder();
Matcher matcher = GLOB_PATTERN.matcher(pattern);
int end = 0;
while (matcher.find()) {
patternBuilder.append(quote(pattern, end, matcher.start()));
String match = matcher.group();
if ("?".equals(match)) {
patternBuilder.append('.');
}
else if ("*".equals(match)) {
patternBuilder.append(".*");
}
else if (match.startsWith("{") && match.endsWith("}")) {
int colonIdx = match.indexOf(':');
if (colonIdx == -1) {
patternBuilder.append(DEFAULT_VARIABLE_PATTERN);
this.variableNames.add(matcher.group(1));
}
else {
String variablePattern = match.substring(colonIdx + 1, match.length() - 1);
patternBuilder.append('(');
patternBuilder.append(variablePattern);
patternBuilder.append(')');
String variableName = match.substring(1, colonIdx);
this.variableNames.add(variableName);
}
}
end = matcher.end();
}
patternBuilder.append(quote(pattern, end, pattern.length()));
this.pattern = (caseSensitive ? Pattern.compile(patternBuilder.toString()) :
Pattern.compile(patternBuilder.toString(), Pattern.CASE_INSENSITIVE));
} private String quote(String s, int start, int end) {
if (start == end) {
return "";
}
return Pattern.quote(s.substring(start, end));
} /**
* Main entry point.
* @return {@code true} if the string matches against the pattern, or {@code false} otherwise.
*/
public boolean matchStrings(String str, @Nullable Map<String, String> uriTemplateVariables) {
Matcher matcher = this.pattern.matcher(str);
if (matcher.matches()) {
if (uriTemplateVariables != null) {
// SPR-8455
if (this.variableNames.size() != matcher.groupCount()) {
throw new IllegalArgumentException("The number of capturing groups in the pattern segment " +
this.pattern + " does not match the number of URI template variables it defines, " +
"which can occur if capturing groups are used in a URI template regex. " +
"Use non-capturing groups instead.");
}
for (int i = 1; i <= matcher.groupCount(); i++) {
String name = this.variableNames.get(i - 1);
String value = matcher.group(i);
uriTemplateVariables.put(name, value);
}
}
return true;
}
else {
return false;
}
}
}

关于Spring中的useSuffixPatternMatch的更多相关文章

  1. Velocity初探小结--Velocity在spring中的配置和使用

    最近正在做的项目前端使用了Velocity进行View层的数据渲染,之前没有接触过,草草过了一遍,就上手开始写,现在又回头细致的看了一遍,做个笔记. velocity是一种基于java的模板引擎技术, ...

  2. Spring中Bean的作用域、生命周期

                                   Bean的作用域.生命周期 Bean的作用域 Spring 3中为Bean定义了5中作用域,分别为singleton(单例).protot ...

  3. Spring中Bean的实例化

                                    Spring中Bean的实例化 在介绍Bean的三种实例化的方式之前,我们首先需要介绍一下什么是Bean,以及Bean的配置方式. 如果 ...

  4. 模拟实现Spring中的注解装配

    本文原创,地址为http://www.cnblogs.com/fengzheng/p/5037359.html 在Spring中,XML文件中的bean配置是实现Spring IOC的核心配置文件,在 ...

  5. Spring中常见的bean创建异常

    Spring中常见的bean创建异常 1. 概述     本次我们将讨论在spring中BeanFactory创建bean实例时经常遇到的异常 org.springframework.beans.fa ...

  6. Spring中配置数据源的4种形式

    不管采用何种持久化技术,都需要定义数据源.Spring中提供了4种不同形式的数据源配置方式: spring自带的数据源(DriverManagerDataSource),DBCP数据源,C3P0数据源 ...

  7. spring中InitializingBean接口使用理解

    InitializingBean接口为bean提供了初始化方法的方式,它只包括afterPropertiesSet方法,凡是继承该接口的类,在初始化bean的时候会执行该方法. 测试程序如下: imp ...

  8. Quartz 在 Spring 中如何动态配置时间--转

    原文地址:http://www.iteye.com/topic/399980 在项目中有一个需求,需要灵活配置调度任务时间,并能自由启动或停止调度. 有关调度的实现我就第一就想到了Quartz这个开源 ...

  9. Spring中的cglib动态代理

    Spring中的cglib动态代理 cglib:Code Generation library, 基于ASM(java字节码操作码)的高性能代码生成包 被许多AOP框架使用 区别于JDK动态代理,cg ...

随机推荐

  1. IIS在ASP.NET Core下的两种部署模式

    KestrelServer最大的优势体现在它的跨平台的能力,如果ASP.NET CORE应用只需要部署在Windows环境下,IIS也是不错的选择.ASP.NET CORE应用针对IIS具有两种部署模 ...

  2. vulnhub靶机djinn:1渗透笔记

    djinn:1渗透笔记 靶机下载地址:https://www.vulnhub.com/entry/djinn-1,397/ 信息收集 首先我们嘚确保一点,kali机和靶机处于同一网段,查看kali i ...

  3. WordPress 网站开发“微信小程序“实战(三)

    本文是"WordPress 开发微信小程序"系列的第三篇,本文记录的是开发"DeveWork+"小程序1.2 版本的过程.建议先看完第一篇.第二篇再来阅读本文. ...

  4. python-筛法求素数

    [题目描述]用户输入整数n和m(1<n<m<1000),应用筛法求[n,m]范围内的所有素数. [练习要求]请给出源代码程序和运行测试结果,源代码程序要求添加必要的注释. [输入格式 ...

  5. Android Studio配置openvc

    最近项目中需要用到opencv,于是就研究了一下怎么在Android studio中配置opencv,记录一下,免得以后还会使用. 一.因为本人Android Studio是4.1的,网上资料大多是3 ...

  6. mapreduce分区

    本次分区是采用项目垃圾分类的csv文件,按照小于4的分为一个文件,大于等于4的分为一个文件 源代码: PartitionMapper.java: package cn.idcast.partition ...

  7. java中checked异常和unchecked异常区别?

    马克-to-win:checked和unchecked异常区别:结论就是:1)RuntimeException和他的子类都是unchecked异 常.其他的都是checked异常.马克-to-win: ...

  8. This program may be freely redistributed under the terms of the GNU GPL

    在centos中安装Google浏览器时 执行[root@server1 opt]# rpm ivh install google-chrome-stable_current_x86_64.rpm 爆 ...

  9. java JDK的安装和环境配置(windows10)

    1.下载JDK,安装.http://www.oracle.com/technetwork/java/javase/archive-139210.html   下载地址 2.配置JDK. (右键我的电脑 ...

  10. 解决vscode卡顿,CPU占用过高的问题

    打开vscode之后,点击文件==>首选项==>设置 搜索设置 search.followSymlinks   然后将这个值改为false