1.创建一个名叫springboot-controller-annotation的SpringBoot项目,并选择DevTools、Lombok和Spring Web三个依赖。
提示:如果提示lnitialization failed for 'https://start.spring.io'Please check URL, network and proxy settings.错误,可以点击左上角File,选择Setting,搜索HTTP Proxy,点击Auto-detect proxy settins,然后选择Automatic proxy configuration URL输入https://start.spring.io,点击OK,再次进行创建SpringBoot项目。
2.设置本地Maven。然后点击右侧Maven,然后点击重新加载图标。
3.在com.example.annotation包下创建包controller,并创建MyController类。
4.@Controller注解:只可用在类上,和@Component功能一样,把添加这个注解的类注册到容器,可以传字符串当做注册到容器中的类的名字。只是为了更加明确语义,才用@Controller注解加到MVC架构的Controller层。当@Controller不传值时,默认将类名第一个字母变为小写,其他不变,传入容器。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
@AliasFor(
annotation = Component.class
)
String value() default "";
}
例子1:
@Controller("newController")
public class MyController {
}
例子2:
@Controller
public class MyController {
}
结果:
package com.example.annotation;
import com.example.annotation.controller.MyController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class SpringbootControllerAnnotationApplication {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(SpringbootControllerAnnotationApplication.class, args);
String[] beanNamesForType = run.getBeanNamesForType(MyController.class);
for (String s : beanNamesForType) {
System.out.println(s);
}
}
}
通过按类型查看注入容器中MyController类的名字,查看控制台打印。
结果1:
结果2:
5.@RestController注解:只可用在类上,表示返回值是数据,而不是映射地址或者页面,并将类注入容器。@RestController注解等价于@Controller和@ResponseBody两者组成的共同注解。@RestController注解和@Controller注解一样,可以传字符串参数当做类注册到容器中的名字,不传值时,默认把类名第一个字母变为小写后,传入容器。导入容器的名字可以自己测试。其他内容,下面会进行测试。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
@AliasFor(annotation = Controller.class)
String value() default "";
}
6.@RequestMapping注解:可以用到类或者方法上,用来表示映射地址和请求类型等。参数有name(String)、value(String [])、path(String [])、method(RequestMethod [])、params(String [])、
headers(String [])、consumes(String [])、produces(String [])共8个。最为常用的就是value和method这两个属性。value用来设置映射的值,也就是请求这个映射地址时,调用对应的方法。method用于设置请求方式,常用的请求方式有RequestMethod.POST
、RequestMethod.DELETE
、RequestMethod.PUT
和RequestMethod.GET
四种,分别用于增删改查。直接给@RequestMapping传值,等于给@RequestMapping中的value属性传值。也就是@RequestMapping("/test")
等价于@RequestMapping(value = "/test")
。但是@RequestMapping用在类上一般只传value值,value属性可以不用写,也就是直接写
@RequestMapping(映射地址)即可。一旦给类上加上映射地址,访问方法时,需要都加上这个地址,然后再加上方法上面的映射地址,才能调用本方法。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
String name() default "";
@AliasFor("path")
String[] value() default {};
@AliasFor("value")
String[] path() default {};
RequestMethod[] method() default {};
String[] params() default {};
String[] headers() default {};
String[] consumes() default {};
String[] produces() default {};
}
由于@RequestMapping有两个属性较为常用,但是默认传值只能赋值给一个属性。所以,针对四个请求方式分别延伸出了@PostMapping、@DeleteMapping、@PutMapping和@GetMapping四个注解。这样,注解可以表明请求方式,默认传值可以表示映射地址。以get请求为例,@RequestMapping(value = "/test",method = RequestMethod.GET)
等价于@GetMapping(value = "/test")
等价于@GetMapping("/test")
。但是延伸出来的四个注解只能用在方法上,不能用在类上了。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.GET)
public @interface GetMapping {
@AliasFor(annotation = RequestMapping.class)
String name() default "";
@AliasFor(annotation = RequestMapping.class)
String[] value() default {};
@AliasFor(annotation = RequestMapping.class)
String[] path() default {};
@AliasFor(annotation = RequestMapping.class)
String[] params() default {};
@AliasFor(annotation = RequestMapping.class)
String[] headers() default {};
@AliasFor(annotation = RequestMapping.class)
String[] consumes() default {};
@AliasFor(annotation = RequestMapping.class)
String[] produces() default {};
}
例子1:
package com.example.annotation.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@GetMapping("/test1")
public String test1() {
return "test1";
}
@RequestMapping(value = "/test2",method = RequestMethod.GET)
public String test2() {
return "test2";
}
}
结果1:浏览器中访问http://localhost:8080/test1和http://localhost:8080/test2分别可以得到以下结果。
例子2:
package com.example.annotation.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class MyController {
@GetMapping("/test1")
public String test1() {
return "test1";
}
@RequestMapping(value = "/test2",method = RequestMethod.GET)
public String test2() {
return "test2";
}
}
结果2:由于在类上加上了映射地址,再请求结果1中的两个地址会报404错误。需要通过类映射地址+方法地址的方式,调用方法。
浏览中访问http://localhost:8080/test1、http://localhost:8080/test2、http://localhost:8080/user/test1、http://localhost:8080/user/test2的结果依次如下。
7.@ResponseBody:可以用在类或方法上,表示返回值是数据,而不是映射地址或者页面。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBody {
}
为了测试@ResponseBody功能,在pom.xml的<dependencies></dependencies>导入thymeleaf依赖。点击右上角m加旋转标志,重新加载依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
在src/resources/templates文件夹下创建test.html文件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text">
<h3>这是用来测试的</h3>
</body>
</html>
例子1:
package com.example.annotation.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class MyController {
@GetMapping("/test")
public String test1() {
return "test";
}
}
结果1:当不加@ResponseBody时,会自动将字符串解析成页面。浏览器访问http://localhost:8080/test,发现进入了test.html的页面。
例子2:
package com.example.annotation.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@ResponseBody
public class MyController {
@GetMapping("/test")
public String test1() {
return "test";
}
}
结果2:加上了@ResponseBody注解,浏览器访问http://localhost:8080/test,发现返回的是test字符串。@ResponseBody注解加在类上,表示类下面的所有方法返回值是数据,而不是映射地址或者页面。@ResponseBody注解加在方法上,表示本方法返回值是数据,而不是映射地址或者页面。
例子3:
package com.example.annotation.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@GetMapping("/test")
public String test1() {
return "test";
}
}
结果3:本方法为了测试序号5中,@RestController注解等价于@Controller和@ResponseBody两者组成的共同注解。现在前后端分离较为流行,一般直接将Controller层的所有类上都上@RestController注解,让Controller层只操作数据,不操作页面。浏览器访问http://localhost:8080/test,发现产生和结果2一样的结果。
8.为了方便下面注解的测试,先在src/resources/templates文件夹下创建test2.html文件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="http://cdn.bootcss.com/jquery/1.12.2/jquery.js"></script>
<script>
function bodySubmit(number) {
let id = "1";
let name = "bodyTest";
let url = "/test/bodyTwo";
if(number === 3) {
url = "/test/bodyThree"
}
let data = {
"id": id,
"name": name
};
$.ajax({
type: "post",
url: url,
dataType: "json",
contentType: "application/json;charset=utf-8",
data: JSON.stringify(data),
success: function (res) {
console.log(res);
}
})
}
</script>
</head>
<body>
<a href="/test/param?name=paramTest1&id=1">@RequestParam测试1</a><br><br>
<a href="/test/path/1/test/pathTest">@PathVariable测试1</a><br><br>
<a href="/test/toAttribute">@RequestAttribute测试1</a><br><br>
<form action="/test/bodyOne" method="post">
<input type="text" name="id" value="1" hidden>
<input type="text" name="name" value="bodyType" hidden>
<input type="submit" value="@RequestBody测试1">
</form><br>
<button onclick="bodySubmit(2)">@RequestBody测试2</button><br><br>
<button onclick="bodySubmit(3)">@RequestBody测试3</button><br><br>
<a href="/test/header">@RequestHeader测试1</a><br><br>
<a href="/test/cookie">@CookieValue测试1</a><br><br>
<a href="/test/matrix;id=1;name=matrixTest1/matrix/matrixTwo;idTwo=2/matrixThree;id=3,4;name=name3,name4">@MatrixVariable测试1</a>
</body>
</html>
9.@RequestParam注解:只能用在方法的参数上,表示从请求参数上获取值,并赋值给所修饰的方法参数。此注解常与GET请求结合使用。@RequestParam一共有name(String),value(String),required(boolean),defaultValue(String)四个参数。常用的只有value,required,defaultValue三个参数。value表示请求参数的名称,@RequestParam不声明具体参数的传值,默认给value传值。required表示此参数是否必填,默认true。当required为true时,表示请求参数中必须有这个参数,否则会报错。defaultValue表示当请求参数上没有这个参数时,会给个默认值。
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
String defaultValue() default ValueConstants.DEFAULT_NONE;
}
例子:
package com.example.annotation.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/test")
public class MyController {
@RequestMapping("")
public String test() {
return "test2";
}
@GetMapping("/param")
@ResponseBody
public Map<String, String> paramTest1(@RequestParam String name,
@RequestParam("id") String id,
@RequestParam(value = "id") String getId,
@RequestParam(defaultValue = "15") String age,
@RequestParam(required = false) String hobby,
@RequestParam(required = false,defaultValue = "空地址") String address,
@RequestParam(value = "sex",required = false,defaultValue = "男") String sex,
@RequestParam Map<String,String> paramMap
) {
Map<String, String> map = new HashMap<>();
map.put("name",name);
map.put("id",id);
map.put("getId",getId);
map.put("age",age);
map.put("hobby",hobby);
map.put("address",address);
map.put("sex",sex);
map.put("paramMap",paramMap.toString());
return map;
}
}
结果:浏览器访问http://localhost:8080/test,点击“@RequestParam测试1”,会产生以下结果。(请求地址是http://localhost:8080/test/param?name=paramTest1&id=1)
总结(以下名称均是方法参数的名称):
1.从getId参数可以发现,方法参数名称和请求参数名称可以不相同,只要保证注解的value值与请求参数名称相同即可。
2.从id和getId参数可以发现,给注解传值,不指定参数名称时,会自动给value参数赋值。
3.从name和id参数可以发现,不给注解赋值时,会将方法参数名称和请求参数名称进行绑定。
4.从age参数可以发现,方法参数从请求参数中没有找到绑定值,且定义了defaultValue时,会取出defaultValue值赋值给方法参数。
5.从hobby参数可以发现,当required值为false时,哪怕请求参数中没有方法参数需要的值时,也不会报错。
6.address参数的注解表示的意思是方法参数绑定名称为address的请求参数,并且允许此请求参数不存在,方法参数的默认值为“空地址”。
7.sex参数的注解表示的意思是方法参数绑定名称为sex的请求参数,并且允许此请求参数不存在,方法参数的默认值为“男”。
8.从paramMap参数可以发现,可以将所有的请求参数通过类型为Map<String,String>的map进行接收。
9.如果required的值为true,且没有给defaultValue赋值时,并且方法参数没有在请求参数值中找到绑定值时,会报错。
10.@PathVariable注解:只能用在方法的参数上,表示从请求路径上获取值,并赋值给所修饰的方法参数。@PathVariable一共有name(String),value(String),required(boolean)三个参数。常用的只有value,required两个参数。value表示请求地址中参数的名称,@PathVariable不声明具体参数的传值,默认给value传值。required表示此参数是否必填,默认true。当required为true时,表示请求地址的参数中必须有这个参数,否则会报错。
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PathVariable {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
}
例子:
package com.example.annotation.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/test")
public class MyController {
@RequestMapping("")
public String test() {
return "test2";
}
@GetMapping("/param")
@ResponseBody
public Map<String, String> paramTest1(@RequestParam String name,
@RequestParam("id") String id,
@RequestParam(value = "id") String getId,
@RequestParam(defaultValue = "15") String age,
@RequestParam(required = false) String hobby,
@RequestParam(required = false, defaultValue = "空地址") String address,
@RequestParam(value = "sex", required = false, defaultValue = "男") String sex,
@RequestParam Map<String, String> paramMap
) {
Map<String, String> map = new HashMap<>();
map.put("name", name);
map.put("id", id);
map.put("getId", getId);
map.put("age", age);
map.put("hobby", hobby);
map.put("address", address);
map.put("sex", sex);
map.put("paramMap", paramMap.toString());
return map;
}
@GetMapping("/path/{id}/test/{name}")
@ResponseBody
public Map<String, String> pathTest1(@PathVariable String id,
@PathVariable("name") String name,
@PathVariable(value = "name") String getName,
@PathVariable(required = false) String age,
@PathVariable(value = "address", required = false) String address,
@PathVariable Map<String, String> pathMap
) {
Map<String, String> map = new HashMap<>();
map.put("id", id);
map.put("name", name);
map.put("getName", getName);
map.put("age", age);
map.put("address", address);
map.put("pathMap", pathMap.toString());
return map;
}
结果:浏览器访问http://localhost:8080/test,点击“@PathVariable测试1”,会产生以下结果。(请求地址是http://localhost:8080/test/path/1/test/pathTest)
总结(以下名称均是方法参数的名称):
1.从name和getName参数可以发现,给注解传值,不指定参数名称时,会自动给value参数赋值。
2.从id和getName参数可以发现,不给注解赋值时,会将方法参数名称和请求地址的参数名称进行绑定。
3.从age参数可以发现,当required值为false时,哪怕请求地址的参数中没有方法参数需要的值时,也不会报错。
4.address参数的注解表示的意思是方法参数绑定名称为address的请求地址的参数,并且允许此请求地址的参数不存在。
5.从pathMap参数可以发现,可以将所有请求地址的参数通过类型为Map<String,String>的map进行接收。
6.如果required的值为true,并且方法参数没有在请求地址的参数值中找到绑定值时,会报错。
11.@RequestAttribute注解:只能用在方法的参数上,表示从Attribute上获取值,并赋值给所修饰的方法参数。@RequestAttribute一共有name(String),value(String),required(boolean)三个参数。常用的只有value,required两个参数。value表示Attribute中参数的名称,@RequestAttribute不声明具体参数的传值,默认给value传值。required表示此参数是否必填,默认true。当required为true时,表示Attribute中必须有这个参数,否则会报错。
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestAttribute {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
}
例子:
package com.example.annotation.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/test")
public class MyController {
@RequestMapping("")
public String test() {
return "test2";
}
@GetMapping("/toAttribute")
public String toAttribute(Model model) {
model.addAttribute("id","1");
model.addAttribute("name","attributeTest");
return "forward:attribute";
}
@GetMapping("/attribute")
@ResponseBody
public Map<String,String> attributeTest(@RequestAttribute String id,
@RequestAttribute("name") String name,
@RequestAttribute(value = "name") String getName,
@RequestAttribute(required = false) String age,
@RequestAttribute(value = "address", required = false) String address
) {
Map<String,String> map = new HashMap<>();
map.put("id", id);
map.put("name", name);
map.put("getName", getName);
map.put("age", age);
map.put("address", address);
return map;
}
}
结果:浏览器访问http://localhost:8080/test,点击“@RequestAttribute测试1”,会产生以下结果。(请求地址是http://localhost:8080/test/toAttribute)
总结(以下名称均是方法参数的名称):
1.从name和getName参数可以发现,给注解传值,不指定参数名称时,会自动给value参数赋值。
2.从id和getName参数可以发现,不给注解赋值时,会将方法参数名称和Attribute的参数名称进行绑定。
3.从age参数可以发现,当required值为false时,哪怕Attribute的参数中没有方法参数需要的值时,也不会报错。
4.address参数的注解表示的意思是方法参数绑定名称为address的Attribute的参数,并且允许此Attribute的参数不存在。
5.如果required的值为true,并且方法参数没有在Attribute的参数值中找到绑定值时,会报错。
12.@RequestBody注解:只能用在方法参数上,表示从JSON字符串上获取值,并赋值给所修饰的方法参数。此注解常与POST请求结合使用,所修饰的方法参数常常是Map或者pojo类。并且,一个请求中只能有一个@RequestBody注解。@RequestBody注解只有一个required(boolean)参数。required表示此参数是否必填,默认为true。当required为true时,没有请求体传过来,会报错。
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestBody {
boolean required() default true;
}
例子1:
package com.example.annotation.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/test")
public class MyController {
@RequestMapping("")
public String test() {
return "test2";
}
@PostMapping("/bodyOne")
@ResponseBody
public String bodyTest1(@RequestBody String content ) {
return content;
}
}
结果1:浏览器访问http://localhost:8080/test,点击“@RequestBody测试1”,会产生以下结果。由于http提交form表单默认请求头是application/x-www-form-urlencoded,而不是JSON字符串。所以,此类型的表单只能用String类型数据进行接收,用Map或pojo类进行接收会报错。如果需要数据,自行处理字符串。这样,失去了用@RequestBody的初衷,还不如用@RequestParam更加方便。
例子2:
package com.example.annotation.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/test")
public class MyController {
@RequestMapping("")
public String test() {
return "test2";
}
@PostMapping("/bodyTwo")
@ResponseBody
public Map<String,Object> bodyTest2(@RequestBody Map<String,Object> map ) {
System.out.println(map);
return map;
}
}
结果2:前端用ajax传递post请求,设置请求头和数据类型为JSON,用JSON.stringify将数据封装为JSON数据,三者缺一不可,否则会报错(具体可以查看序号8中test2.html文件)。可以通过设置类型为 Map<String,Object>的map接收数据,这是一种万能接收数据的方式。后期,可以根据自己需要,将Object数据转换为自己需要的数据。如果都是一种类型的数据,也可以自行设置接收类型。
浏览器访问http://localhost:8080/test,点击“@RequestBody测试2”,会产生以下结果。
例子3:
package com.example.annotation.controller;
import com.example.annotation.pojo.Person;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/test")
public class MyController {
@RequestMapping("")
public String test() {
return "test2";
}
@PostMapping("/bodyThree")
@ResponseBody
public String bodyTest3(@RequestBody Person person) {
System.out.println(person);
return person.toString();
}
}
结果3:前端和结果2中一样,后端用Person类接收数据,会自动将JSON字符串中的值和Person类的属性进行绑定。这样对数据较多的类来说,操作数据更加方便。
浏览器访问http://localhost:8080/test,点击“@RequestBody测试3”,会产生以下结果
补充:在src/main/java的com.example.annotation包下面创建名叫pojo的包,并在此包下创建Person类。
package com.example.annotation.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String id;
private String name;
}
13.@RequestHeader注解:只能用在方法的参数上,表示从Header中获取数据,并赋值给所修饰的方法参数。@RequestHeader一共有name(String),value(String),required(boolean),defaultValue(String)四个参数。常用的只有value,required,defaultValue三个参数。value表示RequestHeader参数的名称,@RequestHeader不声明具体参数的传值,默认给value传值。required表示此参数是否必填,默认true。当required为true时,表示RequestHeader参数中必须有这个参数,否则会报错。defaultValue表示当RequestHeader参数上没有这个参数时,会给个默认值。
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestHeader {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
String defaultValue() default ValueConstants.DEFAULT_NONE;
}
@RequestHeader注解只可以Request Headers获取的参数,不可以获取Response Headers的参数。
例子:
package com.example.annotation.controller;
import com.example.annotation.pojo.Person;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/test")
public class MyController {
@RequestMapping("")
public String test() {
return "test2";
}
@GetMapping("/header")
@ResponseBody
public Map<String,String> headerTest(@RequestHeader String Connection,
@RequestHeader("Host") String host,
@RequestHeader(value = "Host") String getHost,
@RequestHeader(required = false) String name,
@RequestHeader(value = "address", required = false) String address,
@RequestHeader Map<String, String> headerMap
) {
Map<String,String> map = new HashMap<>();
map.put("host",host);
map.put("Connection",Connection);
map.put("getHost",getHost);
map.put("name",name);
map.put("address",address);
map.put("headerMap",headerMap.toString());
return map;
}
}
结果:浏览器访问http://localhost:8080/test,点击“@RequestHeader测试1”,会产生以下结果。
总结:可以参考序号10中@PathVariable注解,@PathVariable注解的6条总结,同样适用这个注解。
写例子时,遇到了两个小事情,同样总结下来,就是@RequestHeader只能用于Get请求,否则会报错。我是为了简化代码,复制上面的代码才导致出现这个问题,一般获取数据都是用@GetMapping,只要写代码规范就不会遇到。
另一个就是获取Request Headers没有的参数时,IDEA会划横线,这样有效避免了写错参数名,比如上面的address,这是为了测试才写的,不过也不属于程序错误。
14.@CookieValue:只能用于方法的参数上,表示从Cookie中获取数据,并赋值给所修饰的方法参数。@CookieValue一共有name(String),value(String),required(boolean),defaultValue(String)四个参数。常用的只有value,required,defaultValue三个参数。value表示Cookie中参数的名称,@CookieValue不声明具体参数的传值,默认给value传值。required表示此参数是否必填,默认true。当required为true时,表示Cookie的参数中必须有这个参数,否则会报错。defaultValue表示当Cookie的参数上没有这个参数时,会给个默认值。
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CookieValue {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
String defaultValue() default ValueConstants.DEFAULT_NONE;
}
例子:
package com.example.annotation.controller;
import com.example.annotation.pojo.Person;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/test")
public class MyController {
@RequestMapping("")
public String test() {
return "test2";
}
@GetMapping("/cookie")
@ResponseBody
public Map<String,String> cookieTest(@CookieValue String id,
@CookieValue("name") String name,
@CookieValue(value = "name") String getName,
@CookieValue(required = false) String age,
@CookieValue(value = "address", required = false) String address) {
Map<String,String> map = new HashMap<>();
map.put("id",id);
map.put("name",name);
map.put("getName",getName);
map.put("age",age);
map.put("address",address);
return map;
}
}
结果:浏览器访问http://localhost:8080/test,点击“@CookieValue测试1”,会产生以下结果。
补充:手动添加cookie到http://localhost:8080中。浏览访问http://localhost:8080/test(最好使用谷歌浏览器,这样会和我统一,设置看起来差不多),点击鼠标右键,选择最下面的“检查”,选择“Application”,选择左侧Cookies点击http://localhost:8080网址下,然后添加id和name属性的值。
总结:可以参考序号11中@RequestAttribute注解,@RequestAttribute注解的5条总结,同样适用这个注解。
15.@MatrixVariable注解:只能用于方法的参数上,表示从路径中获取数据,并赋值给所修饰的方法参数。感觉起来和@PathVariable差不多,都是从路径上获取值。但是,两者差距很大的,@PathVariable注解,无论是变量还是路径之间都是用"/"隔开,且变量用"{}"包裹。而@MatrixVariable注解,路径之间还是用"/"隔开,但是变量之间或者变量与路径之间以分号";"隔开,并且后跟变量的路径上加"{}"。@PathVariable一共有name(String),value(String),pathVar(String),required(boolean),defaultValue(String)五个参数。常用的只有value,pathVar,required,defaultValue四个参数。value表示路径中参数的名称,@MatrixVariable不声明具体参数的传值,默认给value传值。required表示此参数是否必填,默认true。当required为true时,表示路径的参数中必须有这个参数,否则会报错。defaultValue表示当路径的参数上没有这个参数时,会给个默认值。对于第一次出现的pathVar属性,表示的是路径名称,由于这种特有的矩阵传参方式,参数名是跟在路径后面的,为了区分不同路径下同名的参数,才有了这个属性。
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MatrixVariable {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
String pathVar() default ValueConstants.DEFAULT_NONE;
boolean required() default true;
String defaultValue() default ValueConstants.DEFAULT_NONE;
}
注意:SpringBoot默认禁用了矩阵变量的功能,如果想要使用,需要手动配置。查看WebMvcAutoConfiguration类下configurePathMatch()方法,发现创建了UrlPathHelper类。点击这个类进去查看源码,发现UrlPathHelper有个属性removeSemicolonContent的值为true(removeSemicolonContent表示是否移除分号功能)。查看SpringBoot官网,我们可以发现有两种方式解决这个问题。第一种方式就是在配置类中注入WebMvcConfigurer接口的实现类 ,第二种方式就是重写configurePathMatch()方法。
在src/main/java的com.example.annotation下创建一个名叫config的包,并在此包下面创建一个名叫MyWebConfig的类。
package com.example.annotation.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.util.UrlPathHelper;
@Configuration
public class MyWebConfig {
@Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
};
}
}
package com.example.annotation.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.util.UrlPathHelper;
@Configuration
public class MyWebConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
}
例子:
package com.example.annotation.controller;
import com.example.annotation.pojo.Person;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Controller
@RequestMapping("/test")
public class MyController {
@RequestMapping("")
public String test() {
return "test2";
}
@GetMapping("/{matrix}/matrix/{matrixTwo}/{matrixThree}")
@ResponseBody
public Map<String,String> matrixTest(@MatrixVariable String idTwo,
@MatrixVariable("idTwo") String id2,
@MatrixVariable(pathVar="matrix",value = "id") String id1,
@MatrixVariable(pathVar = "matrixThree",value = "id") List<String> id3,
@MatrixVariable(pathVar = "matrixFour",value = "id",defaultValue = "4",required = false) String id4,
@MatrixVariable Map<String,String> matrixMap1,
@MatrixVariable(pathVar = "matrixTwo") Map<String,String> matrixMap2
) {
Map<String,String> map = new HashMap<>();
map.put("idTwo",idTwo);
map.put("id2",id2);
map.put("id1",id1);
map.put("id3",id3.toString());
map.put("id4",id4);
map.put("name3",matrixMap1.toString());
map.put("matrixMap",matrixMap2.toString());
return map;
}
}
结果:浏览器访问http://localhost:8080/test,点击“@MatrixVariable测试1”,会产生以下结果。(url为http://localhost:8080/test/matrix;id=1;name=matrixTest1/matrix/matrixTwo;id=2/matrixThree;id=3,4;name=name3,name4)
总结(以下名称均是方法参数的名称):
1.从idTwo参数可以发现,不给注解传任何值时,默认就是在路径上找名叫idTwo参数,但是要保证这个名称的在路径上唯一,否则会报错。
2.从idTwo和id2参数可以发现,给注解传值,给注解传值,不指定参数名称时,会自动给value参数赋值。
3.从id1参数可以发现,通过路径名称和参数名称可以精确获取值。
4.从id3可以发现,当一个路径参数赋多个值时,被注解修饰的方法参数可以通过List接收。
4.从id4参数可以发现,当required值为false时,哪怕路径的参数中没有方法参数需要的值时,也不会报错。
5.从matrixMap1可以发现,可以获取某个路径上所有的参数,并放在Map中,默认获取第一路径的所有参数。
6.从matricMap2可以发现,可以精确获取某个路径上的所有参数,并放在Map中。
7..如果required的值为true,并且方法参数没有在路径的参数中找到绑定值时,会报错。
注解总结:上面是我认为用的比较多的注解,还有一部分注解,这里没有介绍。我也是从2022年3月份才系统的学习java,也算是个新手,也是在学习过程中。上面内容,可能有些叙述是不合适的,甚至有些叙述可能就是错误的,我也是在学习过程中,请多包涵。
细心的小伙伴,可能发现,很多内容就是根据源码来总结的,然后自己在去尝试一下,就知道哪些是正确的,哪些是错误的了。当然,上面内容不可能都记住的,要学会自己看看一些简单的源码,我现在也在努力尝试。都说授之以鱼不如授之以渔,下面以@MatrixVariable注解为例,讲一下源码怎么看,如果是大佬这里可以略过,甚至这一篇文章都不用看了。
/*
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.bind.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
/**
* Annotation which indicates that a method parameter should be bound to a
* name-value pair within a path segment. Supported for {@link RequestMapping}
* annotated handler methods.
*
* <p>If the method parameter type is {@link java.util.Map} and a matrix variable
* name is specified, then the matrix variable value is converted to a
* {@link java.util.Map} assuming an appropriate conversion strategy is available.
*
* <p>If the method parameter is {@link java.util.Map Map<String, String>} or
* {@link org.springframework.util.MultiValueMap MultiValueMap<String, String>}
* and a variable name is not specified, then the map is populated with all
* matrix variable names and values.
*
* @author Rossen Stoyanchev
* @author Sam Brannen
* @since 3.2
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MatrixVariable {
/**
* Alias for {@link #name}.
*/
@AliasFor("name")
String value() default "";
/**
* The name of the matrix variable.
* @since 4.2
* @see #value
*/
@AliasFor("value")
String name() default "";
/**
* The name of the URI path variable where the matrix variable is located,
* if necessary for disambiguation (e.g. a matrix variable with the same
* name present in more than one path segment).
*/
String pathVar() default ValueConstants.DEFAULT_NONE;
/**
* Whether the matrix variable is required.
* <p>Default is {@code true}, leading to an exception being thrown in
* case the variable is missing in the request. Switch this to {@code false}
* if you prefer a {@code null} if the variable is missing.
* <p>Alternatively, provide a {@link #defaultValue}, which implicitly sets
* this flag to {@code false}.
*/
boolean required() default true;
/**
* The default value to use as a fallback.
* <p>Supplying a default value implicitly sets {@link #required} to
* {@code false}.
*/
String defaultValue() default ValueConstants.DEFAULT_NONE;
}
在这里看源码,没有在IDEA中舒服。首先看最上面的注释,发现有Map<String,String>这个(在IDEA中很清晰,直接为蓝色),就说明可以用这个类型的map进行接收数据,具体的用法自己尝试。然后看来类上的注解,先看@Target,这个是表示注解用的地方,可以Ctrl+鼠标左键点入ElementType进去看,一个Type代表一个类型,可以看每个Type上面的注解,比如METHOD代表方法,TYPE代表类、接口或者枚举类等。再看紧挨着类的注解,比如@RestController会显示有@Controller和@ResponseBody就代表是这两个注解的结合。
下面就要看类里面了,看看一共有几个属性,一般name属性不用管,value、required和defaultValue一般都会有,看看上面注释就大体知道每个意思,再不行去搜一下。看这个主要的目的就是看看参数个数,一般看到参数名也就才到7788了,比如这里多了个pathVar,大体一猜就知道干什么的了。再比如@RequestBody注解中就一个required属性,我们也可以猜出这个注解每个方法上只能出现一次,因为没有value属性指明参数名。我本来也很讨厌看源码,主要看不懂。学会看源码,就可以记住一下常规情况,不需要记很多内容,真正遇到了,再去查看就好了。哪怕我们什么也不会,看下源码,我们也有方向去搜索内容去学习。尝试一下,看简单源码,你会发现更多的乐趣。