类注解
RestController
@RestController 是spring4.x新引入的一个注解,定义在类前面,相当于@Controller和@ResponseBody的一个结合,使该类中的方法直接返回字符串或者json数据给浏览器,而不是返回视图页面。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController { }
Controller
@Controller用于标记在一个类上,使用它标记的类就是一个SpringMvc Controller对象,分发处理器会扫描使用该注解的类的方法,并检测方法是否使用了@RequestMapping注解。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller { }
Component
@Component 泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。常见的组件有:@Controller 控制层,@Service 业务逻辑层,@Repository DAO层/数据访问层。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component { }
方法注解
RequestMapping
@RequestMapping 用来标注请求的方法类型
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping { }
请求方法一共有 8 种(最常用的只有 GET 和 POST):
public enum RequestMethod {
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
}
GetMapping
@GetMapping 是对类型为 GET 的 RequestMapping 的封装
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.GET)
public @interface GetMapping { }
PostMapping
@PostMapping 是对类型为 POST 的 RequestMapping 的封装
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.POST)
public @interface PostMapping { }
ResponseBody
@PostMapping 将方法的返回值以特定的格式写入到response的body区域,进而将数据返回给客户端。如果返回的是字符串,则直接写入到body中;如果是对象,则转化为 json,然后写入body中。
但是注意:如果是返回对象,默认会按 UTF-8 编码;如果返回的是String,默认会按 iso8859-1 编码,因此页面可能会出现乱码。我们可以通过:@RequestMapping(value=“test”, produces=“text/html;charset=utf-8”),前面是请求的路径,后面是编码格式。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBody { }
参数注解
RequestBody
表单格式使用的是默认的 ContentType 类型 application/x-www-form-urlencoded 或者 multipart/form-data,格式为key1=value1&key2=value2 提交到后台 ,不需要加 @RequestBody。
如果请求的 ContentType 为 application/json,数据类型为 json 时,如 {“aaa”:“111”,“bbb”:“222”},必须要加 @RequestBody,这样注解的属性才能映射到值。
RequestParam
GET和POST请求传的参数会自动转换赋值到 @RequestParam 所注解的变量上。
例如:
//https://127.0.0.1/person?name=bob&age=28
@GetMapping("/person")
public PersonModel person(@RequestParam(value="name", defaultValue="unknown") String name, @RequestParam(value="age") String age) {
return new PersonModel(name, age);
}
PathVariable
@PathVariable 可以将 URL 中占位符参数,绑定到控制器处理方法的输入参数中。
例如:
//http://127.0.0.1:8080/blog/bob/123
@GetMapping("/blog/{name}/{pageId}")
public BlogModel blog(@PathVariable String name, @PathVariable String pageId) {
return new BlogModel (name, pageId);
}
带占位符的URL 是Spring3.0 新增的功能,该功能在SpringMVC 向REST目标挺进发展过程中具有里程碑的意义。
ModelAttribute
@ModelAttribute 用来将请求参数整合到一个类内部,这样,一来可以避免表单参数过多每个都要写注解的麻烦,二来也可以更好的面向对象进行封装抽象。
@ModelAttribute 可以整合 @RequestParam,@PathVariable,@MultipartFile 的参数。
特别注意:model 需要有 getter / setter 的方法才能被赋值,否则无法正确赋值。
例如:
//https://127.0.0.1/person?name=bob&age=28
@GetMapping("/person")
public PersonModel person(@ModelAttribute PersonModel model) {
return model;
}
//http://127.0.0.1:8080/blog/bob/123
@GetMapping("/blog/{name}/{pageId}")
public BlogModel blog(@ModelAttribute BlogModel model) {
return model;
}
MultipartFile
@MultipartFile 用于在 enctype=“multipart/form-data” 的 form 中上传文件数据。
public interface MultipartFile extends InputStreamSource {
//表单中的name值
String getName();
//文件的真实名称
String getOriginalFilename();
//获取文件的MIME类型(比如:image/png)
String getContentType();
//文件是否为空
boolean isEmpty();
//获取文件大小
long getSize();
//获取文件内容
byte[] getBytes() throws IOException;
//获取文件输入流
InputStream getInputStream() throws IOException;
//获取文件描述符
default Resource getResource() {
return new MultipartFileResource(this);
}
//将文件内容写入到指定文件
void transferTo(File dest) throws IOException, IllegalStateException;
//将文件内容写入到指定路径
default void transferTo(Path dest) throws IOException, IllegalStateException {
FileCopyUtils.copy(getInputStream(), Files.newOutputStream(dest));
}
}
案例实战
首先,创建一个spring boot starter项目,勾选 “ Web Starter ” 选项。
在 “src/main/resources/static/” 静态目录下,新建一个测试网页,比如 test.html
<!DOCTYPE HTML>
<html>
<head>
<title>Hello Static Page</title>
<meta charset="utf8">
<style>
#form1 input, textarea, button {
display: block;
margin-bottom: .5rem;
}
input[type=image], input[type=file] {
display: block;
}
</style>
</head>
<body>
<form id="form1" method="post" action="http://127.0.0.1:8080/form1">
<textarea name="message" rows="5" cols="15">
This is a long message!
</textarea>
<select name="car">
<option value="Toyota">丰田卡罗拉</option>
<option value="BaoJun">宝骏360</option>
<option value="BMW">宝马</option>
</select>
<input list="browser" name="browser" placeholder="browser">
<datalist id="browser">
<option value="Internet Explorer">
<option value="Firefox">
<option value="Chrome">
<option value="Opera">
<option value="Safari">
</datalist>
<fieldset>
<legend>This is fieldset's legend</legend>
<input type="text" name="text" placeholder="text">
<input type="password" name="password" placeholder="password">
<input type="email" name="email" placeholder="email"/>
<input type="url" name="url" placeholder="url"/>
<input type="number" name="number" step="2" placeholder="number"/>
<input type="range" name="range" />
<input type="date" name="date"/>
<input type="search" name="search" placeholder="keyword"/>
</fieldset>
<input type="submit" value="submit">
</form>
<form id="form2" method="post" action="http://127.0.0.1:8080/form2" oninput="x.value=parseInt(a.value)+parseInt(b.value)" enctype="multipart/form-data">
<input type="radio" name="sex" value="male" id="male">
<label for="male">男人</label>
<input type="radio" name="sex" value="female" id="female">
<label for="female">女人</label>
<button onclick="alert('This is button')">Click Me!</button>
I have a
<input type="checkbox" name="vehicle" value="bike">bike</input>
<input type="checkbox" name="vehicle" value="car">car</input>
<br>
0<input type="range" id="a" value="50">100
+<input type="number" id="b" value="50">
=<output name="x" for="a b"></output>
<br>
<input name="file1" type="file"/>
<input name="file2" type="file" accept="image/*" multiple/>
<br>
<input type="submit" value="submit">
</form>
</body>
</html>
效果如下,我在这里面做了两个表单
- output 标签是H5新增的标签,用来实时显示结果输出,oninput 是一个表单输入改变事件,可以在该事件中修改output的输出值。 output 标签的值不会被上送。
- 如果表单中存在需要上传的文件,需要在 form 上添加 enctype=“multipart/form-data”,意思是指明表单数据由多部分构成,既有文本数据,又有文件等二进制数据;默认的 enctype=“application/x-www-form-urlencoded”,这种情况下只能上传文本数据,不能上传文件。
新建两个对应的实体类
Form1Model.java
public class Form1Model {
public String message;
public String car;
public String browser;
public String text;
public String password;
public String email;
public String url;
public String number;
public String range;
public String date;
public String search;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getCar() {
return car;
}
public void setCar(String car) {
this.car = car;
}
public String getBrowser() {
return browser;
}
public void setBrowser(String browser) {
this.browser = browser;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getRange() {
return range;
}
public void setRange(String range) {
this.range = range;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getSearch() {
return search;
}
public void setSearch(String search) {
this.search = search;
}
}
Form2Model.java
public class Form2Model {
public String sex;
public String vehicle;
public String x;
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getVehicle() {
return vehicle;
}
public void setVehicle(String vehicle) {
this.vehicle = vehicle;
}
public String getX() {
return x;
}
public void setX(String x) {
this.x = x;
}
}
在 “src/main/java” 中新建 “TestController.java”
@PostMapping("/form1")
public Form1Model form1(@ModelAttribute Form1Model model) {
return model;
}
@PostMapping("/form2")
//这里的 MultipartFile 也可以整合到 ModelAttribute 里面,但是这样就不能直接返回这个model了
public Form2Model form2(@ModelAttribute Form2Model model, @RequestParam("file1") MultipartFile file1, @RequestParam("file2") MultipartFile[] file2) {
System.out.println("file1:\n" + printFile(file1));
System.out.println("file2:\n" + file2 != null ? printFile(file2[0]) : null);
return model;
}
private String printFile(MultipartFile file) {
if(file == null) return null;
StringBuilder sb = new StringBuilder();
sb.append("name: ");
sb.append(file.getName());
sb.append("\n");
sb.append("originalFilename: ");
sb.append(file.getOriginalFilename());
sb.append("\n");
sb.append("contentType: ");
sb.append(file.getContentType());
sb.append("\n");
sb.append("size: ");
sb.append(file.getSize());
sb.append("\n");
return sb.toString();
}
最后访问 http://127.0.0.1/test.html 提交表单,就可以测试了。