Spring MVC 核心实战
什么是 Spring MVC
Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架,从一开始就包含在 Spring 框架中。它的正式名称'Spring Web MVC'来自其源模块的名称 (Spring-webmvc),但它通常被称为'Spring MVC'。

MVC 架构模式
MVC 是 Model View Controller 的缩写,它是软件工程中的一种软件架构模式,它把软件系统分为模型、视图和控制器三个基本部分。

Model(模型):应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。 View(视图):应用程序中处理数据显示的部分。通常视图是依据模型数据创建的。 Controller(控制器):应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。
MVC 和 Spring MVC 的区别
MVC 是一种思想,而 Spring MVC 是对 MVC 思想的具体实现。总结来说,Spring MVC 是一个实现了 MVC 模式,并继承了 Servlet API 的 Web 框架。既然是 Web 框架,那么当用户在浏览器中输入了 url 之后,我们的 Spring MVC 项目就可以感知到用户的请求。
RequestMapping 注解
@RequestMapping 是 Spring Web 应用程序中最常被用到的注解之一,它是用来注册接口的路由映射的。
路由映射:所谓的路由映射指的是,当用户访问一个 url 时,将用户的请求对应到程序中某个类的某个方法的过程就叫路由映射。
@RequestMapping 即可修饰类,也可以修饰方法,当修饰类和方法时,访问的地址是类 + 方法。
GET 还是 POST?
@RequestMapping("/request")
@RestController
public class RequestController {
@RequestMapping("/hello")
public String say(){
return "hello, Spring mvc";
}
}
使用 get 请求访问该接口,能够正常返回结果;使用 post 请求同样可以访问。我们发现,@RequestMapping 既能处理 get 请求也能处理 post 请求。

传递参数详解
无参数情况
如果方法不需要任何参数,直接调用即可。
@RequestMapping("/request")
@RestController
public class RequestController {
@RequestMapping("/hello")
public String say(){
return "hello, Spring mvc";
}
}
结果会正常返回字符串内容。

单个参数
针对 String 类型
@RequestMapping("/request")
@RestController
public class RequestController {
@RequestMapping("/r1")
public String r1(String name){
return "接收到参数,name:"+name;
}
}
正确传参的结果如下,参数名必须保持一致。

不传递参数的结果(结果为 null):

传递错误参数的结果(结果为 null):

由此我们知道,传递单个参数时,参数名必须保持一致!!!
针对 Integer 类型
@RequestMapping("/r2")
public String r2(Integer age){
return "接收到参数,age:"+age;
}
正确传参的结果:

错误传参的结果(结果为 null):

不传参的结果(结果为 null):

对于 Integer 类型的参数,当不传参数时,默认值为 null,如果传参名字不一致,值也是默认值 null。
针对 int 类型
@RequestMapping("/r3")
public String r2(int age){
return "接收到参数,age:"+age;
}
正确传参的结果:

错误传参的结果(错误码 500):

不传参的结果(错误码 500):

对于 int 类型的参数,如果传参的参数名不一致时,会报错且错误码为 500;如果不传参,也会报错且错误码为 500,这一点与 int 的包装类型 Integer 不一样!!!
针对自定义类型
@RequestMapping("/r5")
public String r5(Student student){
return "接收到参数,student:"+student;
}
正确传参的结果:

部分传参的结果:

错误传参的结果:

针对自定义类型,如果不传参也不会报错,包装类型参数的默认值为 null,如果传参类型不匹配会报错!!!
多个参数
@RequestMapping("/r4")
public String r4(String name,Integer age){
return "接收到参数,name:"+name+",age:"+age;
}

参数重命名
有时候方法参数名和前端传来的参数名不一致,可以使用 @RequestParam 进行绑定。
参数强制一致
@RequestMapping("/r6")
public String r6(@RequestParam("name") String userName){
return "接收到参数,name:"+userName;
}
正确传参的结果:

不传参的结果(错误码 400):

