抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

摘要:本文学习了使用SpringMVC如何解析请求和接收参数。

环境

Windows 10 企业版 LTSC 21H2
Java 1.8
Tomcat 8.5.50
Maven 3.6.3
Spring 5.2.25.RELEASE

1 请求接入

1.1 @Controller

使用@Controller注解可以将类注册为接入层对象,具备处理请求的能力,是最基本的注解。

常用属性:

属性名 作用 取值
value 指定对象名称 对象名称,默认使用类名

示例:

java
1
2
3
@Controller
public class UserController {
}

1.2 @RestController

使用@RestController注解可以支持RESTful设计风格,所有方法默认返回JSON数据,是4.0引入的注解,等价于@Controller@ResponseBody的组合注解。

常用属性:

属性名 作用 取值
value 指定对象名称 对象名称,默认使用类名

示例:

java
1
2
3
@RestController
public class UserController {
}

1.3 @RequestMapping

使用@RequestMapping注解可以将请求映射到具体的方法,支持类级别和方法级别,是最核心的请求映射注解。

常用属性:

属性名 作用 取值
value或path 指定请求路径 表达式
method 指定请求方法 枚举值
params 指定请求参数条件 表达式
headers 指定请求头条件 表达式
consumes 指定请求内容类型 字符串
produces 指定响应内容类型 字符串

示例:

java
1
2
3
4
@RequestMapping(value = "/test", method = RequestMethod.GET)
public String test() {
return "success";
}

1.4 @XxxMapping

使用@XxxMapping注解可以简化@RequestMapping注解,是4.3引入的更简洁的组合注解:

  • @GetMapping:等价于@RequestMapping(method = RequestMethod.GET)
  • @PostMapping:等价于@RequestMapping(method = RequestMethod.POST)
  • @PutMapping:等价于@RequestMapping(method = RequestMethod.PUT)
  • @DeleteMapping:等价于@RequestMapping(method = RequestMethod.DELETE)
  • @PatchMapping:等价于@RequestMapping(method = RequestMethod.PATCH)

示例:

java
1
2
3
4
@GetMapping("/test")
public String test() {
return "success";
}

2 路径匹配

当存在多个匹配的映射时,会按照以下优先级进行匹配:

  1. 精确匹配
  2. 单个字符匹配
  3. 任意字符匹配
  4. 任意深度匹配

2.1 精确匹配

最简单的路径映射方式,URL必须完全匹配。

示例:

java
1
2
3
4
@RequestMapping("/test")
public String test() {
return "success";
}

2.2 模式匹配

SpringMVC支持Ant风格的路径模式。

2.2.1 单个字符

使用?匹配单个字符。

示例:

java
1
2
3
4
@RequestMapping("/test/?")
public String test() {
return "success";
}

匹配:

  • /test/a
  • /test/b
  • /test/c

2.2.2 任意字符

使用*匹配任意数量的字符,但不包括路径分隔符。

示例:

java
1
2
3
4
@RequestMapping("/test/*")
public String test() {
return "success";
}

匹配:

  • /test/a
  • /test/bb
  • /test/ccc

2.2.3 任意深度

使用**匹配任意深度的字符。

示例:

java
1
2
3
4
@RequestMapping("/test/**")
public String test() {
return "success";
}

匹配:

  • /test/a
  • /test/a/bb
  • /test/a/bb/ccc

3 请求映射

3.1 请求方法

通过method属性可以将同一个URL路径映射到不同的处理方法,根据请求方法区分。

示例:

java
1
2
3
4
@RequestMapping(value = "/test", method = RequestMethod.GET)
public String test() {
return "success";
}

3.2 请求条件

3.2.1 请求参数

通过params属性可以指定请求参数的条件。

示例:

java
1
2
3
4
@RequestMapping(value = "/test", params = "action=list")
public String test() {
return "success";
}

3.2.2 请求头

通过headers属性可以指定请求头的条件。

示例:

java
1
2
3
4
@RequestMapping(value = "/test", headers = "Referer=http://localhost:8080/demo-springmvc/")
public String test() {
return "success";
}

3.3 内容类型

3.3.1 请求内容类型

通过consumes属性可以指定处理请求的内容类型。

