目录
1. 什么是Spring MVC?
1.1 什么是MVC
1.2 MVC和Spring MVC 的关系
1.3 为什么要学Spring MVC?
2. Spring MVC的使用
2.1 Spring MVC的创建和连接
2.1.1 项目创建
2.1.2 项目连接
2.1.2.1 连接
2.1.2.2 @RequestMapping注解介绍
2.1.2.3 @GetMapping和 @PostMapping
2.2 获取参数
获取单个参数
获取多个参数
获取对象
@RequestParam重命名后端参数 (后端参数映射)
设置参数必传@RequestParam
非必传参数设置
@RequestBody接收JSON对象
@PathVariable获取URL基础参数(不是从URL参数部分获取参数)
@RequestPart上传文件
@CookieValue获取Cookie
@RequestHeader获取header
@SessionAttribute获取Session
2.3 返回数据
返回静态页面
返回text/html
返回JSON对象
请求转发或请求重定向
两者区别
定义
请求方
数据共享
最终URL
代码实现不同
查看更多注解
1. 什么是Spring MVC?
官方描述:
Spring Web MVC is the original web framework built on the Servlet APl and has been included in the Spring Framework from the very beginning. The formal name, "Spring Web MVC", comes from the name of its source module (spring-webmvc), but it is more commonly known as "Spring MVC"
Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架,从一开始就包含在 Spring 框架中。它的正式名称“Spring Web MVC”来自其源模块的名称(Spring-webmvc),但它通常被称“Spring MVC”
由官方对Spring MVC的定义可以知道:
- Spring MVC是一个Web框架;
- Spring MVC是基于Servlet API构建的.
1.1 什么是MVC
定义: MVC是Model View Controller的缩写, 它是软件工程中的一种软件架构模式, 它把软件系统分为 模型, 视图, 控制器 三个基本部分.
- Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。
- View(视图)是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的。
- Controller(控制器)是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据控制用户输入,并向模型发送数据
当用户发送请求的时候, 请求会首先来到Controller控制器, 然后在Controller里面 验证完请求没有问题之后就会将请求转发给Model模型层(也就是数据模型), 然后数据模型会根据请求的信息得到相应的结果, 然后把结果返回给控制器, 接下来控制器再把结果交给View视图渲染器, 然后View就根据它的模板并加上返回来的数据进行数据的解析和渲染, 最终将用户可以看得懂的页面交给用户, 这样就完成了一次响应. 这是最初的MVC的机制.
模型层是比较广义上的模型层, 程序中实体类所在的层就是这个Model模型层, 也可以叫做entity, 那么这里的模型指的是数据模型.
但是目前来讲这种模式已经过时了, 现在都是在做前后端分离的, 已经不再使用View这种已经淘汰的技术来返回页面给用户.
Spring提供的Web容器只有这一个, 我们实现的是基于http协议的, 所以我们需要用这个框架.
Spring MVC经过它的演变和改善, 是可以直接从Controller返回数据给用户的, 也就是说不再经过View视图的渲染了.
1.2 MVC和Spring MVC 的关系
MVC 是一种思想,而 Spring MVC 是对 MVC 思想的具体实现。
总结来说,Spring MVC 是一个实现了 MVC 模式,并继承了 Servlet API的 Web 框架。既然是 Web框架,那么当用户在浏览器中输入了 url 之后,我们的 Spring MVC 项目就可以感知到用户的请求。
1.3 为什么要学Spring MVC?
现在绝大部分的 Java 项目都是基于 Spring (或 Spring Boot)的,而 Spring 的核心就是 Spring MVC。也就是说 Spring MVC 是 Spring 框架的核心模块,而 Spring Boot 是 Spring 的脚手架,因此我们可以推断出,现在市面上绝大部分的 Java 项目约等于 Spring MVC 项目,这是我们要学 SpringMVC 的原因。
2. Spring MVC的使用
学习Spring MVC的使用需要掌握以下它的功能:
- 连接的功能: 将用户 (浏览器) 和 Java 程序连接起来,也就是访问一个地址能够调用到我们Spring程序。
- 获取参数的功能: 用户访问的时候会带一些参数,在程序中能够获取到用户输入的参数。
- 输出数据的功能: 执行了业务处理,然后把程序执行的结果返回给用户。
2.1 Spring MVC的创建和连接
2.1.1 项目创建
Spring MVC 可以基于 Spring Boot 创建,也就是创建一个 Spring Boot 项目,勾选上 Spring Web模块即可,如下图所示:
2.1.2 项目连接
2.1.2.1 连接
接下来,创建一个 UserController 类,实现用户到 Spring 程序的互联互通, 具体实现代码如下:
package com.example.demo.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;//@Controller // 让 Spring 框架启动时, 加载
//@ResponseBody // 返回非页面数据
@RestController // = @Controller + @ResponseBody
public class UserController {@RequestMapping("/say") // 该注解能让后端获取到用户请求, 后文介绍该注解public String sayHi(){return "Hi Spring MVC";}
}
这样实现之后,当访问地址: http://localhost:8080/say 时就能打印"Hi Spring MVC"的信息了.
2.1.2.2 @RequestMapping注解介绍
@RequestMapping 是 Spring Web 的常用注解之一,它是用来注册接口的路由映射的。
路由映射: 当用户访问一个 url 时, 将用户的请求对应到程序中某个的某个方法的过程就叫路由映射.
@RequestMapping的使用就如上面例子的打印"Hi Spring MVC"所示, 上例是用于修饰方法的, 那么它也可以用于修饰类. 当修饰了类和方法之后就要注意访问地址为: 类+方法.
注: 还可以在一个@RequestMapping中设置多级路由.
@RequestMapping能够支持较多的请求, 最常见的就是GET和POST, 那么它也能够支持PUT, PATCH, DELETE等其他请求, 适用范围较广. 我们通过工具Postman来发送不同种类的请求来验证(验证过程略). 也可以在它的源码中找到.
从源码中我们可以知道, 我们还可以通过加method来控制@RequestMapping的请求类型, 比如:
@RequestMapping(value = "/say", method = RequestMethod.POST)
此时只有POST类型的请求能够获取, 其他类型的请求就无法获取, 比如获取GET, 就会直接报错.
2.1.2.3 @GetMapping和 @PostMapping
通过@GetMapping和 @PostMapping可以单独的只支持GET请求或者POST请求.
@PostMapping("/sayhello")
public String sayHello() {return "hello spring mvc";
}@GetMapping("/hi")
public String hi() {return "spring mvc hi";
}
2.2 获取参数
获取单个参数
在Spring MVC中可以直接用方法中的参数来实现传参,比如以下代码:
@RequestMapping("/sayhi")
public String sayHi(String name) {return "Hi " + name;
}
获取多个参数
@RequestMapping("/sayhi") // 可以是一级路由, 也可以是 N 级路由
public String sayHi(String name, String password) {return "hi " + name + " | password: " + password;
}
注意: 当有多个参数时,前后端进行参数匹配时,是以参数的名称进行匹配的,因此参数的位置是不影响后端获取参数的结果。
获取对象
package com.example.demo.entity;import lombok.Data;@Data
public class Userinfo {private int id;public String name;private String password;private int age;
}
@RequestMapping("/reg")
public Object reg(Userinfo userinfo) {System.out.println(userinfo);return userinfo;
}
@RequestParam重命名后端参数 (后端参数映射)
某些特殊的情况下,前端传递的参数 key 和我们后端接收的 key 可以不一致,比如前端传递了一个username 给后端,而后端又是有name字段来接收的,这样就会出现参数接收不到的情况,如果出现这种情况,我们就可以使用 @RequestParam 来重命名前后端的参数值。
@RequestMapping("/reg2")
public Object reg2(@RequestParam("username") String name, String password) {return name + " : " + password;
}
设置参数必传@RequestParam
上面的例子,如果我们是前端传递一个非 username 的参数,就会出现程序报错的情况,如下图所示:
这是因为后端已经声明了前端必须传递一个username的参数,但是前端没有给后端传递,我们查看@RequestParam 注解的实现细节就可以发现端倪,注解实现如下:
required: 必须的意思,默认值为 true,因此不传递此参数就会报 400 的错误.
非必传参数设置
如果我们的实际业务前端的参数是一个非必传的参数,我们可以通过设置 @RequestParam 中的required=false 来避免不传递时报错,具体实现如下:
@RequestMapping("/reg2")
public Object reg2(@RequestParam(value = "username", required = false) String name, String password) {return name + " : " + password;
}
@RequestBody接收JSON对象
@RequestMapping("/reg3")
public Object reg3(@RequestBody Userinfo userinfo) {return userinfo;
}
@PathVariable获取URL基础参数(不是从URL参数部分获取参数)
@RequestMapping("/reg4/{name}/{pwd}")
public Object reg4(@PathVariable String name, @PathVariable(required = false, name = "pwd") String password) {return "name=" + name + " | password=" + password;
}
注意事项: @PostMapping("/reg4/{name}/{password}")中的 {password} 参数不能省略
@RequestPart上传文件
@RequestMapping("/myupload")public Object upload(@RequestPart("myimg6") MultipartFile file) {String fileName = UUID.randomUUID() + // 文件名file.getOriginalFilename().substring( // 文件后缀file.getOriginalFilename().lastIndexOf("."));File saveFile = new File("D:\\JavaWebStudy\\" + fileName);try {file.transferTo(saveFile);return true; } catch (IOException e) {e.printStackTrace();}return false;}
通过Postman模拟上传文件,
注: 文件大小有要求, 那么可以通过Spring官方文档查询默认值, 进而在配置文件中修改默认大小. Common Application Properties
@CookieValue获取Cookie
@RequestMapping("/getck")
public Object getCk(@CookieValue(value = "java", required = false) String java) {return java;
}
注意, 直接访问/getck是没有任何东西在页面上的, 那么需要我们F12中手动伪造一份Cookie, 才能看见.
@RequestHeader获取header
@RequestMapping("/gethd")
public Object getHeader(@RequestHeader("Host") String host) {return "header -> " + host;
}
@SessionAttribute获取Session
private static final String SESSION_KEY = "USERINFO_SESSION_KEY";// 存储 session@RequestMapping("/setsess")public void setSess(HttpServletRequest request) {HttpSession session = request.getSession();session.setAttribute(SESSION_KEY, "zhangsan");}// 获取 session@RequestMapping("/getsess")public Object getSession(@SessionAttribute(SESSION_KEY) String name) {return "session -> " + name;}
执行完localhost:8080/setsess后再访问localhost:8080/getsess
2.3 返回数据
返回静态页面
在resources.static中创建前端页面index.html:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>index.html</title>
</head>
<body><h1>hello index</h1>
</body>
</html>
创建控制器TestController:
package com.example.demo.controller;import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;@Controller
@RequestMapping("/test")
public class TestController {@RequestMapping("/index")public Object getIndex(){return "/index.html";}
}
注意代码中的返回路径:
当return中加了"/"之后就代表是从根路径去找index.html的.
当不加"/"的时候, 它是在"/test"底下去找index.html的, 即在当前目录底下去找index.html的. 显然在上面的URL并找不到, 因为index.html是放在根路径下的.
返回text/html
package com.example.demo.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class OwnerController {@RequestMapping("/m7")public String method_1(){return "<h1>hi, h1.</h1>";}
}
返回JSON对象
@RequestMapping("/m2")public HashMap<String, String> method_2(){HashMap<String, String> map = new HashMap<>();map.put("C", "C Value");map.put("Java", "Java Value");map.put("MySQL", "MySQL Value");return map;}
请求转发或请求重定向
// TestController下, 类注解@RequestMapping("/test")// 请求转发@RequestMapping("/fw")public String fw(){return "forward:/index.html";}// 请求重定向@RequestMapping("/rd")public String rd(){return "redirect:/index.html";}
请求转发和请求重定向的直观区别:
请求转发: 当输入localhost:8080/test/fw后, URL不变.
请求重定向: 当输入localhost:8080/test/rd后, URL变为localhost:8080/index.html
两者区别
定义
请求转发(Forward)是发生于服务器程序内的, 当服务器收到客户端的请求之后, 服务器会先将请求转发给目标地址, 由目标地址对请求进行处理, 再将目标地址返回的结果返回给客户端. 那么显然客户端就只知道发出了请求, 并收到了返回结果, 但是客户端并不知道服务器内部发生了转发行为.
请求重定向(Redirect)主要发生于客户端程序内, 当服务器收到客户端的请求之后, 服务器会给客户端发送一个用于重定向的临时响应头, 这个响应头记录了重定向之后的目标地址, 然后客户端会向这个目标地址发起请求.
请求方
数据共享
请求转发是由服务器实现的, 在整个执行流程中, 客户端只会发送一次请求, 所以在整个交互过程中使用的都是同一个请求对象和响应对象, 这说明在请求和返回的数据是共享的.
请求重定向则是客户端向服务器发起两次完全不同的请求, 所以两次请求中的数据是不同的.
最终URL
请求转发是服务器端代为请求,再将结果返回给客户端的,所以整个请求的过程中 URL 地址是不变的;
而请求重定向是服务器端告诉客户端,"你去另一个地访问去”,所以浏览器会重新再发送一次请求,因此客户端最终显示的 URL也为最终跳转的地址,而非刚开始请求的地址,所以 URL 地址发生了改变.
代码实现不同
由前文例子可见.
注: 以上关于请求转发和请求重定向的区别说明摘自请求转发和请求重定向有什么区别? | Javaᶜⁿ 面试突击
查看更多注解
Request Mapping :: Spring Framework