文章目录
- 一、什么是 Spring MVC?
- 1.1、MVC定义
- 1.2、MVC 和 Spring MVC 的关系?
- 1.3、为什么要学习 Spring MVC?
- 二、需要掌握Spring MVC的一些功能
- 2.1 Spring MVC 的创建和连接功能实现
- 2.2 Spring MVC 获取参数功能
- 2.2.1、传递单个参数
- 2.2.2、传递对象
- 2.2.3、后端参数重命名(后端参数映射)
- 2.2.4、@RequestBody 接收 JSON 对象[!]
- 2.2.5、使用 URL(baseurl) 获取参数[!]
- 2.2.6、上传文件(譬如图片...)
- 2.2.7、比较特殊的一类参数获取
- 2.3 Spring MVC 输出数据功能
- 2.3.1、后端默认给前端返回的是一个页面
- 2.3.2、类加上注解 @ResponseBody 后端默认返回数据给前端,而不是页面
- 2.3.3、后端返回一个 JSON 对象给前端
- 2.3.4、请求转发或请求重定向
- 2.3.4.1、forward vs redirect
一、什么是 Spring MVC?
1、Spring MVC 也叫做 Spring Web MVC,是基于Servlet 构建的,(因此之前在Servlet中的写法在MVC中同样适用,比如Servlet中的常用对象: Request、Response 对象,在MVC中同样可以使用)。
2、是一个Web框架(即MVC具备HTTP能力:可以支持HTTP协议,当我们从前端/浏览器通过ajax(ajax底层是HTTP)发起一个请求时,此时这个MVC就可以直接去响应;MVC也可以作为一个接口/网站(因为其具备HTTP能力),不管此时的客户端是什么,是小程序、PC应用、安卓应用、IOS应用…,所有类型的应用都可以去调用这个接口,只要该接口是遵循HTTP协议)。
3、来自于Spring的WebMVC模块。
1.1、MVC定义
MVC(model view controller 模型视图控制器)。也是一种设计模式,但是由于出现得较晚,跟门面设计模式一样,都没有列入23种设计模式中。
model:拥有与数据库直接交互的能力,譬如从数据库中读取/存储数据。
view:给用户展示消息/数据。
控制器校验完用户请求后,从数据库查询信息,如果有符合用户请求的数据,数据库就返回查询到的数据给控制器,控制器将数据发送给视图层,视图层返回HTTP响应给用户。
1.2、MVC 和 Spring MVC 的关系?
MVC是一种设计思想,Spring MVC 是思想的具体实现。就跟 Spring中的 ioc 也是一种设计思想,DI是具体实现。
1.3、为什么要学习 Spring MVC?
前面学习了 Spring、SpringBoot,他们都可以通过在浏览器中输入URL时,可以映射到具体的java代码上,直接响应 HTTP 请求了,为什么还要学 Spring MVC 呢??其实正是因为 Spring、SpringBoot 内置了 Spring MVC框架,他们才能够直接响应 HTTP 请求。因此学好了 Spring MVC,才能够用好 SpringBoot 框架里的 web 框架。
二、需要掌握Spring MVC的一些功能
1、连接的功能: 将用户(浏览器)和程序连接起来,也就是访问一个地址能够调用到我们的Spring程序。即用户通过 浏览器访问或者通过ajax、form表单请求的时候,能够直接和项目中的某个方法关联起来;如果项目中不添加 Spring Web 框架,那么通过浏览器/ajax/form表单都访问不到我们项目。
2、获取参数的功能: 用户访问的时候会带一些参数,在程序中要想办法获取到参数,这样的参数有可能是一个、两个、也可能是一个对象、一个数组…
3、输出数据的功能: 执行了业务逻辑之后,要把程序执行的结果返回给用户。
对于SpringMVC来说,掌握了以上3个功能就相当于掌握了SpringMVC。
2.1 Spring MVC 的创建和连接功能实现
Spring MVC 创建是在SpringBoot项目创建时,勾选Spring Web 依赖即可。
Spring MVC 连接一般是通过在类上或方法上使用注解 @RequestMapping 来实现路由映射,也就是浏览器连接程序的作用。
那么 @RequestMapping 是 post 请求还是 get 请求呢?@RequestMapping 是通过浏览器输入URL访问项目,因此是支持 get 请求的,但是@RequestMapping支持post请求吗??使用 工具postMan进行接口测试来测试一下,测试过程如下:
打开postMan:
项目代码:
所以可以知道:
1、当为注解 @RequestMapping(“/xxx”),此时既支持 get 请求 又支持 post 请求。
2、当为注解 @GetMapping(“/xxx”),此时只支持 get 请求。
3、当为注解 @PostMapping(“/xxx”),此时只支持 post 请求。
2.2 Spring MVC 获取参数功能
一般情况下,用户(浏览器)与项目代码连接上了之后,连接请求极有可能带有参数,参数类型有很多,单个参数、多个参数、表单、对象、文件、图片、MP3…那此时应该怎么获取参数。
2.2.1、传递单个参数
要注意:传递参数的key值一定要和代码中的key值对应一致,否则就会获取不到,顺序无所谓,但key值必须一样。
传参数又一注意事项: 在Spring Boot (Spring MVC)中传参一定要传 包装类型,而非 基础类型,传基础类型容易出问题,看以下代码例子:
如果用户在前端忘记传参数了,此时该接口就没办法继续用了,直接报错(程序500)!
将参数类型从基础类型换成包装类,此时即使前端忘记传参数了,页面也不会报错(值为null):
2.2.2、传递对象
如果一个方法中,需要很多参数,那么就可以考虑传一个对象作为方法参数,因为如果太多变量作为方法参数,此时如果参数变了,方法就需要重新跟着进行调整,导致项目的维护成本太高,耦合度也很高。
那此时前端应该如何从程序中获取呢?
1、如果是get请求,这样获取:(前端传递对象,后端获取对象的方式)
SpringBoot就会将我们在前端传递的 key 值,和对象中的属性对应起来,就会给对象执行 set() 方法,从而对象中的属性就会有内容了。
2.2.3、后端参数重命名(后端参数映射)
某些特殊的情况下,前端传递的参数key和我们后端接收的key可能不一致,比如前端传递了一个time给后端,而后端却是由createtime字段来接收的,这样就会出现参数接收不到的情况,如果出现这种情况,我们就可以使用 @RequestParam 来重命名前端的key值。
代码例子:
但是 @RequestParam 有个注意的点,代码中有多少个参数设置了 @RequestParam,那这些参数都必须从前端传过来,否则浏览器直接报错!如果说,当前代码设置的某个参数本来就不需要前端传过来,但是又设置了@RequestParam ,怎么办?
2.2.4、@RequestBody 接收 JSON 对象[!]
接收对象和接收 JSON 对象,是两回事!那什么情况下前端要给程序传递一个JSON对象呢?在第3方系统调用时候。比如学校教务系统需要调用第三方系统智慧树APP获取学生线上选修课选课情况,那此时第三方智慧树APP就要求教务系统传一个JSON对象过来才能访问接口,因为JSON格式是标准格式,
通过postMan模拟前端传递一个JSON对象:
解决办法:使用注解 @RequestBody,即从请求的Body部分获取数据,这样才能获取数据。
2.2.5、使用 URL(baseurl) 获取参数[!]
前端通常在URL后面加上查询字符串传入参数到项目中,但是URL中还有一种不需要使用查询字符串也能传入参数的方法。
代码例子:
2.2.6、上传文件(譬如图片…)
使用注解 @RequestPart
代码例子:
使用工具 postMan 模拟用户(前端)使用接口 getfile上传一张图片,看看用户能否上传图片成功:
postMan 如何上传图片?
上述代码中我的 路径 path 是设置为 D 盘下的 JavaEEProcess 的 img.png,即我希望我此时上传的图片文件能够保存在 img.png中,这个 img.png 在目录 JavaEEProcess 下是没有的,后续会自己生成。
如果说此时想要模拟前端上传一份txt文件,不是图片了,是文件,此时需要将path里的 img.png改成以 .txt 为后缀的路径,此时才能上传成功。因此此处就有个问题出现了,如果说我们的用户从前端,给我们传了不止一张图片/文件,但是此时我们代码中的path是写死了,此时只能接收到一张/份图片/文件,并且,如果用户上传的不是一张以.png为结尾的图片,可能是以.mp3、.mp4、.txt、.jpg…等为结尾的文件,并且这些文件的名字也不叫png呀,但是因为我们代码中写死了,导致此时用户上传文件失败了!此时就出大问题了!
所以此时我们可以使用 UUID为用户这些文件生成不同的文件名,即使用户上传的文件名相同也不会报错文件重复,因为UUID生成的文件名都是唯一不同的,然后通过原生的API getOriginalFilename() 在用户上传文件时获取到用户的文件名,并且通过以.截取字符串,获取用户的文件后缀名,但是如果通过 split() 以 . 为分隔符获取文件名,可能会出错,因为有可能文件名是含有.的,譬如 aa.aa.txt,此时获取文件后缀名就容易出错,可以使用方法 substring() 定位最后一个.,然后获取到文件名的后缀即可,再通过方法 transferto() 将用户上传的文件保存至设定的硬盘位置。
2.2.7、比较特殊的一类参数获取
获取 Cookie/Session/header,这些并不是用户上传的,而是由系统自己生成的。
直接使用 Servlet 内置类获取一个普通参数:
1、获取Cookie
1)、使用 Servlet 内置的类 HttpServletRequest 获取全部的 Cookies(如果此时没有 Cookie ,程序就会报异常,需要先含有 Cookie,可以在浏览器开发者工具自己输入Cookie 来测试接口):
2)、使用注解 @CookieValue 获取到单个 Cookie
但是为什么在 url 中,我们明明没有传递任何参数,为什么就能获取到了对应的 Cookie 的 value 了呢??这是因为浏览器自己实现的机制,即:用户每一次访问网站的时候,浏览器就将该网站下的所有Cookie全部传递到后端。(用户每次访问网站时,浏览器就会将网站下的所有Cookie放到请求的标头的 Cookie 中,一块跟着请求传递给后端,为什么浏览器要实现这个机制呢?这是因为 HTTP 本身是 无状态的,无状态的话如果HTTP想要保存会话标识,那就只能通过 Cookie 了,如果一个程序,用户登陆成功之后,程序就要一直通过Cookie知道当前用户是谁才能继续进行操作,但是如果每一个操作都需要用户去传递cookie,实在太麻烦了,所以当用户访问网站时,就由浏览器一次性传递该网站下所有的cookie给后端就可以了)。
2、获取Header
3、Session的存储和获取 [!]
很重要!所有需要登录功能的系统都需要获取session。
session 在获取之前需要先去存,不去存的话,是无法获取到的。
Session是基于Cookie去实现的,所以当浏览器不同的时候,Session也是不同的,因为 Session 在浏览器中的存储是通过字段 JSESSIONID 来存储的,JSESSIONID就是 SESSIONID,而 Session 又是借助 Cookie去实现的,那为什么会这样呢?其实这也是浏览器的一种优化,就跟浏览器将所有Cookie跟着请求传递给后端是一样的,因为如果不是浏览器传递给后端,那就是用户自己手动传给后端,可这太麻烦了,所以浏览器就作了优化。
Session 是存在服务器的东西,而 Cookie 是存在客户端的,Session 存在服务器,那客户端咋知道你是谁?客户端访问你的时候如何保持会话呢?浏览器将网站中的所有Cookie跟着用户请求传递给后端,此时Session就跟着Cookie传到后端了。
Session是默认存在内存中的,此时就有一个问题,当服务器机器重启或者机子关闭时,内存就会被释放,此时 Session 就会消失不见,那么此时会话就没了,可能客户端还存在这个 SessionId,但是此时服务器已经没有了SessionId,所以服务器压根不认识客户端里的 SessionId。
但是通常情况下我们将IDEA中的程序重启,却发现还是能获取到Session,因此此处的问题就是:项目重启了,Session 没有释放。所以如果想证明Session就是在内存中,不在磁盘中,就需要在任务管理器中将IDEA进程结束,此时Session就会被真正释放,至于为什么在IDEA中重启程序,Session没有被释放,很有可能是Windows对如今高配置电脑做了一些优化。
2.3 Spring MVC 输出数据功能
默认情况下,无论是 Spring MVC 还是 SpringBoot 返回的都是视图(xxx.html),但现在大多数下都是前后端分离的,后端只需要返回给前端数据即可,因此当后端想要返回数据而不是返回一个页面给前端时,类上都需要加上注解 @ResponseBody
2.3.1、后端默认给前端返回的是一个页面
这是一个例子:
2.3.2、类加上注解 @ResponseBody 后端默认返回数据给前端,而不是页面
2.3.3、后端返回一个 JSON 对象给前端
那如何查看后端给前端返回的响应其Body是否是一个JSON对象,可以在 fiddler 中抓包,然后查看响应的 content-type 是否为 json类型 即可。
小例子:
前端使用 ajax 发起请求,后端返回一个 JSON 对象
2.3.4、请求转发或请求重定向
其实 return 不但可以 返回一个页面/视图,返回数据/对象…,还可以进行跳转。跳转的方式有两种:
1、forward (请求转发)
2、redirect(请求重定向)
请求转发和请求重定向的使用对比:
请求转发就好比你想要买国外的东西,你找代购买,即让别人帮忙去买;而请求重定向则是你的代购没空,他让你自己亲自跑到国外去买。 转发是服务器帮转的,而重定向是让浏览器重新请求另一个地址。
2.3.4.1、forward vs redirect
forward和redirect具体区别如下:
1.请求重定向(redirect) 将请求重新定位到资源;请求转发(forward) 服务器端转发。
2.请求重定向地址发生变化,请求转发地址不发生变化。
3.请求重定向与直接访问新地址效果一致,不存在原来的外部资源不能访问;请求转发服务器端转发有可能造成原外部资源不能访问。