常用取值:

  • MediaType.APPLICATION_JSON_VALUE:对应application/json,接收JSON格式数据。
  • MediaType.APPLICATION_XML_VALUE:对应application/xml,接收XML格式数据。
  • MediaType.APPLICATION_FORM_URLENCODED_VALUE:对应application/x-www-form-urlencoded,接收表单格式数据。
  • MediaType.MULTIPART_FORM_DATA_VALUE:对应multipart/form-data,接收文件上传。
  • */*:接收任意内容类型,默认值。

示例:

java
1
2
3
4
@RequestMapping(value = "/test", consumes = MediaType.APPLICATION_JSON_VALUE)
public String test() {
return "success";
}

3.3.2 响应内容类型

通过produces属性可以指定响应的内容类型。

常用取值:

  • MediaType.APPLICATION_JSON_VALUE:对应application/json,返回JSON格式数据。
  • MediaType.APPLICATION_XML_VALUE:对应application/xml,返回XML格式数据。
  • MediaType.TEXT_HTML_VALUE:对应text/html,返回HTML格式数据。
  • MediaType.TEXT_PLAIN_VALUE:对应text/plain,返回纯文本数据。
  • MediaType.APPLICATION_OCTET_STREAM_VALUE:对应application/octet-stream,返回二进制文件流,用于文件下载。
  • */*:返回任意内容类型,默认值。

示例:

java
1
2
3
4
@RequestMapping(value = "/test", produces = MediaType.APPLICATION_JSON_VALUE)
public String test() {
return "success";
}

4 参数绑定

单个参数会自动绑定到属性中,多个参数会自动绑定到数组或者集合中。

4.1 @RequestParam

使用@RequestParam注解可以绑定请求参数,是最常用的参数绑定注解。

常用属性:

