目录
官方的解释是:
Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架,从⼀开始就包含在 Spring 框架中。它 的正式名称“Spring Web MVC”来⾃其源模块的名称(Spring-webmvc),但它通常被称为“Spring MVC” 。
从上述定义我们可以得出两个关键信息:
1. Spring MVC 是⼀个 Web 框架。
2. Spring MVC 是基于 Servlet API 构建的。
MVC 是 Model View Controller 的缩写,它是软件⼯程中的⼀种软件架构模式,它把软件系统分为模型、视图和控制器三个基本部分。

Model(模型):是应⽤程序中⽤于处理应⽤程序数据逻辑的部分。通常模型对象负责在数据库
View(视图):是应⽤程序中处理数据显示的部分。通常视图是依据模型数据创建的。
Controller(控制器):是应⽤程序中处理⽤户交互的部分。通常控制器负责从视图读取数据, 控制⽤户输⼊,并向模型发送数据。
注意:View(视图)分为服务器端的视图和客户端的视图。客户端视图就是我们看到的在浏览器上展示的视图,而服务器视图是还未经过客户端渲染的数据。而这里我们返回的视图实际是服务器视图。
MVC 是⼀种思想,而Spring MVC 是对 MVC 思想的具体实现(类似于前面我们讲的IoC与DI的关系)。 总结来说,Spring MVC 是⼀个实现了 MVC 模式,并继承了 Servlet API 的 Web 框架。既然是 Web 框 架,那么当⽤户在浏览器中输入了 url 之后,我们的 Spring MVC 项⽬就可以感知到⽤户的请求。
我们主要需要掌握以下功能:
1. 连接的功能:将⽤户(浏览器)和 Java 程序连接起来,也就是访问⼀个地址能够调⽤到我们的 Spring 程序。
2. 获取参数的功能:⽤户访问的时候会带⼀些参数,在程序中要想办法获取到参数。
3. 输出数据的功能:执⾏了业务逻辑之后,要把程序执⾏的结果返回给⽤户。
@RequestMapping 是 Spring Web 应⽤程序中最常被⽤到的注解之⼀,它是⽤来注册接⼝的路由映射的。
路由映射:所谓的路由映射指的是,当⽤户访问⼀个 url 时,将⽤户的请求对应到程序中某个类的某个方法的过程就叫路由映射。
@RequestMapping 基础使⽤:
- @Controller
- public class PersonController {
- @RequestMapping("/index")
- public Object index(Person person){
- // 获取参数
- System.out.println(person.getName() +":"+
- person.getPassword());
- // 执⾏业务...
- return "/index.html";
- }
- }
@RequestMapping 即可修饰类,也可以修饰方法,当修饰类和方法时,访问的地址是类 + 方法。
比如以上代码我们就可以通过http://localhost:8080/index来访问
我们知道一般我们都是通过GET或者POST方法来去访问网页的,那么@RequestMapping支持的是哪种请求呢?
实际上@RequestMapping默认既支持GET也支持POST请求。
如果我们有些特殊的需求要求某个网页只能通过GET或者POST方法访问我们该怎么办呢?
这里我们可以使用@GetMapping 和 PostMapping来指定请求的方法。
1.GET请求的3种写法
- // 写法1
- @RequestMapping("/index")
- // 写法2
- @RequestMapping(value = "/index",method = RequestMethod.GET)
- // 写法3
- @GetMapping("/index")
2.post 请求的3种写法:
- //写法1
- @RequestMapping("/index")
- // 写法2
- @RequestMapping(value = "/index",method = RequestMethod.POST)
- // 写法3
- @PostMapping("/index")
在 Spring MVC 中可以直接⽤⽅法中的参数来实现传参,⽐如以下代码:
- @RequestMapping("/m1")
- @ResponseBody
- public String method_1(Integer id){
- return "参数 id:"+id;
-
- }
注意我们这里使用的是包装类Integer而不是int。这是为了防止有时用户不传参数而直接导致500的错误。而使用了包装类之后即使用户没有传参数也只会返回null而不会导致500错误。这时候就需要我们在前后端进行参数校验。
同时前端发送的参数名一定要与后端设置的参数名一致,否则会导致后端无法读取参数。
并且 Spring MVC 可以⾃动实现参数对象的赋值,⽐如 Person 对象:
- @Data
- public class Person {
- private int id;
- private String name;
- private String password;
- }
传递对象代码实现:
- @RequestMapping("/m2")
- @ResponseBody
- public Person method_2(Person p){
- return p;
- }
此时访问网址@Data注解中的toString方法会自动帮我们输出Person对象中的信息。
- @RequestMapping("/m3")
- @ResponseBody
- public String method_3(String name, String pwd) {
- return name+" "+password
- }
某些特殊的情况下,前端传递的参数 key 和我们后端接收的 key 可以不⼀致,比如前端传递了⼀个 time 给后端,⽽后端⼜是有 createtime 字段来接收的,这样就会出现参数接收不到的情况,如果出现 这种情况,我们就可以使用 @RequestParam 来重命名前后端的参数值。
具体示例如下,后端实现代码:
- @RequestMapping("/m4")
- @ResponseBody
- public String method_4(@RequestParam("time") String createtime) {
- return "时间:" + createtime;
- }
但是我们会发现即使我们此时的参数已经是一个对象而不是基础数据类型,但在参数为空的时候还是会引发500错误,这是为什么呢?
我们看到@RequestParam 的源代码

