本篇主要介绍Spring MVC 的概念和基本使用
目录
一、Spring MVC 的概念
什么是MVC?
在介绍Spring MVC 之前,我们先来了解一下什么是MVC。
MVC是一种web架构设计思想,它将整个web项目分为了三个层次,View(视图)、Controller(控制器)、Mode(模型)。
这三个层次具体的功能如下
- View:主要用于与用户的交互,显示界面和数据
- Mode:进行具体的业务逻辑和数据处理
- Controller:相当于一个分发中心,他能将View的请求传递到具体的模块,并前将模块处理的数据返回给视图
一个web项目的具体执行逻辑为
用户根据视图的显示并通过浏览器向控制器发送请求,控制器收到请求后选择具体的模型来执行业务逻辑,然后模型再将处理好的数据返回给控制器,控制器再依据数据选择一个新的视图返回给浏览器,然后浏览器再将这个新的视图展示给用户。
MVC的优势
使用MVC这种设计思想有什么优势呢?
首先,MVC实现了用户和服务端的交互,其次MVC的分层处理降低了项目的耦合程度,因为各层次只要完成相应的任务即可,无需去关注其他层的实现细节。
Spring MVC
Spring MVC 是一种基于MVC思想实现的web开发框架,它相当于是在Spring Boot的基础上引入了一个web模块,从而具有了web开发的功能,所以Spring MVC也可以被称为Spring Web。Spring MVC项目的创建方式也很简单,只需在创建Spring Boot项目时,引入Spring Web的相关依赖即可。在Spring MVC中对Tomcat服务器进行了集成,我们启动一个spring MVC项目就相当于启动了一个web服务器。
二、Spring MVC的使用
Spring MVC的使用主要从三个方面入手,建立连接,请求,响应
建立连接
在Spring MVC中,主要通过注解@RequestMapping注解与浏览器建立连接,他能将url中的地址信息与其中的参数进行路由映射,从而定位到处理请求所需要执行的方法(这个方法就是平常所说的接口)
我们通过代码具体来看一下
@RestController
public class Controller {
@RequestMapping("hello")
public String test01(){
return "hello";
}
}
然后我们通过postman构建一个请求,请求的地址与我们在RequestMapping中设置的参数保持一致
发送请求后成功返回了我们方法中的返回值
在上述案例中,我们可以知道的是要建立与客户端的连接,首先得配置好匹配的路由,也就是注解的参数(可以称为路由)与url中地址的保持一致 ,并且方法的返回值就是我们所返回的数据,并且我们需要在类的上面再加一个RestController注解。
下面我们来详细了解一下@RequestMapping这个注解
首先,@RequestMapping既可以加在类上也可以加在方法上,加在方法上我们前面演示过了,加在类上的话如果我们需要访问类中的方法,就必须在请求url中再加上一层。就比如我们在类上加一个注解,路由设为"hi"
@RestController
@RequestMapping("hi")
public class Controller {
@RequestMapping("hello")
public String test01(){
return "hello";
}
}
如果我们在按前面的url进行请求的话 ,就会返回一个404错误,因为在服务器中已经无法找到与之匹配的路由了
要想成功获取匹配到路由,就得在url中加上修饰类的@RequestMapping的路由
这次我们就能顺利的匹配到方法,并返回数据了 。这要做主要是为了区分不同类中的方法。
@RequestMapping中的参数还有很多,我们打开源码看一下
在源码中我们可以发现,在 @RequestMapping上还加了很多注解,像这种加在注解上的注解我们称之为元注解,在这些元注解中有一个@Target,他代表当前注解哪些地方可以加,里面的参数ElementType.TYPE和ElementType.METHOD,前者是表示类,后者表示方法,这也就说明了为什么@RequestMapping既可以加在类上也可以加在方法上。然后我们再来看一下下面@RequestMapping的参数,我们这里先只介绍path和method,path即为我们前面配置的路由,而method则代表所支持的http方法,如果我们不去设置的话,默认是支持所有方法的,如果我们想设置成只支持其中一种方法的话,就得自己写参数了,注意我们前面由于只写了path一种参数,如果要加入其他参数的话就得标注一下是哪个参数了,具体如下
此时就只能通过get方法来 访问了,如果使用其他方法的请求,则会报405方法不允许的错误
请求
在Spring MVC中,请求中的数据是通过方法形参获得的。
获取单个或多个数据
我们直接在方法中加入形参就能获取到来自请求的单个或多个数据,具体后端代码如下
@RequestMapping("test02")
public String test02(String s1 , String s2){
return s1+s2;
}
然后我们再通过Postman构建一个请求,并让请求带上与后端方法的形参同名的数据(原因后面会介绍)
后端的响应如下
通过响应我们能发现请求的数据已经被后端获取并返回了。
参数重命名
前面我们在获取参数时,获取的是请求中与后端方法的形参同名的数据,如果不同名会怎么样呢,我们在构建一个请求来看一下
后端的响应
通过响应我们发现s2已经被后端拿到了,而s没有拿到,所以返回了一个null,那我们要如何拿到像s这样与后端形参不同名的数据呢,此时我们就需要对参数进行重命名了,对参数重命名我们得在需要重命名的形参上加上一个注解@RequestParam,并将它的value参数设为我们需要的请求中的数据名称,具体代码如下:
@RequestMapping("test02")
public String test02(@RequestParam(value = "s") String s1 , String s2){
return s1+s2;
}
再发送一下上面相同的请求
通过响应可以发现,这次s的值已经获取到了。前面之所以要去传与形参同名的请求数据,是因为如果没有对参数重命名,Spring MVC会根据形参名称去检索请求中的数据,并将找到的与形参名称相同的数据赋值给形参,而重命名后,重命名后的value值,会与形参名称进行映射,然后使用重命名后的value值进行检索并将检索后的结果通过映射赋给形参。(注意,如果检索不到的话,形参会被设为对应数据类型的零值,所以在使用引用类型的形参时需要预防空指针异常)
设置参数是否必传
在Spring MVC中我们可以根据需求将参数设置为必传或者非必传,具体还是使用上面提到的@RequestParm,在@RequestParm中有一个required参数,这个参数设为false即为非必传,设为true即为必传,如果我们将某个参数设为必传,但请求中并没有传这个参数,会发生什么呢?我们来看一下。首先我们将s1设为设为必传
然后我们再去掉请求中的数据s,再发送请求
这时响应了一个400错误
由此我们可以知道如果前端请求中没有传方法中必传形参的数据,后端会认为这个请求是不好的,并响应一个400错误。接下来我们将s在加入到请求中,发送请求,这次正常响应了
如果参数为非必传参数,请求中没有传的话,形参则会被设为对应零值。
通过对象接受数据
如果我们请求中的数据过多,一个一个用形参接收会显得十分冗余,所以我们可以考虑用对象来接受来自前端的数据。
首先,我们先定义一个实体类User,要注意的是在创建实体类时我们必须得保证实体类中是有get和set方法、无参构造方法(如果你自定义了一个有参构造方法,自动生成的无参构造方法会消失)的,因为Spring MVC在将请求的数据封装到我们定义实体类中时,是会调用这些方法的,如果大家觉得加上这些方法太麻烦,大家可以引入lombok依赖,并在实体类上加上他的@Data,加上之后会自动为你的实体类生成上面所说的方法。
然后我们再创建一个以刚才创建的实体类 为形参的方法
然后我们再构建一个包含创建User对象所需数据的请求
发送请求后,根据响应结果可以发现,后端已经成功通过对象接收到请求中的数据了。
接收数组
在请求中也是可以传递数组的,在请求中直接在一个key的value中包含多个同类型数值即可,具体如下
后端直接对应数据类型的数组作为形参接收即可
响应结果:
通过集合接收数据
在Spring MVC中我们还可以通过集合类来接收请求中的数据,但在使用集合类时我们需要通过@RequestParam来绑定一下请求中的参数名,后端代码具体如下:
设置请求:
响应结果:
通过响应结果可以发现,后端已经成功通过set集合获取了数据,并且set集合已经对数据进行了去重。
接收文件
在Spring MVC中我们也是可以接收文件数据的,接收文件我们需要使用一个新的注解@RequestPart, 并且通过MultipartFile来接收请求中的文件,注意在这里使用@RequestPart也是需要自己进行参数绑定的
接下来我们通过postMan传一个带文件数据的请求给后端,并通过后端将传来的文件进行一个保存,具体后端代码如下:
@RequestMapping("test06")
public String test06(@RequestPart("file") MultipartFile multipartFile) throws IOException {
if (multipartFile != null){
//获取文件名称
String fileName = multipartFile.getOriginalFilename();
//在本地某个目录下创建一个同名文件
File file = new File("C:/test"+"/" + fileName);
//然后通过MultipartFile的transferTo方法将文件拷贝到我们新建的文件下
multipartFile.transferTo(file);
return fileName;
}
return "传输文件失败";
}
在PostMan中构建带文件的请求,postMan传文件的步骤如下:
在这里我们随便选择一张图片进行传输,图片名称即为上图中的名称,发送请求后响应如下:
,响应返回的文件名称与我们传输的名称一致,打开我们保存图片的目录
传输的图片已经保存了
接收Json
在Spring MVC中,我们也是可以接收json格式的数据的,接收Json我们需要使用注解@RequestBody,并且我们通常用对象来接收Json数据,Spring MVC会自动解析Json的数据,并将其封装到我们的对象形参里。
这里还是使用前面的User类来进行接收
然后我们通过PostMan构建请求,并设置Json格式的数据
发送请求后获得响应
在web开发中,我们通常都是采用这种Json格式来进行前后端的数据交互的,因为Json有如下几个优点
- 易于使用:json的语法格式简单,已于编写和解读
- 跨平台:Json可以被多种不同的平台解析和生成,因此Json可以做到跨平台使用。
- 轻量:Json相当于其他的数据格式如xml等,更加轻量,在进行网络传输占用的带宽更少,因此Json在网路上传输的速度也更快
- 易于扩展:Json支持数组和嵌套等数据格式,使用起来相对灵活,易于使用和扩展
- 安全:Json是一种纯文本格式的数据格式,里面不包含代码,因此也就避免了恶意代码的问题,从而相对具有更高的安全性
接收url中的数据
Spring MVC 支持从url中获取数据
后端要接收url中的数据,只需要通过在路由的后面使用大括号与形参进行绑定并在形参前加上@PathVariable即可具体如下:
构建请求时我们只需要在url中与路由中设置的顺序保持一致进行传值即可,具体如下
响应结果
获取Header中的数据
在Spring MVC 中我们是可以获得请求中的header的数据的。
Sprng MVC 内部对servlet框架进行了封装,所以在我们仍然是可以获取Servlet的相关对象的。
通过Servlet提供的HttpServletRequest对象我们就能够获取请求的header,例如我们要获取Header中的"User-Agent"
然后我们通过PostMan构建请求访问一下
响应:
成功获取到了header中的数据
还有一种获取Header的方法,通过@RequsetHeader,并将其参数设为想要获取的Header名称即可,具体如下:
通过这种方式也能成功获取到Header。
获取Session
在Spring MVC中主要有三种获取session的方式
首先我们同样可以使用HttpServletRequest获取session,具体如下:
@RequestMapping("test11")
public String test11(HttpServletRequest request){
//创建session
HttpSession session = request.getSession(true);
//设置session属性
session.setAttribute("UserName","zhangsan");
//返回设置的属性值,注意的是返回的属性默认为Object,需要强转成String才能返回
return(String) session.getAttribute("UserName");
}
在构建请求进行访问时由于这里的HttpServletRequest是由Servlet提供的,所以不需要请求中传参
直接访问即可
响应结果:
还有一种方法是直接使用Servlet提供的HttpSession,这相当于上面那种方式 自动帮你执行了第一行代码,由于HttpSession也是由Servlet提供的,所以访问时也是不需要进行传参的。
最后一种方法是通过@SessionAttribute,使用时需要指定value参数,值为我们设定的属性名称。
这种方法相当于是直接获取session中的属性值,require代表这个属性值是否必须,效果和前面@RequestParam中的required类似。如果找不到对应的属性的话,后端会返回一个400错误
获取Cookie
在Spring MVC中,我们可以获取请求中的cookie。获取cookie具体有两种方式,
第一种方式还是通过HttpServletRequest,调用其getCookies()方法,他会将请求中的所有cookies
Cookie[]数组的形式返回。
还有一种方式时使用@CookieValue,并设置其参数为cookie的名称。
下面我们来演示一下这两种方式,
首先我们通过PostMan在请求中自定义几个co
okie,自定义cookie的方式如下
然后我们通过前面介绍的两种方式获取cookie
然后看一下控制台的打印结果,cookie被顺利获取并打印了,其中Cookie_7是我自定义的cookie,如果先前没创建过session的话,cookie中是不会有JSESSIONID的
如果我们通过注解获取的cookie在请求中不存在,会返回400错误
响应
@ResponseBody
在Spring MVC中默认的返回数据类型是html,如果返回的不是HTML文件会报错,我们要返回数据的话就得使用@ResponseBody,通过源码可以发现,这个注解和@RequestMapping一样,也是既可以加在方法上,又可以加在类上
加在类上表示类中的所有方法都可以以数据的形式返回响应 ,加在方法上则代表只能当前方法通过数据返回。
在前面的测试请求的代码中,我们并没有加@ResponseBody,但也成功的返回数据了,这是因为我们在类上加了一个@RestController,让我们来看一下它的源码
可以发现@ResponseBody是@RestController的元注解,这也就可以解释为什么前面能正常返回数据了,在源码中,我们可以发现@Controller也是@RestController 的元注解,这表示@RestController也具有@Controller的功能,并且通过Target注解可以知道,@RequestController注解是只能加在类上的。
返回HTML片段
在Spring MVC中我们可以返回一个HTML片段,具体如下
由于这里是HTML 片段,所以我们通过浏览器构建请求,请求发送后可以发现HTML已经渲染到浏览器上了
返回Json
Spring MVC返回对象时大多都是采用Json格式,例如我们返回前面的User对象,Spring MVC会读取User中的属性,并通过这些属性自动生成一个Json字符串,然后将这个Json字符串响应给前端。下面我们定义一个直接返回User对象的代码
然后通过Fiddler来抓取一下这个方法所返回的响应
可以发现响应的数据格式是Json。
设置Content-Type
设置响应的Content-Type需要使用到@RequestMapping中的produces参数。
这里我们来继续访问前面的test05,并将返回类型设置为json类型
根据抓到的响应可以发现,返回的数据格式已经是json了
事实我们通常并不需要去特意修改返回的数据格式,Spring MVC 会自动解析我们返回的数据,并将其转换成合适的格式。例如我们返回的是String,抓到的响应的数据就是text格式的,返回对象抓到的响应的数据就是Json格式的,返回的是HTML文件,抓到的响应就是HTML格式的。
设置状态码
设置状态码我们需要使用Servlet提供的HttpServletResponse,具体如下
通过抓取响应可以发现状态码已经成功设置了
设置自定义header
我们可以在响应的Header中设置一些自定义的信息,具体还是通过HttpServletResponse
在抓到的响应的Header里我们可以找到刚才我们自己定义的信息
三层架构
随着时代的发展,传统的MVC模型已经不在适用当今所流行的前后端分离的项目了,所以大佬们为后端开发进行了一个新的分层,这个分层就是三层架构
三层架构将整个web项目的后端开发重新分为三个层次,表现层,业务逻辑层,数据层
- 表现层:负责页面展示和接收请求,用于和用户进行交互,这层的代码一般统一放到Controller包下
- 业务逻辑层:处理业务的具体逻辑,这层的代码通常放到Service包下
- 数据层:直接操作数据库,用于对数据的管理和存储,代码通常放到Dao包下
三层架构对于用户(浏览器)的请求通常以以下流程执行:
我们在创建web项目时通常需要以三层架构的方式进行分层,使用三层架构的优势其实和MVC差不多,都是对项目进行解耦合,让每一层都只需要关注本层的情况,而无需去关注其他层的实现细节。