属性名 作用 取值
value或name 指定参数名 参数名,默认使用变量名
required 是否必填 默认为true表示必填,设置为false表示非必填
defaultValue 指定默认值 默认为null

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// id=1&name=张三
@GetMapping("/test")
public String test(
@RequestParam(defaultValue = "1") Integer id,
@RequestParam String name,
@RequestParam Map<String, String> params) {
System.out.println("id: " + id);
System.out.println("name: " + name);
// 遍历所有请求参数
for (Map.Entry<String, String> entry : params.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
return "success";
}

4.2 @PathVariable

使用@PathVariable注解可以绑定URL中的路径变量,支持在路径变量中使用正则表达式来限制变量的格式。

可以与@RequestParam注解一起使用,同时绑定请求参数和路径变量。

常用属性:

属性名 作用 取值
value或name 指定参数名 参数名,默认使用变量名
required 是否必填 默认为true表示必填,设置为false表示非必填

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// test/1/张三/2026-01-01?sex=男
@GetMapping("/test/{id}/{name}/{birth:\\d{4}-\\d{2}-\\d{2}}")
public String test(
@PathVariable("id") Integer id,
@PathVariable String name,
@PathVariable String birth,
String sex,
@PathVariable Map<String, String> params) {
System.out.println("id: " + id);
System.out.println("name: " + name);
System.out.println("birth: " + birth);
System.out.println("sex: " + sex);
// 遍历所有路径变量
for (Map.Entry<String, String> entry : params.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
return "success";
}

4.3 @ModelAttribute

使用@ModelAttribute注解可以绑定请求对象。

@RequestParam注解的比较:

  • @RequestParam注解只能用于方法参数,并且不能绑定对象。
  • @ModelAttribute注解可以用于方法参数和方法上,并且可以绑定对象。

常用属性:

属性名 作用 取值
value或name 指定参数名 参数名,在入参默认使用变量名,在方法默认使用返回类型
binding 是否绑定 默认为true表示绑定,设置为false表示非绑定

示例:

java
1
2
3
4
5
6
7
// id=1&name=张三
@GetMapping("/test")
public String test(@ModelAttribute User user) {
System.out.println("id: " + user.getId());
System.out.println("name: " + user.getName());
return "success";
}

如果在方法上使用@ModelAttribute注解,则会将方法的返回值绑定到模型中:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 有返回值时自动设置模型参数,如果没有指定参数名则默认使用返回类型作为参数名
@ModelAttribute("hobbies")
public List<String> populateHobbies() {
return Arrays.asList("唱歌", "跳舞");
}
// 手动设置模型参数
@ModelAttribute
public void populateInfo(Model model) {
model.addAttribute("id", 1);
model.addAttribute("name", "张三");
}
// 通过模型获取参数
@GetMapping("/test")
public String test(Model model) {
System.out.println(model.getAttribute("id"));
System.out.println(model.getAttribute("name"));
System.out.println(model.getAttribute("hobbies"));
return "success";
}

4.4 @RequestHeader

使用@RequestHeader注解可以绑定请求头信息。

常用属性:

属性名 作用 取值
value或name 指定参数名 参数名,默认使用变量名
required 是否必填 默认为true表示必填,设置为false表示非必填
defaultValue 指定默认值 默认为null

示例:

java
1
2
3
4
5
6
7
8
@GetMapping("/test")
public String test(
@RequestHeader("host") String host,
@RequestHeader(value = "referer", defaultValue = "") String referer) {
System.out.println("host: " + host);
System.out.println("referer: " + referer);
return "success";
}

4.5 @CookieValue

使用@CookieValue注解可以绑定Cookie信息。

常用属性:

属性名 作用 取值
value或name 指定参数名 参数名,默认使用变量名
required 是否必填 默认为true表示必填,设置为false表示非必填
defaultValue 指定默认值 默认为null

示例:

java
1
2
3
4
5
6
7
8
@GetMapping("/test")
public String test(
@CookieValue("JSESSIONID") String sessionId,
@CookieValue(defaultValue = "guest") String username) {
System.out.println("sessionId: " + sessionId);
System.out.println("username: " + username);
return "success";
}

4.6 @RequestBody

使用RequestBody注解可以绑定请求体中的JSON数据。

这种方式不能处理GET请求,因为GET请求没有请求体。请求体支持JSON和XML等格式,需要引入相应的Maven依赖。

添加依赖:

pom.xml
1
2
3
4
5
6
<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.1</version>
</dependency>

常用属性:

属性名 作用 取值
required 是否必填 默认为true表示必填,设置为false表示非必填

示例:

java
1
2
3
4
5
6
7
// JSON.stringify({id: 1, name: '张三'})
@PostMapping("/test")
public String test(@RequestBody User user) {
System.out.println("id: " + user.getId());
System.out.println("name: " + user.getName());
return "success";
}

4.7 RequestEntity

使用RequestEntity可以更灵活的获取请求信息。

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// JSON.stringify({id: 1, name: '张三'})
@PostMapping("/test")
public String test(RequestEntity<User> requestEntity) {
// 获取请求方法
HttpMethod method = requestEntity.getMethod();
System.out.println("method: " + method);
// 获取请求URL
URI url = requestEntity.getUrl();
System.out.println("url: " + url);
// 获取请求头
HttpHeaders headers = requestEntity.getHeaders();
System.out.println("content-type: " + headers.getContentType());
// 获取请求体
User user = requestEntity.getBody();
System.out.println("id: " + user.getId());
System.out.println("name: " + user.getName());
return "success";
}

5 参数验证

使用参数验证需要添加依赖:

pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- JavaEE Validation -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<!-- Hibernate Validator -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.13.Final</version>
</dependency>
<!-- EL -->
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.0</version>
</dependency>

创建实体类:

java
1
2
3
4
5
6
7
public class User {
@NotNull(message = "ID不能为空")
private Integer id;
@NotBlank(message = "姓名不能为空")
private String name;
// ...
}

5.1 @Valid

@Valid注解用于数据校验,是Java标准注解,需要引入相应的Maven依赖。

使用方式:

  • 验证对象参数:支持在方法参数上使用,只能验证对象参数,需要在对象属性上添加约束注解。
  • 验证对象嵌套:支持在对象属性上使用,只能验证对象属性,需要在对象属性的属性上添加约束注解。使用@Valid注解只能在对象存在时验证对象属性,如果对象不存在,就不会验证对象属性,所以需要同时使用@NotNull注解保证对象存在。
  • 验证对象分组:不支持。

5.1.1 验证对象参数

验证对象参数,如果验证失败抛出Spring提供的BindException异常:

java
1
2
3
4
5
6
7
8
9
@Controller
public class UserController {
// id=1&name=张三
@GetMapping("/test")
public String test(@Valid User user) {
System.out.println(user);
return "success";
}
}

5.1.2 验证对象嵌套

修改实体类,增加嵌套属性:

java
1
2
3
4
5
6
7
8
9
10
public class User {
@NotNull(message = "ID不能为空")
private Integer id;
@NotBlank(message = "姓名不能为空")
private String name;
@Valid
@NotNull(message = "地址不能为空")
private Address address;
// ...
}

创建嵌套类:

java
1
2
3
4
public class Address {
@NotBlank(message = "省份不能为空")
private String province;
}

验证对象嵌套,如果验证失败抛出Spring提供的BindException异常:

java
1
2
3
4
5
6
7
8
9
@Controller
public class UserController {
// id=1&name=张三&address.province=北京
@GetMapping("/test")
public String test(@Valid User user) {
System.out.println(user);
return "success";
}
}

5.2 @Validated

@Validated注解用于数据校验,是Spring框架提供的注解。

使用方式:

  • 验证简单参数:支持在类上使用,只能验证简单参数,需要在方法参数上添加约束注解,还需要配置方法验证后置处理器。
  • 验证对象参数:支持在方法参数上使用,只能验证对象参数,需要在对象属性上添加约束注解。
  • 验证对象嵌套:不支持,需要配合@Valid注解使用。
  • 验证对象分组:支持在方法参数上使用并且需要指定分组,只能验证对象参数,需要在对象属性上添加约束注解并且指定分组。

5.2.1 验证简单参数

验证简单参数需要配置后置处理器,建议配置为静态方法:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
@EnableWebMvc
@ComponentScan("com.example.controller")
public class WebConfig implements WebMvcConfigurer {
// 后置处理器需要处理对象的初始化,要在所有对象实例化之前创建,包括配置类
// 配置类会在解析@Bean注解时进行实例化
// 将后置处理器配置为静态方法,保证在不实例化配置类的情况下调用,避免警告
@Bean
public static MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
// ...
}

验证简单参数,如果验证失败抛出Java提供的ConstraintViolationException异常:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
@Controller
@Validated
public class UserController {
// id=1&name=张三
@GetMapping("/test")
public String test(
@NotNull(message = "ID不能为空") Integer id,
@NotBlank(message = "姓名不能为空") String name) {
System.out.println("id: " + id);
System.out.println("name: " + name);
return "success";
}
}

5.2.2 验证对象参数

验证对象参数,如果验证失败抛出Spring提供的BindException异常:

java
1
2
3
4
5
6
7
8
9
@Controller
public class UserController {
// id=1&name=张三
@GetMapping("/test")
public String test(@Validated User user) {
System.out.println(user);
return "success";
}
}

5.2.3 验证对象嵌套

修改实体类,增加嵌套属性,需要配合@Valid注解使用:

java
1
2
3
4
5
6
7
8
9
10
public class User {
@NotNull(message = "ID不能为空")
private Integer id;
@NotBlank(message = "姓名不能为空")
private String name;
@Valid
@NotNull(message = "地址不能为空")
private Address address;
// ...
}

创建嵌套类:

java
1
2
3
4
public class Address {
@NotBlank(message = "省份不能为空")
private String province;
}

验证对象嵌套,如果验证失败抛出Spring提供的BindException异常:

java
1
2
3
4
5
6
7
8
9
@Controller
public class UserController {
// id=1&name=张三&address.province=北京
@GetMapping("/test")
public String test(@Validated User user) {
System.out.println(user);
return "success";
}
}

5.2.4 验证对象分组

创建分组接口:

java
1
public interface UserGroup {}

修改实体类,使用分组:

java
1
2
3
4
5
6
7
public class User {
@NotNull(message = "ID不能为空")
private Integer id;
@NotBlank(groups = UserGroup.class, message = "姓名不能为空")
private String name;
// ...
}

验证对象分组,如果验证失败抛出Spring提供的BindException异常:

java
1
2
3
4
5
6
7
8
9
@Controller
public class UserController {
// id=1&name=张三
@GetMapping("/test")
public String test(@Validated(UserGroup.class) User user) {
System.out.println(user);
return "success";
}
}

5.3 获取验证结果

支持传入BindingResult类型的参数获取验证结果。

使用@Valid注解验证对象参数:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
@Controller
public class UserController {
// id=1&name=张三
@GetMapping("/test")
public String test(@Valid User user, BindingResult result) {
System.out.println(user);
if (result.hasErrors()) {
result.getFieldErrors().forEach(error -> System.out.println(error.getDefaultMessage()));
return "error";
}
return "success";
}
}

使用@Validated注解验证对象参数,不能验证简单参数:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Controller
@Validated
public class UserController {
// id=1&name=张三
@GetMapping("/test")
public String test(@Validated User user, BindingResult result) {
System.out.println(user);
if (result.hasErrors()) {
result.getFieldErrors().forEach(error -> System.out.println(error.getDefaultMessage()));
return "error";
}
return "success";
}
}

6 格式处理

6.1 @DateTimeFormat

使用@DateTimeFormat注解可以指定日期格式,支持日期和时间。

示例:

java
1
2
3
4
5
6
// birth=2026-01-03
@GetMapping("/test")
public String test(@DateTimeFormat(pattern = "yyyy-MM-dd") Date birth) {
System.out.println("birth: " + birth);
return "success";
}

6.2 @NumberFormat

使用@NumberFormat注解可以指定数字格式,支持货币和百分比。

示例:

java
1
2
3
4
5
6
// amount=100,000
@GetMapping("/test")
public String test(@NumberFormat(pattern = "###,###.##") BigDecimal amount) {
System.out.println("amount: " + amount);
return "success";
}

7 类型转换

7.1 Converter

自定义转换器通过实现Converter接口实现,是全局通用的类型转换工具,适用于任意两个类型之间的转换,单个转换器只能单向转换,不支持本地化,是最基础的转换方式。

定义转换器:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class BirthConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
if (source == null || source.trim().isEmpty()) {
return null;
}
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.parse(source);
} catch (Exception e) {
throw new IllegalArgumentException("日期格式错误");
}
}
}

