目录
1. Spring MVC 介绍
1.1 MVC 定义
1.2 MVC 和 Spring MVC 的关系
1.3 Spring、Spring Boot 和 Spring MVC 的关系
2. Spring MVC 的创建和连接
2.1 Spring MVC 项目返回视图
2.2 Spring MVC 项目返回数据
2.3 @RequestMapping 是 post 还是 get 请求
3. 获取参数
3.1 接收单个参数
3.2 接收多个参数
3.3 接收对象
3.4 接收表单
3.5 后端参数重命名
3.6 改成非必传参数
3.7 接收 JSON 对象
3.8 获取 url 中的参数
3.8 上传文件
3.9 获取 Cookie
3.10 获取 Session/header
3.11 获取 Header
4. 返回数据
4.1 返回静态页面
4.2 返回 text/html
4.3 返回 JSON 对象
4.4 请求转发或请求重定向
1. Spring MVC 介绍
Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架,从一开始就包含在 Spring 框架中。它的正式名称“Spring Web MVC”来自其源模块的名称(Spring-webmvc),但它通常被称为“Spring MVC”。
由上述可知:
- Spring MVC 是一个 Web 框架
- Spring MVC 是基于 Servlet API 构建的
1.1 MVC 定义
MVC 是 Model View Controller 的缩写,它是软件工程中的⼀种软件架构模式,它把软件系统分为模型、视图和控制器三个基本部分。
- Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。
- View(视图)是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的。
- Controller(控制器)是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据, 控制用户输入,并向模型发送数据。
现在的 Spring MVC 更合适称之为 Spring Web,因为现在很多项目都使用了前后端分离。
1.2 MVC 和 Spring MVC 的关系
MVC 是⼀种思想,而 Spring MVC 是对 MVC 思想的具体实现。
总结来说,Spring MVC 实现了 MVC 模式,并继承了 Servlet API 的 Web 框架。既然是 Web 框架,那么当用户在浏览器中输入了 url 之后,我们的 Spring MVC 项目就可以感知到用户的请求。
1.3 Spring、Spring Boot 和 Spring MVC 的关系
- Spring:包含了众多工具的 IoC 容器;
- Spring Boot:是简化 Spring 程序的;
- Spring MVC:使程序具备了 web 的能力。
现在绝大部分的 Java 项目都是基于 Spring(或 Spring Boot)的,而 Spring 的核心就是 Spring MVC。也就是说 Spring MVC 是 Spring 框架的核心模块,而 Spring Boot 是 Spring 的脚手架。
2. Spring MVC 的创建和连接
Spring MVC 项目创建和 Spring Boot 创建项目相同(Spring MVC 使用 Spring Boot 的方式创建), 在创建的时候选择 Spring Web 就相当于创建了 Spring MVC 的项目。
在 Spring MVC 中使用 @RequestMapping 来实现 URL 路由映射,也就是浏览器连接程序的作用。
接下来我们来创建 Spring MVC 项目:
2.1 Spring MVC 项目返回视图
早期的 Spring MVC 项目返回的是视图。
接下来,我们来看一下早期的 Spring MVC 项目是如何创建和连接的。
首先,导入 jar 包后,新建 WebController 类:
@RequestMapping("/web")
public class WebController {
@RequestMapping("/index")
public String index(){
return "hello,Spring MVC";
}
}
但是此时我们运行程序后,进行访问发现并不能成功访问:
因为我们只做了路径的映射,但是并没有交给 Spring 去管理,因此需要通过五大注解(@Bean)将 Bean 交给 Spring 去管理,修改代码如下:
@Controller
@RequestMapping("/web")
public class WebController {
@RequestMapping("/index")
public String index(){
return "hello,Spring MVC";
}
}
但是,可以看到依然无法进行访问:
因为现在的 Spring MVC 项目采用的是前后端分离的思想,返回的不再是视图而是数据,@Controller 表示的是返回一个视图,因此我们新建一个 html 文件来接收:
@Controller
@RequestMapping("/web")
public class WebController {
@RequestMapping("/index")
public String index(){
return "/index.html";
}
}
此时可以看到视图可以返回:
2.2 Spring MVC 项目返回数据
在早期的 Spring MVC 项目中,返回的都是视图,那么如果我们想返回数据该怎么处理呢?
使用 @ResponseBody 注解。
@Controller
@RequestMapping("/web")
public class WebController {
@ResponseBody
@RequestMapping("/index")
public String indexData(){
return "hello,Spring MVC";
}
}
此时,我们可以看到成功返回了数据:
通过 url:http://127.0.0.1:8080/web/indexData
我们可以知道 @RequestMapping 即是方法注解,又是类注解,访问的 url 等于类注解加上方法注解。
通过组合注解:@RestController = @ResponseBody + @Controller,同样可以实现返回数据:
@ResetController
@RequestMapping("/web")
public class WebController {
@RequestMapping("/index")
public String indexData(){
return "hello,Spring MVC";
}
}
@RestController 是类注解,表示该类下的所有方法返回的都是数据,而不是界面。
因此,如果一个类里面的方法,既有返回页面又有返回数据的,就不可以使用 @ResetController 注解。
那么既然是通过五大注解交给 Spring 管理的,我们可以把 @Controller 注解换成其他注解是否可以呢?
答案是否定的。因为只有 @Controller 才可以保证被外界访问到。
2.3 @RequestMapping 是 post 还是 get 请求
我们通过 Postman 来测试一下:
可以看到 @RequestMapping 同时支持 GET 和 POST 请求,那么如果只希望它支持 GET 请求呢?
@Controller
@RequestMapping("/web")
public class WebController {
@ResponseBody
@RequestMapping(value = "/indexData",method = RequestMethod.GET)
public String indexData(){
return "hello,Spring MVC";
}
}
可以看到此时无法再使用 POST 请求:
设置为只可以使用 POST 请求:
@Controller
@RequestMapping("/web")
public class WebController {
@ResponseBody
@RequestMapping(value = "/indexData",method = RequestMethod.POST)
public String indexData(){
return "hello,Spring MVC";
}
}
只允许 GET 请求:
// 写法一
@RequestMapping("/indexData")
// 写法二
@RequestMapping(value = "/indexData",method = RequestMethod.GET)
// 写法三
@GetMapping("/indexData")
只允许 POST 请求:
// 写法一
@RequestMapping(value = "/indexData",method = RequestMethod.POST)
// 写法二
@PostMapping("/indexData")
3. 获取参数
3.1 接收单个参数
在 Servlet 阶段,通过以下代码来获取参数:
@RestController
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/get1")
public String get1(HttpServletRequest request){
String name = request.getParameter("name");
return "name:"+name;
}
}
我们运行后可以看到:
那么,在 Spring 中,我们如何获取参数呢?
@RestController
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/get2")
public String get2(String name){
return "name:"+name;
}
}
可以看到通过以上代码,我们可以直接获取到:
3.2 接收多个参数
@RestController
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/get3")
public String get2(String name,Integer age){
return "name:"+name + "| age:"+age;
}
}
可以看到将两个参数的位置互换之后,结果依然不变:
也就是说参数的顺序和 url 传参的顺序无关。
@RestController
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/get4")
public String get4(Integer age){
return "age:"+age;
}
}
正确传参:正确响应
不传参:age = null
错误传参: age = aa
3.3 接收对象
当参数非常多的时候,一个一个写不太现实,同时会导致数据之间的耦合度太高,因此,我们可以将参数封装成一个对象。
@Data
public class Student {
private Integer id;
private String name;
private Integer age;
}
@RestController
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/get5")
public String get5(Student student){
return student.toString();
}
}
3.4 接收表单
表单是前端传参的方式,与接收参数的方式无关。接下来我们通过 postman 模拟表单发送:
可以看到同样可以接收到数据,通过 fillder 抓包可以看到:
3.5 后端参数重命名
@RestController
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/get6")
public String get6(@RequestParam("n") String name){
return "name: " +name;
}
}
把调用方发送的参数 n 重命名为 name。
3.6 改成非必传参数
可以看到将 n 改为 name 进行传递时,会出现以下报错:
说明了 n 是一个必传参数,但是我们没有传递,才导致以上报错。因此,我们可以将 n 修改为非必传参数:
@RestController
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/get7")
public String get7(@RequestParam(name = "n",required = false) String name){
return "name: " +name;
}
}
此时,页面显示如下:
3.7 接收 JSON 对象
使用 postman 发送 json:
使用 fillder 抓包:
可以看到成功传递了 json 对象。
@Slf4j
@RestController
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/get8")
public String get8(@RequestBody Student student){
log.info(student.toString());
return student.toString();
}
}
@RequestBody 表示接收的是 json 字符串。Spring 会将字符串转为 json 对象。
3.8 获取 url 中的参数
@RestController
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/get9/{shopid}")
public String get9(@PathVariable Integer shopid){
return "shopid:" + shopid;
}
}
通过 postman 可以看到,成功从 url 中获取了参数:
还可以重新命名参数的名称:
@RestController
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/get9/{shopid}/{dealid}")
public String get9(@PathVariable Integer shopid,@PathVariable("dealid")Integer dealId){
return "shopId:" + shopid +"| dealId:" + dealId;
}
}
同样可以获取到:
3.8 上传文件
@Slf4j
@RestController
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/get10")
public String get10(@RequestPart("file") MultipartFile file) throws IOException {
log.info(file.getOriginalFilename());
file.transferTo(new File("D:/temp/"+file.getOriginalFilename()));
return "success";
}
}
此时,我们的文件夹下并没有任何文件:
接下来,我们通过 postman 来上传文件:
此时看到 D 盘相应文件上传成功:
在 Fillder 中同样可以看到上传文件成功了:
3.9 获取 Cookie
@Slf4j
@RestController
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/get11")
public String get11(@CookieValue String value){
return "value:"+value;
}
}
可以看到出现以下报错:
接下来,我们按下 f12 进行设置:
然后刷新界面接可以看到获取到了 Cookie 值:
由上述可知:Cookie 是可以自行设置的。Cookie 是存在于客户端的,因此再次重启服务端(重新运行程序)获取的值依然不变。
3.10 获取 Session/header
@Slf4j
@RestController
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/get12")
public String get12(@SessionAttribute(required = false) String username){
return "username:"+username;
}
}
在 @SessionAttribute 注解加上:required = false(设置为非必传参数),可以看到在没有值时,不会进行报错,而是直接显示为 null。
运行上述代码后可以看到:
接下来,我们需要获取 Session 的值,Session 的值不能人为设置。
@Slf4j
@RestController
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/get12")
public String get12(@SessionAttribute(required = false) String username){
return "username:"+username;
}
@RequestMapping("/set1")
public String set1(HttpSession session){
session.setAttribute("username","value");
return "success";
}
}
设置后,Cookie 中多了一个 Sessionid。此时,我们来获取这个 session :
3.11 获取 Header
@Slf4j
@RestController
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/get13")
public String get13(@RequestHeader("User-Agent") String userAgent){
return "userAgent"+userAgent;
}
}
在 fidder 中可以看到两者值相同:
4. 返回数据
4.1 返回静态页面
在没有加上注解 @ResponseBody 时,返回的就是页面:
4.2 返回 text/html
4.3 返回 JSON 对象
@Slf4j
@RestController
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/get14")
public Map<String,String> get14(){
Map<String,String> map = new HashMap<>();
map.put("k1","v1");
map.put("k2","v2");
map.put("k3","v3");
map.put("k4","v4");
map.put("k5","v5");
return map;
}
}
4.4 请求转发或请求重定向
return 不但可以返回一个视图还可以实现跳转,跳转的方式有两种:
- forward:请求转发
- redirect:请求重定向
forward 和 redirect 具体区别如下:
- 请求重定向(redirect)将请求重新定位到资源;请求转发(forward)服务器端转发。
- 请求重定向地址发生变化,请求转发地址不发生变化。
- 请求重定向与直接访问新地址效果一致,不存在原来的外部资源不能访问;请求转发服务器端转发有可能造成原外部资源不能访问。
也就是说,请求转发时服务器进行转发,是由服务器内部进行处理的;而请求重定向时请求资源重定向,是对外的。
@Controller
@RequestMapping("/index")
public class IndexController {
@RequestMapping("/forward")
public String forward(){
return "forward:index.html";
}
@RequestMapping("/redirect")
public String redirect(){
return "redirect:index.html";
}
}
请求转发(forward):
请求重定向(redirect):
可以看到,请求转发的 url 并未发生变化,而请求重定向的 url 跳转到了另一个界面。