其中required这个方法默认为true,也就是它要求我们必须要传入参数,否则就会报错。此时我们可以通过在@RequestParam修改这个参数来避免这种现象发生。
- @RequestMapping("/m4")
- @ResponseBody
- public String method_4(@RequestParam(value="time",required = false) String createtime) {
- return "时间:" + createtime;
- }
虽然我们的Spring已经很智能,可以让我们在接收或者返回参数时不用再像servlet一样设置参数的类型,但是还是有一些需要我们注意的。
如果前端发送的是一个JSON对象而我们使用@RequestMapping来接收就会发现参数全部都是null。也就是@RequestMapping实际上无法接收JSON对象。此时我们需要用到@RequestBody 接收JSON对象。
- @RequestMapping(value = "/m5", method = RequestMethod.POST)
- @ResponseBody
- public String method_5(@RequestBody Person person) {
- return person;
- }
我们一般传参数都是通过query来传递的,比如下面的url。?后面的就是我们的query
![]()
而我们这里说的url中的参数实际上是?前面的部分。
那你可能想问这有什么用,直接在query内传参不就好了吗,何必多此一举。而实际上将参数放置在url中是为了提高网站在搜索引擎中的优先级,提高网站的曝光率。
后端实现代码:
- @RequestMapping("/user/{id}/{name}")
- @ResponseBody
- public String getUserInfo(@PathVariable Integer id,@PathVariable String name){
- return "id= "+id+" "+"name= "+name;
- }
这里@RequestMapping("/user/{id}/{name}")就是我们要获取的参数在url中的位置。
比如在这里就代表/user后的两个参数就是我们要获取的id和name
http://localhost:8080/user/123/张三
此时我们获取的就是id=123,name=张三
而我们还需要在参数前面加上@PathVariable
在上传文件时,我们需要用到MultipartFile这个类型,它代表了我们将要上传的文件。
而关于@RequestPart注解它和我们前面提到的@RequestParam注解作用类似,具体区别可以看到下面这篇博客@RequestParam和@RequestPart的区别_借物小人的博客-CSDN博客_requestpart
而关于文件的上传我们主要需要解决两个问题:
1.文件上传路径的问题。我们一般不会将文件上传的路径写死,而是会通过配置文件来读取
2.文件的命名问题。上传后的文件如果重名了会导致先上传的文件被覆盖,这里我们采用UUID(通用唯一识别码)的方式来规避这个问题
- public class UploadFiles {
-
- @Value("${img.path}")
- //从配置文件获取存储文件的路径
- private String imgPath;
-
- @RequestMapping("/upload")
- public boolean upImg(Integer uid, @RequestPart("img")MultipartFile file){
- boolean result=false;
- String fileName=file.getOriginalFilename();
- fileName=fileName.substring(fileName.lastIndexOf("."));//得到文件后缀
- fileName= UUID.randomUUID().toString()+fileName;//使用UUID避免文件名重复
- //保存文件到本地目录
- try {
- file.transferTo(new File(imgPath+fileName));//将文件转移到目标文件中
- result=true;
- } catch (IOException e) {
- log.error("上传文件失败"+e.getMessage());
- }
- return result;
- }
- }
在上一小节我们提到,在上传文件时路径不要写死,而是要通过读取配置文件来获得。但是我们开发环境(Windows)与运行环境(Linux)实际上在很多方面都不一样。这就需要我们配置多个配置文件来应对不同的环境,那么我们该怎么办呢?
下面我们以properties配置文件为例子,yml同理:
![]()
可以看到我们不同平台的命名规则是:
application-平台.properties(yml)
那么我们该怎么选择我们选择运行的是哪一个配置文件呢?这就需要我们在主配置文件中进行设置了。