配置转换器:

java
1
2
3
4
5
6
7
8
9
10
11
@Configuration
@EnableWebMvc
@ComponentScan("com.example.controller")
public class WebConfig implements WebMvcConfigurer {
// 配置转换器
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new BirthConverter());
}
// ...
}

使用转换器:

java
1
2
3
4
5
6
7
8
// id=1&name=张三&birth=2026-01-01
@GetMapping("/test")
public String test(Integer id, String name, Date birth) {
System.out.println("id: " + id);
System.out.println("name: " + name);
System.out.println("birth: " + birth);
return "success";
}

7.2 Formatter

自定义格式化器通过实现Formatter接口实现,是全局通用的格式化工具,仅支持任意类型和String的转换,单个格式化器同时支持双向转换,支持本地化,优先级比自定义转换器高。

定义格式化器:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class BirthFormatter implements Formatter<Date> {
@Override
public Date parse(String source, Locale locale) throws ParseException {
if (source == null || source.trim().isEmpty()) {
return null;
}
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.parse(source);
} catch (Exception e) {
throw new IllegalArgumentException("日期格式错误");
}
}
@Override
public String print(Date source, Locale locale) {
if (source == null) {
return "";
}
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
return sdf.format(source);
} catch (Exception e) {
throw new IllegalArgumentException("日期格式错误");
}
}
}