错误传参的结果(错误码 400):

当参数重命名之后,默认要求该参数必须传参,否则会报错!!!
参数不强制一致
@RequestMapping("/r7")
public String r7(@RequestParam(value = "name",required = false) String userName){
return "接收到参数,name:"+userName;
}
正确传参的结果:

错误传参的结果(结果为 null):

不传参的结果(结果为 null):

当参数重命名之后,添加 required=false 之后,该参数就不要求必须传递了。
传递数组
@RequestMapping("/r8")
public String r8(String[] array){
return "接收到参数,array:"+ Arrays.toString(array);
}
可以这样传递:

也可以这样传递:

传递 List
@RequestMapping("/r9")
public String r9(List<String> list){
return "接收到参数,list:"+list;
}
响应:

解决方法:
@RequestMapping("/r9")
public String r9(@RequestParam(required = false) List<String> list){
return "接收到参数,list:"+list;
}
响应:

当参数类型是 List 类型时,必须添加 @RequestParam 注解,否则会报错且错误码为 500。
传递 JSON
@RequestMapping("/r10")
public String r10(@RequestBody Student student){
return "接收到参数,student:"+student;
}
响应:

通过 fiddler 抓包可以看到请求体结构:

原因分析:是因为它把 Json 字符串当成了一个不可拆分的整体,所以这样拿不到传递的参数。
解决办法就是加上 @RequestBody 注解,Spring 会自动将 JSON 字符串反序列化为对象。

当传递 Json 字符串时,必须添加 @RequestBody 注解!!!
从路径中获取参数
获取单个参数
@RequestMapping("/article/{articleId}")
public String r11(Integer articleId){
return "接收到参数,article:"+articleId;
}
响应(出错):

解决方法,使用 @PathVariable 注解。
@RequestMapping("/article/{articleId}")
public String r11(@PathVariable("articleId") Integer articleId){
return "接收到参数,article:"+articleId;
}
响应:

关于为什么需要加 @PathVariable("articleId"),这里有几个关键点:
- 明确性:
@PathVariable注解明确指出了哪个 URL 模板变量应该被绑定到方法的哪个参数上。虽然 Spring MVC 也可以尝试通过参数名来自动匹配(如果参数名与 URL 模板中的变量名相同),但使用@PathVariable增加了代码的清晰度和明确性。 - 灵活性:在某些情况下,URL 模板中的变量名可能与你想在控制器方法中使用的参数名不同。通过
@PathVariable,你可以指定一个不同的参数名,这提供了额外的灵活性。 - 可读性:对于阅读代码的人来说,
@PathVariable注解使得 URL 模板变量到方法参数的绑定一目了然,有助于理解代码的功能和目的。 - 强制类型转换:
@PathVariable注解允许你指定参数的类型(如 Integer),这意味着 Spring MVC 会尝试将 URL 模板变量的值转换为指定的类型。如果没有这个注解,你可能需要手动进行类型转换。 - 必需性:虽然在这个特定示例中
@PathVariable可能看起来不是绝对必需的(因为参数名与 URL 模板变量名相同),但在更复杂的 URL 模板或更复杂的控制器方法中,使用@PathVariable可以确保你的代码更加健壮和易于维护。
获取多个参数
@RequestMapping("/article/{articleId}/{name}")
public String r12(@PathVariable("articleId") Integer articleId,@PathVariable("name") Integer name){
return "接收到参数,article:"+articleId+",name:"+name;
}
响应:

在通过路径获取参数时,如果从路径中获取多个参数,传参时必须每个都传递,否则会报错,因为不知道是哪个/哪些参数没有传递!!!
上传文件
@RequestMapping("/r13")
public String r13(@RequestPart("file")MultipartFile file){
String originalFilename=file.getOriginalFilename();
return "接收到参数,文件名称:"+originalFilename;
}
响应:

在上传文件时,文件不能太大,否则会上传失败!!!

