目录
[一、Spring Web MVC](#一、Spring Web MVC)
[1.1 Spring Web MVC 的定义](#1.1 Spring Web MVC 的定义)
[1.2 Servlet](#1.2 Servlet)
[1.3 MVC](#1.3 MVC)
[1.3.1 MVC 的定义](#1.3.1 MVC 的定义)
[1.3.2 Spring MVC](#1.3.2 Spring MVC)
[1.3.3 Spring MVC 和 SpringBoot 的关系](#1.3.3 Spring MVC 和 SpringBoot 的关系)
[二、常见的 Spring Web MVC 注解](#二、常见的 Spring Web MVC 注解)
[2.1 @RequestMapping](#2.1 @RequestMapping)
[2.2 @RequestMapping 的请求类型](#2.2 @RequestMapping 的请求类型)
[2.3 @GetMapping](#2.3 @GetMapping)
[2.4 @PostMapping](#2.4 @PostMapping)
[3.1 传递单个参数](#3.1 传递单个参数)
[3.2 传递多个参数](#3.2 传递多个参数)
[3.3 传递对象](#3.3 传递对象)
[3.4 后端参数重命名](#3.4 后端参数重命名)
[3.5 传递数组](#3.5 传递数组)
[3.5.1 方式一](#3.5.1 方式一)
[3.5.1 方式二](#3.5.1 方式二)
[3.6 传递集合](#3.6 传递集合)
[3.7 传递 JSON 数据](#3.7 传递 JSON 数据)
[3.8 获取URL中参数@PathVariable](#3.8 获取URL中参数@PathVariable)
[3.9 上传文件 @RequestPart](#3.9 上传文件 @RequestPart)
[3.10 获取Cookie/Session](#3.10 获取Cookie/Session)
[3.10.1 Cookie 和 Session 的区别](#3.10.1 Cookie 和 Session 的区别)
[3.10.2 传统获取Cookie](#3.10.2 传统获取Cookie)
[3.10.3 简洁获取 Cookie](#3.10.3 简洁获取 Cookie)
[3.10.4 获取Session](#3.10.4 获取Session)
[3.10.4.1 通过 HttpServletRequest 读取 Session](#3.10.4.1 通过 HttpServletRequest 读取 Session)
[3.10.4.2 通过 HttpSession 读取 Session](#3.10.4.2 通过 HttpSession 读取 Session)
[3.10.4.3 通过注解 @SessionAttribute("") 来获取指定 Key 的 Session](#3.10.4.3 通过注解 @SessionAttribute("") 来获取指定 Key 的 Session)
[3.11 获取Header](#3.11 获取Header)
[3.12 如何返回静态页面](#3.12 如何返回静态页面)
[3.13 返回 JSON](#3.13 返回 JSON)
[3.14 设置状态码](#3.14 设置状态码)
[3.15 设置Header](#3.15 设置Header)
[3.15.1 设置Content-Type](#3.15.1 设置Content-Type)
[3.15.2 设置其他Header](#3.15.2 设置其他Header)
[3.16 约定前后端交互接口](#3.16 约定前后端交互接口)
一、Spring Web MVC
1.1 Spring Web MVC 的定义
Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架, 从一开始就包含在 Spring 框架中。它的正式名称 "Spring Web MVC" 来自其源模块的名称(Spring-webmvc), 但它常被称为"Spring MVC"。
从上述定义我们可以得出一个信息, Spring Web MVC 是一个 Web 框架。
1.2 Servlet
Servlet 是一种实现动态页面的技术, 准确的来讲 Servlet 是一套 Java Web 开发的规范, 或者说是一套 Java Web 开发的技术标准。
1.3 MVC
1.3.1 MVC 的定义
MVC 是 Model View Controller 的缩写, 它是软件工程中的一种软件架构设计模式, 它把软件系统分为模型、视图和控制器三个基本部分。

(1) View(视图) 指在应用程序中专门用来与浏览器进行交互, 展示数据的资源。
(2) Model(模型) 是指应用程序的主体部分, 用来处理程序中数据逻辑的部分。
(3) Controller(控制器) 用来链接视图和模型, 决定对于视图发送来的请求, 需要用哪一个模型来处理, 以及处理完之后需要跳回哪一个视图。
1.3.2 Spring MVC
MVC 是一种架构设计模式, Spring MVC 是对 MVC 思想的具体实现, 除此之外, Spring MVC 还是一个 Web 框架。

总结来说, Spring MVC 是一个实现了 MVC 模式的 Web 框架。
1.3.3 Spring MVC 和 SpringBoot 的关系
在我们创建 SpringBoot 项目的时候, 勾选的 Spring Web 框架其实就是 Spring MVC 框架。

SpringBoot 是实现 Spring MVC 的其中一种方式, SpringBoot 可以添加很多依赖, 借助这些依赖来实现不同的功能, SpringBoot 通过添加 Spring Web MVC 框架来实现web功能。
二、常见的 Spring Web MVC 注解
2.1 @RequestMapping
@RequestMapping 既可以修饰类, 也可以修饰方法, 当修饰类和方法的时候, 访问的地址是类路径+方法路径。在 Spring MVC 中使用 @RequestMapping 来实现 URL 路由映射, 作用就是浏览器连接程序。创建一个 UserController 类, 实现用户通过浏览器和程序的交互, 具体代码如下:
后端代码实现:
java
package com.example.demo.demos.web;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/sayHello")
public String sayHello(){
return "sayHello";
}
}

@RequestMapping 是 Spring Web MVC 应用程序中最常被调用的注解之一, 它是用来注册接口的路由映射的。
路由映射: 当用户访问一个 URL 时, 将用户的请求对应到程序中某个类某个方法的过程就叫做路由映射。
2.2 @RequestMapping 的请求类型
对于 @RequestMapping 来说, 即支持 GET 请求, 又支持 POST 请求。我们可以通过代码来指定 @RequestMapping 接受的请求类型, 下方代码就是指定 GET 请求, 我们尝试用 POST 请求访问失败。
后端代码实现:
java
package com.example.demo.demos.web;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping(value = "/sayHello",method = RequestMethod.GET)
public String sayHello(){
return "sayHello";
}
}

2.3 @GetMapping
只支持 GET 请求
后端代码实现:
java
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping(value = "/getMapping")
public String getMapping(){
return "GetMapping";
}
}

2.4 @PostMapping
只支持 POST 请求
后端代码实现:
java
@RestController
@RequestMapping("/user")
public class UserController {
@PostMapping(value = "/postMapping")
public String postMapping(){
return "PostMapping";
}
}

三、传递参数
3.1 传递单个参数
接受单个参数, 在 Spring MVC 中直接用方法中的参数就可以, 比如以下代码。
后端代码实现:
java
package com.example.demo.demos.web;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/m1")
public String method1(String name){
return "name : " + name;
}
}

使用基本类型来接受参数时, 参数必须传(除boolean类型), 否则会报500错误, 类型不匹配时, 会报400错误。
3.2 传递多个参数
和接受单个参数一样, 直接使用方法的参数接收即可, 使用多个形参。
后端代码实现:
java
package com.example.demo.demos.web;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/m2")
public String method2(String name,Integer age){
return "name : " + name + ",age : " + age;
}
}

3.3 传递对象
如果参数比较多的时候, 方法声明就需要又很多形参, 并且后续每次更新一个参数, 也需要修改方法生命, 我们可以吧这些参数封装为一个对象, 当返回的是一个对象的时候, 自动会转为 JSON 格式。Spring 会根据参数名称自动绑定到对象的各个属性上, 如果某个属性没有传递, 则赋值为null(基本类型则为默认初始值)
后端代码实现:
java
package com.example.demo.demos.web;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/m1")
public UserInfo getUser(UserInfo user ){
return user;
}
}

3.4 后端参数重命名
前段传递的参数 key 和我们后端接受的 key 可以不一致, 我们可以使用@RequestParam 来改变前段传输的数据名称。比如前段传输的 key 名称为 "name", 我们可以改为 "createName" 字段来接收, 并且我们可以使用 @RequestParam 中的 required = true 来设置这个参数为必传参数。
后端代码实现:
java
package com.example.demo.demos.web;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/test")
public class TestController {
@RequestMapping("/m1")
public String getMapping2(@RequestParam(value = "name",required = true) String createName){
return "createName: " + createName;
}
}

使用 @RequestParam 进行参数重命名时, 请求参数只能和 @RequestParam 声明的名称一致, 才能进行参数绑定和赋值。
使用 @RequestParam 进行参数重名时, 参数就变成了必传参数。
3.5 传递数组
后端代码实现:
java
package com.example.demo.demos.web;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
@RestController
@RequestMapping("/test")
public class TestController {
@RequestMapping("/m2")
public String method5(String[] arrayParam) {
return Arrays.toString(arrayParam);
}
}
3.5.1 方式一
每次传输的 key 相同, 后端接收到会自动转化成数组。

3.5.1 方式二
只传递一次 key , value 中的值使用 "," 分割, 后端接收到会自动转化为数组。

3.6 传递集合
集合参数: 和数组类似, 同一个请求参数名有多个, 且需要使用 @RequestParam 绑定参数关系
后端代码实现:
java
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
@RestController
@RequestMapping("/test")
public class TestController {
@RequestMapping("/m3")
public String method6(@RequestParam List<String> listParam){
return "size:"+listParam.size() + ",listParam:"+listParam;
}
}

3.7 传递 JSON 数据
JSON(JavaScript Object Notation) 是一种轻量级的数据交互格式, 它基于 ECMAScrip 的一个子集, 采用完全独立于编程语言的文本格式来储存和表示数据。
JSON 语法:
-
数据在 键值对(Key/Value) 中, 键和值用 ":" 分割。
-
数据由逗号 "," 分割
-
对象用 {} 标识
-
数组用 [] 标识
-
值可以为对象, 也可以为数组, 数组中可以包含多个对象。
java
public class JSON {
private static ObjectMapper objectMapper = new ObjectMapper();
public static void main(String[] args) throws JsonProcessingException {
UserInfo person = new UserInfo();
person.setAge(5);
person.setName("zhangsan");
person.setGender("man");
//对象转为JSON字符串
String jsonStr = objectMapper.writeValueAsString(person);
System.out.println("JSON字符串为:"+jsonStr);
//JSON字符串转为对象
UserInfo p = objectMapper.readValue(jsonStr,UserInfo.class);
System.out.println("转换的对象 age:"+p.getAge()+",name:"+p.getName()+",gender:"+p.getGender());
}
}

3.8 获取URL中参数@PathVariable
path variable : 路径变量
后端代码实现:
java
package com.example.demo.demos.web;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/test")
public class TestController {
@RequestMapping("/m4/{id}/{name}")
public String method8(@PathVariable Integer id, @PathVariable("name") String
userName){
return "解析参数id:"+id+",name:"+userName;
}
}

如果方法参数名称和需要绑定的URL中的变量名一致时, 可以简写, 不用给 @PathVariable 的属性赋值, 如上方例子中的 id 变量。
如果方法参数名称和需要绑定的URL中的变量名称不一致时, 需要 @PathVariable 的属性 value 赋值, 如上方例子中的 userName 变量。
3.9 上传文件 @RequestPart
后端代码实现:
java
@RestController
@RequestMapping("/test")
public class TestController {
@RequestMapping("/m6")
public String method9(@RequestPart MultipartFile file) throws IOException {
String fileName = file.getOriginalFilename();
file.transferTo(new File("C:\\Users\\LEGION\\Desktop\\"+fileName));
return "接收到文件名称为: " + fileName;
}
}

3.10 获取Cookie/Session
HTTP 协议自身属于"无状态"协议,默认情况下 HTTP 协议的客户端和服务器之间的这次通信, 和下一次通信之间没有直接的联系。但我们实际开发中, 很多时候是需要知道请求之间的关联关系的。

上述图中的 "令牌" 通常就存储在 Cookie 字段中, 此时服务器这边就需要记录 "令牌" 对应的用户信息, 这个就是 Session 机制做的工作。换句话来说, Cookie 是客户端这边记录信息的工具, Session 是服务器这边记录信息的工具。Session 是服务器为了保存用户信息而创建的一个特殊对象。
Session 本质是一个 "哈希表", 存储一些键值对结构。Key 就是 SessionID , Value 就是用户信息。
当用户登录的时候, 服务器在 Session 中新增一个新纪录, 并且把 sessionId 返回给客户端(通过HTTP 响应中的 Set-Cookie 字段返回)
客户端后续给服务器发送请求的时候, 需要带上 sessionId (通过 HTTP 请求中的 Cookie 字段带上),服务器接收到请求之后, g根据请求中的 sessionId 在 Session 信息中获取相对应的用户信息, 再进行后续操作, 找不到则重新创建 Session , 并返回 SessionID。
Session 默认是保存在内存中的, 如果重启服务器 Session 数据就会丢失

3.10.1 Cookie 和 Session 的区别
-
Cookie 是客户端保存用户信息的一种机制, Session 是服务器端保存用户信息的一种机制。
-
Cookie 和 Session 之间主要是通过 sessionId 关联起来的, sessionId 是 Cookie 和 Session 之间的桥梁。
-
Cookie 和 Session 经常会在一起配合使用, 但是不是必须配合
<1>完全可以用 Cookie 来保存一些数据在客户端, 这些数据不一定是用户身份信息, 也不一定是SessionId
<2>Session 中的 sessionId 也不需要非得通过 Cookie/Set-Coolie 传递, 可以通过URL传递。
3.10.2 传统获取Cookie
通过使用 Servlet 提供的 HttpServletRequest 类来获取 Cookie; 当客户端通过HTTP协议访问服务器的时候, HTTP 请求中的所有信息就封装在 HttpServletRequest 这个类的对象中, 通过这个对象提供的方法, 可以获得客户端请求的所有信息。
java
@RestController
@RequestMapping("/test")
public class TestController {
@RequestMapping("/m7")
public String method10(HttpServletRequest request) throws IOException {
Cookie[] cookies = request.getCookies();
StringBuilder sb = new StringBuilder();
if (cookies!=null){
for (Cookie cookie : cookies) {
sb.append(cookie.getName()).append("=").append(cookie.getValue()).append(";");
}
}
return sb.toString();
}
}

可以看到 Cookie 是可以伪造的, 也就不是安全的, 所以后端需要进行 Cookie 校验, 上方展示的是通过 HttpServletRequest 来获取多个 Cookie 。
3.10.3 简洁获取 Cookie
我们可以通过注解 @CookieValue("") 来单独获取一个指定 Key 的 Cookie
java
@RestController
@RequestMapping("/test")
public class TestController {
@RequestMapping("/m8")
public String method11(@CookieValue("name") Integer name) throws IOException {
return "" + name;
}
}

3.10.4 获取Session
因为 Session 是服务器的机制, 所以我们需要先储存到本地, 才能获取, 下方展示如何储存Session。
java
@RestController
@RequestMapping("/test")
public class TestController {
@RequestMapping("/setSession")
public String method12(HttpServletRequest request) throws IOException {
HttpSession session = request.getSession();
if(session!=null){
session.setAttribute("userName","java");
}
assert session != null;
return "设置成功 session: " + session.getAttribute("userName");
}
}
两种获取 Session 的方式
java
HttpSession getSession(boolean create);
HttpSession getSession();
当不携带参数或者参数为 true 时, 则当不存在会话时新建会话, 参数如果为 false , 则当不存在会话时返回 null。
这个代码中看不见 SessionId 这样的概念, getSession 操作内部提取到请求中 Cookie 里的SessionId , 然后根据 SessionId 获取到对应的 Session 对象, Session 对象使用 HttpSession 来描述。

3.10.4.1 通过 HttpServletRequest 读取 Session
java
@RequestMapping("/getSession")
public String getSession(HttpServletRequest request) {
HttpSession session = request.getSession();
if(session!=null){
return (String) session.getAttribute("userName");
}
return null;
}
Http 响应中, 通过 Set-Cookie 告知客户端, 并把 SessionID 储存在 Cookie 中。
3.10.4.2 通过 HttpSession 读取 Session
java
@RequestMapping("/getSession2")
public String getSession2(HttpSession session) {
if(session!=null){
return (String) session.getAttribute("userName");
}
return null;
}
Session 不存在的话, 会自动进行创建,把 sessionID 通过Set-Cookie 返回给客户端 ,只能拿到Session。
3.10.4.3 通过注解 @SessionAttribute("") 来获取指定 Key 的 Session
java
@RequestMapping("/getSession3")
public String getSession3(@SessionAttribute(value = "userName",required = true) String username) {
return "username:"+username;
}
3.11 获取Header
传统方式获取 Header, 也是从 HttpServletRequest 中获取
java
@RequestMapping("/getHeader")
public String getHeader(HttpServletRequest request) {
String userAgent = request.getHeader("User-Agent");
return "userAgent: " + userAgent;
}
简介方法获取 Header , 通过@RequestHeader 来获取指定的 Header
java
@RequestMapping("/header")
public String header(@RequestHeader("User-Agent") String userAgent) {
return "userAgent:"+userAgent;
}
3.12 如何返回静态页面
html代码如下:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Index⻚⾯</title>
</head>
<body>
Hello,Spring MVC,我是Index⻚⾯.
</body>
</html>
当我们访问http://127.0.0.1:8080/index时会返回

我们需要把类中的 @RestController 改为 @Controller , 正确代码如下:
java
@Controller
public class IndexController {
@RequestMapping("/index")
public Object index(){
return "/index.html";
}
}
再次运行http://127.0.0.1:8080/index 结果如下:

@RestController = @Controller + @ResponseBody
@Controller: 定义一个控制器, Spring 框架启动时加载, 把这个对象交给 Spring 管理。
@ResponseBody: 定义返回的数据格式为非视图, 返回一个 text/html 信息。即是类注解, 又是方法注解, 如果作用在类上, 就表明该类所有的方法都返回的是数据, 如果作用在方法上, 表示该方法返回的是数据。
如果想返回视图的话, 只需要把 @ResponseBody 去掉就可以了, 也就是留 @Controller。
3.13 返回 JSON
返回 HashMap 类的会自动转成 JSON 格式
java
@RequestMapping("/returnJson")
@ResponseBody
public HashMap<String, String> returnJson() {
HashMap<String, String> map = new HashMap<>();
map.put("Java", "Java Value");
map.put("MySQL", "MySQL Value");
map.put("Redis", "Redis Value");
return map;
}
当返回类对象的时或者用 @RequestBody 修饰之后都会返回 JSON 格式
3.14 设置状态码
Spring MVC 会根据我们方法的返回值自动设置想听状态码, 程序员也可以手动指定状态码。通过Spring MVC 的内置对象 HttpServletResponse 提供的方法来进行设置。
java
@RequestMapping(value = "/setStatus")
public String setStatus(HttpServletResponse response) {
response.setStatus(401);
return "设置状态码成功";
}

3.15 设置Header
Http 响应报头也会向客户端传递一些附加信息, 如 Content-Type, Local 等
value: 制定映射的URL
method: 制定请求的 method 类型, 如GET,POST等
consumes: 指定处理请求(request)的提交内容类型(Content-Type), 如application/jsond等
produces: 指定返回的内容类型, 还可以同时设置返回值的字符编码。
Params: 指定request中必须包含某些参数值时, 才让该方法处理。
headers: 指定request中必须包含某些指定的header值, 才能让该方法处理请求。
3.15.1 设置Content-Type
通过设置 produces 属性的值, 设置响应的报头 Content-Type
java
@RequestMapping(value = "/returnJson2",produces = "application/json")
public String returnJson2() {
return "{\"success\":true}";
}


如果不设置 produces, 方法返回结果为 String 时, Spring MVC 默认返回类型是 text/html
3.15.2 设置其他Header
设置其他 Header 需要使用 Spring MVC 的内置对象 HttpServletResponse 提供的方法来设置
java
@RequestMapping(value = "/setHeader")
public String setHeader(HttpServletResponse response) {
response.setHeader("MyHeader","MyHeaderValue");
return "设置Header成功";
}

3.16 约定前后端交互接口
约定"前后端交互接口"是进行 Web 开发中的关键环节, 接口又叫 API , 我们一般降到接口或者 API, 指的都是同一个东西。API 是指应用程序对外提供的服务的描述, 用于交换信息和执行任务, 就是允许客户端给服务发送那些 HTTP 请求, 并且每种请求预期获取什么样的 HTTP 响应, 简单来说就是规定格式。
四、应用分层
应用分层的作用
如果没有应用分层, 会出现逻辑不清、各模块相互依赖、代码拓展性差、改动一处就需要大刀阔斧等问题。
MVC
MVC 就是把整体分成了 Model , View , Controller 三个层次, 也就是将用户视图和业务处理隔离开, 并且通过控制器连接起来, 很好的实现了表现和逻辑的解耦合, 是一种标准的软件分层架构。

三层架构

对于目前主流的开发方式, 后端开发不再需要关注前端的实现, 所以又有了一种新的分层架构: 把整体分为表现层、业务逻辑层和数据层。
1、Controller: 控制层, 展示数据结果和接受用户指令,接受前端发送的请求, 对请求进行处理, 并响应数据;
2、Service: 负责处理业务逻辑, 里面有复杂业务的具体实现;
3、Dao: 数据访问层, 也被称为持久层, 负责数据访问操作, 包括数据的增删改查;

MVC和三层架构是两种东西, MVC 强调数据和视图分离, 将数据展示和数据处理分开, 通过控制器对两者进行组合。三层架构强调不同维度数据处理的高内聚和低耦合, 将交互页面, 业务处理和数据库操作的逻辑分开。