配置格式化器:

java
1
2
3
4
5
6
7
8
9
10
11
@Configuration
@EnableWebMvc
@ComponentScan("com.example.controller")
public class WebConfig implements WebMvcConfigurer {
// 配置格式化器
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatter(new BirthFormatter());
}
// ...
}

使用格式化器:

java
1
2
3
4
5
6
7
8
// id=1&name=张三&birth=2026-01-01
@GetMapping("/test")
public String test(Integer id, String name, Date birth) {
System.out.println("id: " + id);
System.out.println("name: " + name);
System.out.println("birth: " + birth);
return "success";
}

7.3 @InitBinder

使用@InitBinder注解可以自定义参数绑定器,对单个Controller定制参数绑定规则,满足个性化需求,优先级比转换器和格式化器高。

使用@InitBinder注解修饰的方法需要返回void类型,并且接受WebDataBinder类型的参数。方法会在控制器处理请求之前执行,初始化WebDataBinder对象,将请求参数绑定到目标方法参数。

7.3.1 绑定属性编辑器

绑定属性编辑器,支持自定义属性编辑器:

java
1
2
3
4
5
6
7
8
9
@InitBinder
public void initBinder(WebDataBinder binder) {
// 设置日期格式
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
// 设置严格日期
dateFormat.setLenient(false);
// 绑定属性编辑器
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}

使用属性编辑器:

java
1
2
3
4
5
6
7
8
// id=1&name=张三&birth=2026-01-01
@GetMapping("/test")
public String test(Integer id, String name, Date birth) {
System.out.println("id: " + id);
System.out.println("name: " + name);
System.out.println("birth: " + birth);
return "success";
}

7.3.2 设置属性访问权限

支持设置禁止绑定指定字段和只能绑定指定字段,只对@ModelAttribute注解修饰的对象属性生效。

禁止绑定指定字段,可以省略@ModelAttribute注解:

java
1
2
3
4
5
6
7
8
9
10
11
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.setDisallowedFields("id");
}
// id=1&name=张三
@GetMapping("/test")
public String test(User user) {
System.out.println("id: " + user.getId());// null
System.out.println("name: " + user.getName());// 张三
return "success";
}

