本篇文章,会带大家学习常见的一些Spring Web MVC注解,通过SpringMVC完成一些基础的功能,并且了解一些企业中的命名规范.
一.了解Spring Web MVC
官方对于Spring Web MVC的描述:

翻译成中文就是:Spring Web MVC是基于Servlet API构建的web框架,包含在SpirngFramework中.命名为 Spring Web MVC,通常也叫做SpringMVC
什么是Servlet API?
简单来说,Servlet 是一套由 Java 提供的规范(接口),用于扩展 Web 服务器的功能,处理客户端(通常是浏览器)发送的 HTTP 请求,并返回响应。
在后面马上就会用到有关servlet API接口,到时候会有更深的理解.
1.MVC的定义:
MVC是ModelViewController的缩写,它是软件工程中的⼀种软件架构设计模式,它把软件系统分
为模型、视图和控制器三个基本部分.

Spring MVC就是对MVC思想的一种具体实现,此外,SpringMVC还是一个Web框架.
所以,SpringMVC关注点有两个:MVC和Web框架.
其实我们在之前创建SpringBoot项目的时候,勾选的SpringWeb框架,就是SpirngMVC框架.
那么SpringBoot和SpringMVC之间有什么关系呢?
SpringBoot可以添加很多依赖,借助这些依赖实现不同的功能,SpringBoot通过添加SpringMVC框架,来实现web功能.
二.学习Spring MVC
既然是web框架,那么当用户在输入URL之后,我们的SpringMVC项目就可以感知到用户的请求,并给予响应.
学习SpirngMVC,主要就是学习如何通过浏览器和用户交互.
主要分为三个方面:
(1).建立连接:将用户的浏览器和Java程序连接起来,也就是访问一个地址就可以调用我们的Spring程序,
(2).请求:用户请求的时候会携带一些参数,在程序中需要获取这些参数,请求这块主要是获取参数的功能.
(3).响应:执行业务逻辑之后,要把执行的结果返回给用户,也就是响应.
对于SpirngMVC来说,学习完以上三个功能,就相当于掌握了SpringMVC
1.项目准备:
在上篇文章中已经提及,在创建SpirngBoot项目的时候,选择SpringWeb就相当于是创建了SpirngWeb项目.

2.建立连接
在SpringMVC中,使用**@RequestMapping**来映射URL路径,也就是浏览器连接程序的作用.
创建一个UserController类,实现用户通过浏览器和程序的交互,代码如下:
java
@RestController
public class UserController {
@RequestMapping("/hello")
public String hello(){
return "hello world";
}
}
接下来,用户只需要在服务器上访问URL,就可以通过服务器访问到我们的java代码

3.@RequestMapping注解介绍
@RequestMapping作为SpringMVC应用程序中最常被用到的注解之一,他是用来注册接口的路由映射的.
表示服务器接收到请求的时候,路径为/hello的请求就会调用到hello()这个方法.
路由映射:当用户访问一个URL的时候,将用户的请求对应到程序的某个类的某个方法的过程叫做路由映射.
那么我们注意到,在@RequestMapping 这个注解前面还有一个@RestController注解.这个注解是什么作用呢?
我们试着把这个注解去掉,看看有什么区别.
去掉@RestController注解,重新启动服务器代码,通过浏览器访问,会出现404.表示找不到该页面.

这就是@RestController注解的作用.
在一个项目中会有很多的类,也会有很多的方法,Spring程序怎么会知道要执行那个方法呢?
这就需要用到@RestController注解了,如果一个类添加了@RestController注解,Spring才会去看这个类里面的方法有没有添加@RequestMapping这个注解.
当然这只是@RestController注解的其中一个作用,剩下的作用会在后面讲解.
(1)@RequestMapping注解的使用
@RequestMapping注解既可以修饰方法,也可以修饰类.当同时修饰方法和类的时候,访问的地址就是类路径+方法路径.
举个例子:在类前面也加上@RequestMapping注解.

那么这个方法的访问路径就是:/request/hello
此时我们访问/hello就会访问不到

要访问http://127.0.0.1:8080/request/hello

通过@RequestMapping注解,帮助我们实现了网络通信,简化了http协议的使用.
在之前,我们学习http相关知识的时候,直到http是由不同的请求方式的,GET请求POST请求等等.
那么我们使用@RequestMapping使用的是哪种类型呢?
在学习http相关知识的时候,我们学习了有关Fiddler抓包工具的使用,我们可以用抓包工具看看.