注意这里只需要跟上我们的平台名就好,不需要将配置文件全名都写上。
最后我们总结一下,主配置文件在我们开发中一般就是写一些通用的配置,而不同平台的配置文件之间就写不同平台之间的差异配置。
比如我们上面提到的文件上传路径在Linux和Windows中就是不一样的。
![]()
传统获取 header/cookie
- @RequestMapping("/param10")
- @ResponseBody
- public String param10(HttpServletResponse response, HttpServletRequest
- request) {
- String name = request.getParameter("name");
- // 获取所有 cookie 信息
- Cookie[] cookies = request.getCookies();
- //获取header中的UA信息
- String userAgent = request.getHeader("User-Agent");
- return name + ":"+userAgent;
- }
简洁的获取 Cookie—@CookieValue
- @RequestMapping("/cookie")
- @ResponseBody
- public String cookie(@CookieValue("test") String getcookie) {
- return getcookie;
- }
这里同样需要注意@CookieValue 的value值要和前端的cookie的名字对应才能获取到,下面的@RequestHeader 获取header同理
简洁获取 Header—@RequestHeader
- @RequestMapping("/header")
- @ResponseBody
- public String header(@RequestHeader("User-Agent") String userAgent) {
- return "userAgent:"+userAgent;
- }
Session 存储和 Servlet 类似,是使⽤ HttpServletRequest 中获取的。为什么不使用注解来存储session呢,由于我们注解是在我们定义方法体或者参数时写的,但是session不能在参数内创建,所以session只能使用我们的传统方法。
session存储如下代码所示:
- @RequestMapping("/setsess")
- @ResponseBody
- public String setsess(HttpServletRequest request) {
- // 获取 HttpSession 对象,参数设置为 true 表示如果没有 session 对象就创建⼀个
- session
- HttpSession session = request.getSession(true);
- if(session!=null){
- session.setAttribute("username","java");
- }
- return "session 存储成功";
- }
读取 Session 可以使⽤ HttpServletRequest,如下代码所示:
- @RequestMapping("/sess")
- @ResponseBody
- public String sess(HttpServletRequest request) {
- // 如果 session 不存在,不会⾃动创建
- HttpSession session = request.getSession(false);
- String username = "暂⽆";
- if(session!=null && session.getAttribute("username")!=null){
- username = (String) session.getAttribute("username");
- }
- return "username:"+username;
- }
获取 Session 更简洁的⽅式——@SessionAttribute:
- @RequestMapping("/sess2")
- @ResponseBody
- public String sess2(@SessionAttribute(value = "username",required =
- false) String username) {
- return "username:"+username;
- }
通过上⾯的学习我们知道,默认请求下⽆论是 Spring MVC 或者是 Spring Boot 返回的是视图 (xxx.html),⽽现在都是前后端分离的,后端只需要返给给前端数据即可,这个时候我们就需要使⽤ @ResponseBody 注解了。
创建前端页⾯ index.html

- html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport"
- content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
- <meta http-equiv="X-UA-Compatible" content="ie=edge">
- <title>hello,spring mvctitle>
- <script src="index.js">script>
- head>
- <body>
- <h1>Hello,Spring MVC.h1>
- body>
- html>
返回index.html
- @RequestMapping("/m7")
- public String method_7() {
- return "index.html";
- }
这里需要用到@ResponseBody 注解。当然我们也可以使用复合注解@RestController。它是@controller和@ResponseBody 的结合。
@Controller 将当前修饰的类注入SpringBoot IOC容器,使得从该类所在的项目跑起来的过程中,这个类就被实例化。
@ResponseBody 它的作用简短截说就是指该类中所有的API接口返回的数据,不管你对应的方法返回Map或是其他Object,它会以Json字符串的形式返回给客户端