只能绑定指定字段,可以省略@ModelAttribute注解:

java
1
2
3
4
5
6
7
8
9
10
11
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.setAllowedFields("name");
}
// id=1&name=张三
@GetMapping("/test")
public String test(User user) {
System.out.println("id: " + user.getId());// null
System.out.println("name: " + user.getName());// 张三
return "success";
}

7.4 @ExceptionHandler

使用@ExceptionHandler注解可以自定义异常处理器,对单个Controller定制异常处理规则。

使用@ExceptionHandler注解修饰的方法支持多种返回类型,方法支持多种参数类型,并且同时可以处理多种异常类型。

支持的参数类型:

  • HttpServletRequest:请求对象
  • HttpServletResponse:响应对象
  • Model:模型对象
  • Exception:异常类型,通常与@ExceptionHandler注解指定的异常类型一致

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
// id=1
@GetMapping("/test")
public String test(User user) throws Exception {
if (user.getName() == null) {
throw new Exception("姓名不能为空");
}
return "success";
}
@ExceptionHandler(Exception.class)
public String handleException(Exception e, Model model) {
model.addAttribute("message", e.getMessage());
return "error";
}

8 文件上传

支持上传单个文件和多个文件,上传多个文件需要传入数组参数。

创建上传表单:

jsp
1
2
3
4
5
6
7
8
9
10
11
<form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data">
<div>
<label for="id">ID:</label>
<input type="text" id="id" name="id">
</div>
<div>
<label for="file">头像:</label>
<input type="file" id="file" name="file">
</div>
<button type="submit">提交</button>
</form>

接收文件上传:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@PostMapping("/upload")
public String upload(Integer id, MultipartFile file, HttpServletRequest request) throws IOException {
if (file != null && !file.isEmpty()) {
// 获取项目根目录绝对路径
String rootPath = request.getServletContext().getRealPath("/");
// 创建上传目录
File uploadDir = new File(rootPath, "uploads");
if (!uploadDir.exists()) {
uploadDir.mkdirs();
}
// 保存文件
String fileName = id + "_" + file.getOriginalFilename();
File destFile = new File(uploadDir, fileName);
file.transferTo(destFile);
System.out.println("文件保存路径: " + destFile.getPath());
}
return "success";
}

8.1 基于上传工具

早期Spring实现上传需要通过上传工具实现,需要引入相应的Maven依赖。

添加依赖:

pom.xml
1
2
3
4
5
6
<!-- Commons FileUpload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>

配置文件上传解析器:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Configuration
@EnableWebMvc
@ComponentScan("com.example.controller")
public class WebConfig implements WebMvcConfigurer {
// 配置文件上传解析器
@Bean
public MultipartResolver multipartResolver() throws IOException {
// 创建解析器对象
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
// 设置编码格式
resolver.setDefaultEncoding("UTF-8");
// 设置最大上传文件大小(字节):10MB
resolver.setMaxUploadSize(10 * 1024 * 1024);
// 设置单个文件最大大小(字节):5MB
resolver.setMaxUploadSizePerFile(5 * 1024 * 1024);
// 设置内存阈值(字节):超过1MB写入临时文件
resolver.setMaxInMemorySize(1024 * 1024);
// 设置临时文件目录
resolver.setUploadTempDir(new FileSystemResource("/tmp"));
// 返回解析器对象
return resolver;
}
// ...
}

8.2 基于新版功能

从Spring的3.1版本开始,引入了内置的上传解析器,不需要引入额外的依赖。

配置文件上传解析器:

java
1
2
3
4
5
6
7
8
9
10
11
@Configuration
@EnableWebMvc
@ComponentScan("com.example.controller")
public class WebConfig implements WebMvcConfigurer {
// 配置文件上传解析器
@Bean
public StandardServletMultipartResolver multipartResolver() {
return new StandardServletMultipartResolver();
}
// ...
}

配置文件上传参数:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
// 配置文件上传参数
MultipartConfigElement multipartConfig = new MultipartConfigElement(
System.getProperty("java.io.tmpdir"), // 临时目录(需存在且有写权限)
5 * 1024 * 1024, // 单个文件最大大小(5MB)
10 * 1024 * 1024, // 整个请求最大大小(10MB)
1024 * 1024 // 内存阈值(超过1MB写入临时文件)
);
registration.setMultipartConfig(multipartConfig);
}
// ...
}

评论