通过抓包我们可以知道是支持GET请求的.那么我们怎么知道是否支持POST请求呢?
一种方式,我们可以通过form表单来构造一个POST请求,但是这个需要前端的一些知识.
另一种方式,我们可以使用Postman这个工具.
(2)Postman的使用:

可以看到在Postman上面有这么一行.表示的是传参的类型.
1.Params

我们知道在url后面可以跟查询字符串(在http章节讲过).
在Params这下面添加key,value就会自动拼接到URL后面,我们可以通过前面的√选择是否需要这个查询字符串.
例子:
2.Header
在学习HTTP协议的时候,我们知道发送请求里面有哦Header请求头.里面包含了各种信息.
postman作为一个接口测试工具,可以去由程序员自己编写header里面的内容.

3.body
也就是http协议中的body,
none表示没有body.
form-date表示表单提交的数据,什么是表单提交的数据?类似于登入页面,登入的时候需要你输入账号密码等信息,然后通过点击登入按钮,发送这些数据给服务器,这种数据就可以使用form-date来提交,同时还能上传文件.
x-www-form-urlencoded表示键值对经过URL编码,只能上传文本数据,不能上传文件.
raw表示可以上传任意格式的文本,可以上传text,json,xml,html等等.

了解这些之后我们既可以使用postman来发送不同的请求了.
打开Postman,可以创建一个请求,然后输入URL.这里可以选择各种请求类型
选择POST类型,然后输入正确的URL,点击send,就可以和浏览器一样看到效果了.
可以看到这里我们选择POST类型,然后依旧可以正常访问
再试试别的请求类型,结果还是一样,说明@RequestMapping注解可以支持所有类型的请求.
4.如何在请求中传递参数
访问不同的路径,就是发送不同的请求,在发送请求的时候,可能会带有一些参数,所以学习Spring的请求,就是在学习如何传递参数到后端以及后端如何接收.
1.传递单个参数
接收单个参数,我们只需要在Spring MVC中直接用方法中的参数就可以了.我们只需要保持方法中的参数名和URL中查询字符串中的参数名一致即可.
代码:
java
@RequestMapping("/getname")
public String getname(String name){
return "name:" + name;
}

如果我们将name改为name1,这样就会接收不到参数

这里要注意一个点,使用基本类型进行传参的时候,参数必须传(boolean类型除外),否则会报500错误,类型不匹配会报400错误.这是因为基本类型无法接收null值.
java
@RequestMapping("/getage")
public String getage(int age){
return "接收到age:" + age;
}
使用Fidedler抓包,可以发现正常情况下状态码为200,Content-Type类型为text/html.

此时我们不传递age,再试一次.

可以看到此时状态码为500.我们查看服务器代码日志.这里告诉我们age不能为null,可以考虑将其声明为包装类,这时候我们将int改为integer就行


将int改为Interger之后,重新运行代码,发现接收到null值

当我们传递参数不匹配的时候,也会报400错误,
对于包装类来说,如果不传递对应的参数,则会接收到null,所以在企业开发中,对于参数可能为空的数据,建议使用包装类. 
2.传递多个参数
传递多个参数的方法和传递单个参数一样,只需要用多个参数接收即可.
java
@RequestMapping("/getuser")
public String getuser(String name,Integer age){
return "接收到用户,name:" + name + " age:" + age;
}

当传递多个参数的时候,前后端进行参数匹配时,是以参数的名称进行匹配的,因此参数的我盒子不影响后端获取参数的结果.
3.传递对象
创建一个Person对象,传递这个对象.

java
@RequestMapping("/getperson")
public Person getperson(Person person){
return person;
}
和传递多个参数一样,我们只需要将Person的各个属性正确的填进去,就可以传递过去.
Spring会根据参数名称自动绑定到对象的各个属性上,如果某个属性没有传递,则复制为null.基本类型会赋值为默认初始值,比如int默认为0

4.后端参数重命名(后端参数映射)
某些情况下,前端传递的参数名和后端接收用的参数名称不一致,这时候就会出现接收不到的情况,出现这种情况,我们可以使用@RequestParam这个注解来重命名前后端的参数值
比如,现在前端发送过来的数据名叫name,后端用username接收,正常情况是接收不到的.
java
@RequestMapping("/getUsername")
public String getUsername(String username){
return "username:" + username;
}

