本文中的内容大都来自spring官方文档,官方文档的访问地址为spring官方文档地址,
本文中所有的demo代码在https://github.com/singedli/request_mapping_demo.git可以下载。
实验的项目是采用默认配置的spring boot项目,使用的工具为IDEA和POSTMAN。
希望这些案例能够帮助你理解和思考。
talk is cheap,show me the code!
1、从最简单的hello world开始
@Controller
public class HelloController {
@RequestMapping("/helloWorld")
@ResponseBody
public String helloWorld(){
return "hello world";
}
}
上例也许就是一个最简单的springmvc代码了,当请求的uri为”/helloWorld”时,将会得到字符串”hello world”,下图是用postman模拟请求的结果。
简单介绍几个基础的注解,因为后面的实验中可能会用到:
- @Controller 声明HelloController类为springMVC中的一个controller,优点是不需要继承任何类或者实现任何的接口。在配置了注解扫描后,头上有@Controller注解的类将会被spring容器自动的加载。
spring 注解扫描的配置方式(java config)如下:
@Configuration
@ComponentScan("org.example.web")
public class WebConfig {
// ...
}
- @RequestMapping: 用于提供的请求映射信息。@RequestMapping注解即可以用在class级别中也可以用在方法级别中。
- @ResponseBody: 表示该方法的返回结果直接写入 HTTP response body 中。
- @GetMapping、@PostMapping、@PutMapping等:他们相当于RequestMapping(value = “/xxx” , method = RequestMethod.GET)或 RequestMapping(value = “/xxx” , method = RequestMethod.POST),即限定请求方法的简写。
- @RestController :官方文档的描述如下,即@RestController 注解相当于@Controller和@ResponseBody两个注解的组合。
@RestController is a composed annotation that is itself annotated with @Controller and @ResponseBody
2、通过指定HTTP请求方法来限定请求
URI为”/testShortcut”的接口将请求的方法限定为GET请求,也就以为着请求地址和请求方法都必须正确才会返回字符串”success”。
代码如下:
@Controller
public class MyRequestMappingDemo {
@RequestMapping(value = "/testShortcut",method = RequestMethod.GET)
@ResponseBody
public String testShortcut(){
return "success";
}
}
使用postman请求接口”/testShortcut”如下,可以看到,使用的HTTP METHOD为GET请求,并且成功返回了字符串”success”。
下面使用HTTP METHOD为POST请求同样的接口
可以看到,同样的接口只是请求的方式不同得到的结果也就不相同。达到了通过请求方式来限定请求的目的。
再此顺便试一下@GetMapping、@PostMapping、@PutMapping等注解好不好用。
@Controller
public class GETMappingDemo {
@GetMapping(value= "/testGetMapping")
@ResponseBody
public String testGetMapping(){
return "testGetMapping";
}
}
由上面两张图可以看到,@GetMapping、@PostMapping等注解对于通过请求方式来限定也是贼好用的。
3、url pattern
springMVC也是支持在请求的url中使用通配符的,具体规则如下:- ?通配符表示匹配一个字符(matches one character)。
-
- 通配符表示在一个路径分段中匹配一个或者多个字符(matches zero or more characters within a path segment)。
- ** 统配符表示匹配零个或者多个路径分段(match zero or more path segments)。
相关代码和实验如下:
@RestController
public class URIPatternsDemo {
@RequestMapping("/testURIPatterns/?")
public String testURIPatterns(){
return "?通配符表示匹配一个字符!";
}
@RequestMapping("/testURIPatterns1/*")
public String testURIPatterns1(){
return "*通配符表示匹配零个或者多个字符,但必须是在一个路径分段中!";
}
@RequestMapping("/testURIPatterns2/**")
public String testURIPatterns2(){
return "**通配符表示匹配零个或者多个路径分段!";
}
}
上述代码中,URIPatternsDemo类里定义了三个方法,分别用于测试三种通配符的使用。使用postman测试如下:
1、测试?通配符,路径中输入一个字符a,信息成功返回。
2、测试?通配符,路径中输入一个字符b,信息成功返回。
3、测试?通配符,路径中输入多个字符,报错。
结论,?通配符只能匹配一个随机的字符
4、测试*通配符,路径中输入多个字符,正确返回。
5、测试*通配符,路径中输入零个字符,正确返回。
6、测试*通配符,在多个路径分段中输入多个字符,报错。
结论,* 通配符表示在一个路径分段中匹配一个或者多个字符
7、测试**通配符,在一个路径分段中输入多个字符,正确返回。
8、测试**通配符,在多个路径分段中输入多个字符,正确返回。
8、测试**通配符,零个路径分段,正确返回。
结论,** 统配符表示匹配零个或者多个路径分段。
4、URI Template Patterns
URI templates can be used for convenient access to selected parts of a URL in a @RequestMapping method.
URI模板可以很方便的用于获取@RequestMapping方法里的URL的一部分。(本屌的渣渣翻译)
意思就是说可以使用URI Template获取URL路径中的变量。在sprngMVC中使用的是@PathVariable注解。talk is cheap,上代码。
@Controller
public class TestUrlPatterns {
//演示一个URL路径变量的情况
@RequestMapping("/UrlPatternDemo/{pathVar}")
@ResponseBody
public String UrlPatternDemo(@PathVariable String pathVar){
return pathVar;
}
//演示两个路径变量的情况
@RequestMapping("/multiUrlPatternDemo/{pathVar1}/{pathVar2}")
@ResponseBody
public String multiUrlPatternDemo(@PathVariable String pathVar1 , @PathVariable String pathVar2){
return "this is first path variable:" + pathVar1+", this is second path variable:" + pathVar2;
}
//演示路径变量支持多种基本数据类型,而不是只有String一种
@RequestMapping("/multiUrlPatternDemo2/{pathVar1}/{pathVar2}")
@ResponseBody
public String multiUrlPatternDemo2(@PathVariable Integer pathVar1 , @PathVariable Integer pathVar2){
return "第一个参数是:" + pathVar1+",第二个参数是:" + pathVar2 +"。两个参数的和是:"+ (pathVar1 + pathVar2);
}
}
下面测试一下这三个方法:
结论:@PathVariable注解可以获取请求URL中的路径参数,并且也可以用基本数据类型来接收参数的值。
下面来讨论一些特殊情况:
如果一个请求的URL同时匹配了多个通配符怎么办?
springMVC中有一些规则用于处理这种特殊情况,规则是:
- 例如,
/hotel/{hotel}/*
和/hotel/{hotel}/**
,前者具有一个路径变量和一个通配符,而后者同样是具有一个路径变量,但是后者却有两个通配符。此时,springMVC认为前者更加的明确,所以前者的优先级会更高一些。 /foo/bar*
和/foo/*
springMVC认为前者的指向更加明确,所以前者的优先级更高。/hotels/{hotel}
和/hotels/*
springMVC认为前者的指向更加明确,原因是前者没有用到通配符。/**
在springMVC中被认为是优先级最低的。具体的规则在AntPathMatcher类的AntPatternComparator()方法中有完整的体现。
URI Template另外还有两种用法,即与正则表达式一起使用和与Placeholders一起使用。
第一种:与正则一起使用
@RestController
public class RegexExpressionDemo {
@RequestMapping("/testRegexExpressionPattern/{name:[a-z-]+}")
public String testRegexExpressionPattern(@PathVariable String name){
return "正则表达式匹配的变量为:" + name;
}
}
第二种,与Placeholders一起使用
@Controller
@RequestMapping(value="${demo.path.variable}")
public class PropertiesPlaceholderDemo {
@RequestMapping("/testPropertiesPlaceholder")
@ResponseBody
public String testPropertiesPlaceholder(){
return "asd";
}
}
如上面代码所示,@RequestMapping注解中的参数来自properties配置文件,当路径正确匹配时,将会返回字符串”asd”。所以我们在properties文件中创建一个key为demo.path.variable的属性。在这里就直接写在application.properties中,如下图所示。
所以我们访问的路径应该为”/haha/testPropertiesPlaceholder”,下面为测试:
成功返回字符串“asd”。
5、Matrix Variables
Matrix Variables意味矩阵变量,说白了就是名值对。解释名词是很蛋疼的事,直接上代码和实验结果,对着代码和结果进行解释。
要使用矩阵变量,首先需要将RequestMappingHandlerMapping类中的removeSemicolonContent属性设为false,默认情况下为true。设置的方法也很简单,下面是通过java config的方式配置该属性。
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
}
实验代码如下:
完整代码如下,其中有个没见过的注解@MatrixVariable,下面通过实验结果来说说这个注解到底是干什么。
@RestController
public class MatrixVariablesDemo {
@RequestMapping("/testMatrixVariables/{pathVar1}")
public String testMatrixVariables(@PathVariable String pathVar1,@MatrixVariable int matrixVariable1,@MatrixVariable int matrixVariable2){
return "pathVar1的值为:" + pathVar1 + "; matrixVariable1的值为:" + matrixVariable1 + "; matrixVariable2的值为:" + matrixVariable2;
}
@RequestMapping("/testMatrixVariables2/{pathVar1}/{pathVar2}")
public String testMatrixVariables2(@MatrixVariable(name = "matrixVariable",pathVar = "pathVar1") int matrixVariable1,@MatrixVariable(name = "matrixVariable",pathVar = "pathVar2") int matrixVariable2){
return " pathVar1变量中的matrixVariable的值为:" + matrixVariable1 + "; pathVar2变量中的matrixVariable的值为:" + matrixVariable2;
}
@RequestMapping("/testMatrixVariables3/{pathVar}")
public String testMatrixVariables3(@PathVariable String pathVar,@MatrixVariable(name = "matrixVariable",pathVar = "pathVar",required = false,defaultValue = "222233" ) String matrixVariable){
return "pathVar变量的值是:"+pathVar+",matrixVariable变量什么值也没传,默认值是:" + matrixVariable;
}
@RequestMapping("/testMatrixVariables4/{pathVar1}/{pathVar2}")
public String testMatrixVariables4(@MatrixVariable MultiValueMap<String,String> matrixMap){
return matrixMap.toString();
}
@RequestMapping("/testMatrixVariables5/{pathVar1}/{pathVar2}")
public String testMatrixVariables5(@MatrixVariable(pathVar = "pathVar1") MultiValueMap<String,String> matrixMap){
return matrixMap.toString();
}
}
1、测试第一个接口“/testMatrixVariables/{pathVar1}”,实验中请求的URL为http://localhost:8080//testMatrixVariables/20;matrixVariable1=1;matrixVariable2=2
结果如下:
先观察URL,在{pathVar1}中有三个用分号分隔的部分,分别为20、matrixVariable1=1和matrixVariable2=2。回想Matrix Variable矩阵变量的定义,说它是明值对。这是可以大概明白所谓的矩阵变量就是写在路径变量{pathVar1}中的用分号“;”隔开的name=value对,即本例中的matrixVariable1=1和matrixVariable2=2。结合代码和实验结果可知,@MatrixVariable注解的作用就是用来获取路径变量中的矩阵的变量的,并且可以用基本数据类型来接收。
2、测试第二个接口,URLhttp://localhost:8080//testMatrixVariables2/matrixVariable=1/matrixVariable=2
,结合代码可知,我们可以用@MatrixVariable注解,并通过指定pathVar属性来获取两个不同路径变量中同名的矩阵变量。
3、第三个接口用于测试@MatrixVariable注解的默认值,URLhttp://localhost:8080//testMatrixVariables3/1
,结果为:
4、第四个和第五个接口都是用于测试@MatrixVariable注解中用Map接收参数,URL分别为http://localhost:8080/testMatrixVariables4/a=a;b=b;c=c/a=aa;b=bb
和
http://localhost:8080/testMatrixVariables5/a=a;b=b;c=c/a=aa;b=bb
结果分别如下:
获取所有路径段中的矩阵变量,并用Map接收
指定获取某个路径分段中的矩阵变量,并用Map接收
6、通过限定MediaTypes、Request Parameters和Header Values来限制请求
通过Consumes,Produces注解来限定MediaTypes
consumes:指定处理请求的提交内容类型(Content-Type)。
produces:指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回。
@Controller
@RequestMapping(value="/test")
public class ConsumableMediaTypesDemo {
@RequestMapping(value="/testConsumableMediaTypes",consumes = "application/json")
public String testConsumableMediaTypes(){
return "请求成功!";
}
@RequestMapping(value="/testConsumableMediaTypes1",consumes = MediaType.APPLICATION_JSON_VALUE)
public String testConsumableMediaTypes1(){
return "请求成功! MediaType.APPLICATION_JSON_VALUE";
}
@RequestMapping(value="/testConsumableMediaTypes2",consumes = MediaType.APPLICATION_XML_VALUE)
@ResponseBody
public String testConsumableMediaTypes2(){
return "请求成功! MediaType.APPLICATION_XML_VALUE";
}
}
1、第一个接口是用consumes属性来限定MediaTypes的类型,并使用了字面量的形式限定类型,后面两个接口使用了MediaType类中定义的常量。实验的结果如下:
接口1:
接口2:
接口3:
produces、params和headers的使用方式与@Consumes相同
代码如下
@Controller
public class ProducibleMediaTypesDemo {
@RequestMapping(value = "/testProducibleMediaTypes",produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@ResponseBody
public String testProducibleMediaTypes(){
return "请求成功";
}
}