目录
1.3 Spring、Spring Boot 和 Spring MVC 的关系
2.3 @RequestMapping 是 post 还是 get 请求
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 跳转到了另一个界面。