我们添加@RequestParam注解,括号内填入前端传递的参数名,就可以接收到了.
java
@RequestMapping("/getUsername")
public String getUsername(@RequestParam("name") String username){
return "username:" + username;
}
此时还是一样的URL,我们就可以接收到传递的数据了

此时如果我们在添加@RequestParam注解之后,使用的是原来的参数名username,就会报错

查看后台日志,告诉我们请求所需要的参数"name"不存在,可以得出结论
在我们使用@RequestParam注解重命名之后,请求的参数只能和@RequestParam括号内声明的名称一致,才能进行参数的绑定和赋值.
使用@RequestParam注解进行参数重命名时,参数就变成了必传参数.
通过查看@RequestParam的具体实现,我们可以发现@RequestParam注解有四个属性.分别是value,name,required,defaultValue

通过代码注解可以知道,required表示这个参数是否是必传参数,我们可以更改值来让他变成非必传参数.
当我们将上面的注释内容进行更改,然后使用一样的URL传递参数,可以发现这时候不会报错,而是会接收到null值.
也就是说,我们添加required = false之后,依然要用@RequestParam注解括号内的value或者name值进行参数传递,只是这个时候如果不传递参数,不会报错.只会接收到null值
java
@RequestMapping("/getUsername")
public String getUsername(@RequestParam(value = "name",required = false) String username){
return "username:" + username;
}

5.传递数组
SpringMVC可以自动绑定数组参数的赋值,
后端实现代码:
java
@RequestMapping("/getarr")
public String getarr(String[] arr){
return Arrays.toString(arr);
}
前端传递:
有多种URL传递数组的方式,一种就是下面这种
还可以:
还可以下面这样,%2c就是代表逗号:

URL编码对应表:

6.传递集合:
传递集合和传递数组类似,同一个请求的参数有多个**,但是与数组不同的是,传递集合需要使用@RequestParam绑定参数关系.**
默认情况下,请求中参数名相同的多个值,是封装到数组,如果要封装到集合,需要@RequestParam绑定参数关系.

7.传递JSON数据:
传递JSON数据,需要使用@RequestBody注解
java
@RequestMapping("/getPerson")
public String getPerson(@RequestBody Person person){
return "person:" + person;
}

使用Fiddler抓包看看,可以看到这边请求是application/json.

当我们去掉@Requestbody注解,看看效果.后端未收到结果.
8.获取URL中的参数@PathVariable
PathVariable:路径变量,这个注解的作用就是在请求URL路径上的数据绑定.
通过URL中{}里面的变量名获取值,PathVariable括号中的值要跟URL中的名称保持一致,后面的UserId则是后端使用的变量名,可以一致,也可以不一致.
java
@RequestMapping("/getPathVariable/{id}/{name}")
public String getPathVariable(@PathVariable("id") Integer UserId, @PathVariable("name") String UserName){
return "id:" + UserId + " name:" + UserName;
}

9.上传文件@RequestPart
java
public String getFile(@RequestParam("file") MultipartFile file) throws IOException {
//获取到文件名称
String fileName = file.getOriginalFilename();
//保存文件到新的位置
file.transferTo(new File("D:/" + fileName));
return "接收到file" + fileName;
}

获取到原来的文件名,上传到新的位置.使用Postman发送请求.

可以看到在我们预想的路径下出现了该文件.

10.获取Cookie和Session
在我们学习HTTP协议的时候,我们知道HTTP协议自身是属于"无状态"协议.
也就是说默认情况下的HTTP通信这次通信和下次通信之间是没有直接的联系.
但是在实际开发中,我们是需要知道请求之间的关联关系的.
所以这时候就需要Cookie和Session了.
什么是Cookie?
简单来说,Cookie是服务器让浏览器在客户端保存的一小段文本数据.用来作为身份标识.
比如,在我们登入一个网站的时候,第一次登入浏览器会给一个Cookie值,就比如Set-Cookie: user_id=abc123,然后这个Cookie值就会存在浏览器这边,下次这个再次访问这个网站,浏览器就会带着这个Cookie值去访问服务器,服务器就知道是哪个用户了.

