1.什么是Spring MVC?
1.1 MVC的定义
MVC是Model View Controller的缩写,它是软件工程中的一种软件架构模式,它把软件系统分为模型、视图和控制器三个部分。
Model(模型) 是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存储数据。
View(视图) 是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的。
Controller(控制器) 是应用程序中处理用户交互的部分。通常控制器负责从视图中读取数据,控制用户输入,并向模型发送数据
1.2 MVC和Spring MVC的关系
MVC是一种思想,而Spring MVC是对MVC思想的具体实现。就类似于我们在学习Spring时的IoC和DI一样,IoC是一种思想,而DI是具体的实现。
1.3 Spring、Spring Boot、Spring MVC三者的关系
Spring是包含了众多工具方法的IoC容器。
Spring Boot是为了简化Spring开发而产生的脚手架。
Spring MVC是一个基于MVC设计模式和Servlet API实现的Web项目,同时Spring MVC又是Spring框架中的一个Web模块,它是随着Spring的诞生而存在的一个框架。
2.使用用户和程序的映射
Spring MVC项目创建和Spring Boot创建项目相同(Spring MVC使用Spring Boot),在创建的时候选择了Spring Web就相当于创建了Spring MVC的项目。
先完成Spring MVC项目的创建,再进行下边的工作:
2.1 @RequestMapping注解
@RequestMapping是Spring Web应用程序中最常用到的注解之一,它是用来注册接口的路由映射的。
路由映射:当用户访问一个url时,将用户的请求对应到应用程序的某个类的某个方法中的过程就叫做路由映射。
@RequestMapping基础使用如下:
@RequestMapping既可以修饰类,也可以修饰方法,当修饰类和方法时,访问的地址是类+方法对应的url。访问结果如下图:
默认情况下,@RequestMapping既支持POST请求方式,也支持GET请求方式。但如果想要只支持一种请求,也可以指定哪种请求,如下代码所示:
@RequestMapping(value="/sayhi",method=RequestMethod.GET) @RequestMapping(value="/sayhi",method=RequestMethod.POST)
2.2 @GetMapping和@PostMapping
如果既想要只支持一种访问方式,还想要更简单的写法,就可以使用下边的方式:
GET请求
@GetMapping("/sayhi")
POST请求
@PostMapping("/sayhi")
3.服务端得到用户的请求参数
3.1 获取单个参数
使用postman来构造请求:
注意,整形建议设置类型为包装类(Integer),在设置为包装类时,如果没有传参数,则该参数只是为空,而如果设置的是int类型,不传参数将会报错。
3.2 获取多个参数
代码:
构造请求:
注意:
在获取多个参数的时候,前端传来的参数顺序没影响,参数是按照Key-Value来赋值的,只要保证前后端参数的名称Key一致即可。
在进行交互时一般参数命名都用小写,防止出现解析错误的情况。
3.3 参数重命名
在3.2中获取到多个参数的前提,是要保证前后端参数的命名相同(即Key值相同),但如果前端传来的参数并不合理,可以通过参数重命名的方法,将前端传来的参数映射到自己命名的参数上,进一步完成后续的开发。
方法为使用@RequestParam注解;
比如在3.2中的示例中,前端传来的参数是name,而不是username,就需要进行重命名,代码演示如下:
构造请求:
注意事项:
在加了@RequestParam注解以后,那么前端一定要传递此参数,否则就会报错,如果想要解决此问题,可以给@RequestParam里面添加required=false。
3.4 获取对象
在程序中编写对象类,前端传来参数就给对象完成了初始化。
User类:
获取对象:
基于form表单构造请求:
注意:在对象中的整形,Integer类不传参数默认为null,int类不传参数默认为0,并不会报错。
构造JSON格式请求:
我们发现如果构造JSON格式的请求,并不能获取到参数,我们用Fiddler来进行抓包
我们可以发现,请求报文中确实是有参数,但并没有被获取到,需要在参数列表中加上@RequestBody注解才能解决这个问题。
3.5 从URL地址中获取参数
注意,此处获取参数是通过URL地址,而不是从URL的参数获取
实现需要通过注解@PathVariable,代码演示如下:
构造请求:
3.6 获取文件
实现获取文件的功能,需要用到注解@RequestPart。
因为在不同的环境下图片的保存位置不同,所以需要在配置文件中声明图片的保存路径,配置文件设置如下:
防止图片由于路径的相同而导致图片被覆盖,还需要给图片名增添随机性,就是使用UUID,整体实现代码如下:
使用Postman模拟上传文件(基于form-data格式上传文件)
3.7 获取Cookie
因为Spring MVC是基于Servlet API实现的,所以其天然支持Servlet API,在Spring MVC项目中的每个方法,都可以获取到HttpServletRequest和HttpServletResponce对象。
所以获取Cookie有传统的基于Servlet的方法,和使用Spring MVC中注解的方法来获取Cookie
a)基于Servlet的方式获取Cookie
为了防止空指针异常,我们先在浏览器随便加上一些Cookie。
然后刷新页面,看idea控制台的日志信息打印:
b)使用@CookieValue注解实现Cookie的读取
请求访问:
3.8 获取Header
a)基于Servlet的方式获取请求头中的信息
b)使用@RequestHeader注解来实现
3.9 存储和获取Session
存储Session的实现,Servlet和Spring MVC的操作方法相同,代码如下:
存储成功后,浏览器的cookie中会多出一条记录(如下图),红框记录的Key-Value中的Value就是sessionID,可以根据这个sessionID来找到session会话,对应的session中就是我们刚刚通过setAttribute()方法存进去的session属性Key-Value。
获取session还是有两种方法:
a)基于Servlet
b)使用@SessionAttribute注解
红框中的代码通常要加,如果不加的话,那么当session中不存在此属性的时候程序就会报错。
构造请求:
4.服务端返回结果给用户
4.1 返回静态页面
在1.1 中我们就提到了Spring MVC的MVC架构模式(如下图),因为Spring MVC是随着Spring的诞生而诞生的,在那个时期并没有将前后端分离开来,所以在View层也就不存在前后端视图的说法,所以,在Spring MVC的方法中,默认返回的都是静态页面的名称。
我们先在resource资源文件路径下新建前端页面
前端代码如下:
后端代码:
浏览器访问路径:
4.2 返回非静态页面数据
因为在4.1中我们提到了Spring MVC方法默认返回的是一个静态页面,如果我们想要返回非静态页面的数据,就需要通过注解@ResponseBody或者组合注解@RestController来实现。
@ResponseBody
与4.1相同的代码,加上@ResponseBody注解,我们再来看浏览器访问的结果
再次通过浏览器进行访问,就会发现返回的是非静态页面的数据了。
注意:
@ResponseBody注解既可以修饰类,又可以修饰方法,如果加在类上:表示当前类中所有方法都会返回一个非静态页面的数据;但如果加在方法上:表示当前方法返回的是一个非静态页面数据。
@RestController
@RestController注解相当于@Controller+@ResponseBody,所以它的效果就相当于两个注解的效果和。
4.3 返回JSON对象
在Spring MVC的方法中,服务器给前端返回一个JSON格式的对象,要比Servlet的方式简单很多,直接返回存储了键值对的HashMap对象给前端,Spring就能让其自动识别转变为JSON格式返回给前端。
代码如下:
构造请求,结果如下:
4.4 综合练习
计算器
前端使用form表单的方式提交参数,在后端实现计算器的功能返回结果给前端。
前端代码如下:
后端代码:
浏览器构造请求演示:
登录
前端使用ajax请求传递参数,后端返回状态给前端,实现登录的功能。
前端代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <script src="js/jquery.min.js"></script> <title>登录</title> <script> // ajax提交 function mysub(){ // 1.判空 let username=jQuery("#username"); let password=jQuery("#password"); if(jQuery.trim(username.val())=="") { alert("请先输入用户名!"); username.focus();// 光标重置到此处 return; } if(jQuery.trim(password.val())=="") { alert("请先输入密码!"); password.focus();// 光标重置到此处 return; } jQuery.ajax({ url:"/login", type:"POST", data:{"username":username.val(), "password":password.val()}, success:function(body) { alert(JSON.stringify(body)); } }); } </script> </head> <body> <div style="text-align: center;"> <h1>登录</h1> 用户:<input id="username"><br> 密码:<input id="password" type="password"><br> <input type="button" value="提交" onclick="mysub()" style="margin-top: 20px;margin-left: 50px;"> </div> </body> </html>
后端代码:
浏览器构造请求演示运行:
4.5 请求转发或请求重定向
forward VS redirect
return 不但可以返回视图或者数据,还可以实现跳转,跳转的方式有以下两种:
forward实现请求转发
redirect实现请求重定向
两者的理解可以用一个借钱的例子来理解,张三向李四借钱,李四没有钱,但他向王五借来钱,又给了张三,这就是请求转发的过程,李四代为完成了张三的请求;但如果李四没有钱,他告诉张三王五有钱,让张三去找王五借钱,这个过程就是请求重定向的过程。
分别来看两种方法的请求结果:
forward
redirect
可以发现两个方法都跳转成功到了同一个界面,但两者又有不同,具体区别如下:
【参考文章链接跳转】
经典面试题:请求转发和请求重定向的区别
1.定义不同
请求转发(forward)发生在服务端程序内部,当服务端收到一个客户端的请求之后,会先将请求转发给目标地址,再将目标地址返回的结果转发给客户端。
而请求重定向(redirect)是指服务端接受到客户端的请求以后,会给客户端返回一个临时响应头,这个临时响应头中记录了客户端需要再次发送请求(重定向)的URL地址,然后客户端按照这个URL再次发送请求。
2.请求方不同
请求转发(forward)是服务端的行为,而请求重定向(redirect)是客户端的行为。
交互流程如下图所示:
3.数据共享不同
请求转发(forward)是由服务端实现的,所以整个执行流程中,客户端只发送了一次请求,因此整个交互过程中都是同一个Request对象和一个Response对象,所以请求和返回的数据是共享的;而请求重定向(redirect)是向由客户端发送两次完全不同的请求,所以两次请求中的数据是不同的。
4.最终URL地址不同
在上边的浏览器访问结果中我们也可以看出,虽然访问到的是同一个界面,诞生两次访问的最终URL地址是不同的。这是因为请求转发(forward)是在服务器端代为完成的,整个过程URL地址不变,而请求重定向(redirect)是服务器端告诉客户端另一个地址,客户端进行了再一次访问,而非刚开始请求的地址,所以URL地址发生了改变。
5.代码实现不同