就好比上面这个流程,Cookie就相当于是令牌.
Session是什么呢?
Session是服务器为每一个客户端创建的一个服务端数据存储,用来记住用户的状态.它的本质是一个哈希表,存储了一些键值对,Key就是SessionID,Value就是用户信息.
用户登入成功,服务器会生成一个唯一的SessionID,服务器会把用户的信息保存在自己的数据库中,然后服务器把SessionID通过Cookie发送给浏览器,这个Cookie的key一般叫JSESSIONID 或者叫PHPSESSID.浏览器后续请求都会带上这个Cookie,然后服务器收到请求会去数据库找对应用户的数据.

Cookie和Session的区别:
1.Cookie 是客户端保存用户信息的一种机制. Session 是服务器端保存用户信息的一种机制.
2.Cookie 和 Session 之间主要是通过 SessionId 关联起来的,SessionId 是 Cookie 和 Session 之间的桥梁.
3.Cookie 和 Session 经常会在一起配合使用。但是不是必须配合.完全可以用 Cookie 来保存一些数据在客户端。这些数据不一定是用户身份信息,也不一定是 SessionId.Session 中的 sessionId 也不需要非得通过 Cookie/Set-Cookie 传递,比如通过 URL 传递
1.获取Cookie
我们知道SpringMVC是基于servletAPI构建的web框架,HttpServletRequest,HttpServletResponse是Servlet提供的两个类,可以拿到Http里面的各种数据.
java
@RequestMapping("/getCookie")
public String getCookie(HttpServletRequest request, HttpServletResponse response){
//获取到请求中的Cookie信息.
Cookie[] cookies = request.getCookies();
//通过循环打印Cookie信息
StringBuffer sb = new StringBuffer();
if(cookies != null){
for (Cookie cookie : cookies) {
sb.append(cookie.getName() + ":" + cookie.getValue() + " ");
}
}
return "cookie:" + sb.toString();
}
运行程序,我们会发现啥也没有,这是因为这里面的Cookie是空的,这时候我们可以去添加几对cookie


重新运行程序,就可以拿到这里面的cookie值.
还有更加简单的获取cookie的方法:
通过@CookieValue注解的方式获取,在括号内加入Cookie的key值.如果不存在默认情况下会抛出异常.可以通过制定required = false来避免这个异常.
java
@RequestMapping("/getCookie2")
public String getCookie2(@CookieValue(value = "cookie1") String cookie1){
return "cookie:" + cookie1;
}

当我们删掉这个Cookie,就会报400.

改动代码,让required = false
java
@RequestMapping("/getCookie2")
public String getCookie2(@CookieValue(value = "cookie1",required = false) String cookie1){
return "cookie:" + cookie1;
}
就会显示null

2.存储和获取Session
Session是服务器端的机制,我们需要先存储,才能获取.
设置Session:
java
@RequestMapping("setSession")
public String setSession(HttpServletRequest request, HttpServletResponse response){
//先获取到Session
HttpSession session = request.getSession();
session.setAttribute("session1","session1");
return "session:" + session.getAttribute("session1");
}

获取session:
java
@RequestMapping("getSession")
public String getSession(HttpServletRequest request, HttpServletResponse response){
HttpSession session = request.getSession();
return "获取到session1:" + session.getAttribute("session1");
}

通过@SessionAttribute注解获取Session
java
@RequestMapping("getSession2")
public String getSession2(@SessionAttribute(value = "session1",required = false) String session1){
return "获取到session1:" + session1;
}

通过SpringMVC内置对象HttpSession来获取
java
@RequestMapping("getSession3")
public String getSession3(HttpSession session){
String key = "session1";
return "获取到session1:" + session.getAttribute(key);
}

11.获取Header
传统获取Header:
java
@RequestMapping("getHeader")
public String getHeader(HttpServletRequest request, HttpServletResponse response){
String value = request.getHeader("User-Agent");
return "获取到User-Agent:" + value;
}

通过Fiddler抓包对比,观察结果是否正确

通过@RequestHeader注解简洁获取Header,括号内的参数值为请求报头中的key.
java
@RequestMapping("getHeader2")
public String getHeader2(@RequestHeader(value = "User-Agent") String userAgent){
return "获取到User-Agent:" + userAgent;
}

