Spring Web MVC
Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架,从一开始就包含在 Spring 框,Spring
Web MVC 是一个 Web 框架.下面咱们简称之为: Spring MVC
MVC 定义
MVC 是 Model View Controller 的缩写,它是软件工程中的一种软件架构设计模式,它把软件系统分 为模型、视图和控制器三个基本部分.

|-----------------|-------------------------------------------------------------------|
| View(视图) | 指在应用程序中专门用来与浏览器进行交互,展示数据的资源. |
| Model(模型) | 是应用程序的主体部分,用来处理程序中数据逻辑的部分. |
| Controller(控制器) | 可以理解为一个分发器,用来决定对于视图发来的请求,需要用哪一个模型 来处理,以及处理完后需要跳回到哪一个视图。即用来连接视图和模型 |
了解Spring MVC
Spring MVC 是 Spring 框架中的 Web 模块,是一个基于 MVC 设计模式的轻量级 Web 框架。
简化理解,它主要做三件事:
- 核心思想(MVC):把代码拆分成三层
- Controller(控制器):接收请求,协调业务。
- Model(模型):处理业务逻辑,封装数据。
- View(视图):展示数据(如网页、JSON)。
- 核心机制:通过一个叫 DispatcherServlet(前端控制器) 的核心组件统一接收所有请求,然后自动分发(路由)给对应的 Controller 去处理。
- 核心作用:解耦(让代码各司其职)、简化开发(通过注解即可轻松写接口),主要用于构建 Web 应用和 RESTful API。
关于Spring Boot项目的补充
我们创建的Spring Boot项目时,勾选的Spring Web选项就是运用到了,Spring Boot是实现Spring
MVC的其中一种方式。
Spring Boot 可以添加很多依赖,借助这些依赖实现不同的功能。Spring Boot 通过添加Spring
Web MVC框架来实现web功能.

学习Spring MVC
学习Spring MVC就是学习如何通过浏览器与用户程序进行交互,可以通过以下三个方面来学习:
- 建立连接:将用户(浏览器)和Java程序连接起来,也就是访问一个地址能够调用到我们的Spring程序
- 请求:用户请求的时候会带一些参数,在程序中要想办法获取到这些参数,所以请求这块主要是获取参数的功能
- 响应:执行业务逻辑之后要把程序执行的结果返回给用户,也就是响应
项目准备
关于项目的创建,上一篇已经讲解如何进行项目创建,不清楚的可以去看下上一篇blog
建立连接
在 Spring MVC 中使用 @RequestMapping 来实现 URL 路由映射
创建一个UserController类,实现用户通过浏览器和程序的交互,具体实现代码如下:
java
package com.chen.spring.demo;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/user")
@RestController
public class UserController {
@RequestMapping("/m1")
public String m1(){
return "hello world";
}
}
接下来访问: http://127.0.0.1:8080/m1, 就可以看到程序返回的数据了

@RequestMapping
@RequestMapping 注解介绍
@RequestMapping是Spring Web MVC应用程序中最常被用到的注解之一,它是用来注册接口的 路由映射的
路由映射: 当用户访问一个 URL 时, 将用户的请求对应到程序中某个类的某个方法的过程就叫路由映射.
@RequestMapping 使用
@RequestMapping 既可修饰类也可以修饰方法 ,当修饰类和方法时,访问的地址是类路径 + 方 法路径.
|-----------------------|------------------|
| @RequestMapping标识一个类 | 设置映射请求的请求路径的初始信息 |
| @RequestMapping标识一个方法 | 设置映射请求请求路径的具体信息 |
注意: @RequestMapping 的URL 路径最前面加不加 / (斜杠)都可以, Spring程序启动时, 会进行判断,如果前面没有加 / , Spring会拼接上一个 /
请求
访问不同的路径,就是发送不同的请求. 在发送请求时可能会带一些参数, 所以学习Spring的请求, 主要是学习如何传递参数到后端以及后端如何接收
传递参数, 咱们主要是使用浏览器和Apifox来模拟
传递单个参数
接收单个参数, 在 Spring MVC 中直接用方法中的参数就可以,比如以下代码:
java
@RequestMapping("/request")
@RestController
public class RequestController {
@RequestMapping("/r1")
public String r1(String keyword) {
return "接收参数" + keyword;
}
}

状态码显示200,表示返回请求成功
注意事项
使用基本类型来接收参数时, 参数必须传(除boolean类型), 否则会报500错误
类型不匹配时, 会报400错误
对于包装类型,如果不传对应参数,Spring 接收到的数据则为null,所以企业开发中,对于参数可能为空的数据,建议使用包装类型
传递多个参数
如何接收多个参数呢?
和接收单个参数一样, 直接使用方法的参数接收即可. 使用多个形参.
java
@RequestMapping("/r2")
public String r2(String userName, String password){
return "接收参数:userName"+userName+", password"+password;
}

当有多个参数时,前后端进行参数匹配时,是以参数的名称进行匹配的,因此参数的位置是不影响
后端获取参数的结果.
传递对象
如果参数比较多时,方法声明就需要有很多形参. 并且后续每次新增一个参数, 也需要修改方法声
明. 我们不妨把这些参数封装为一个对象.
java
public class UserInfo {
private String name;
private int gender;
private int age;
public UserInfo(){
}
public UserInfo(String name,int gender,int age){
this.name=name;
this.gender=gender;
this.age=age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getGender() {
return gender;
}
public void setGender(int gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "UserInfo{" +
"name='" + name + '\'' +
", gender=" + gender +
", age=" + age +
'}';
}
}
java
@RequestMapping("r5")
public String r5(UserInfo userInfo){
return "接收参数:userInfo"+userInfo.toString();
}

Spring 会根据参数名称自动绑定到对象的各个属性上, 如果某个属性未传递, 则赋值为null(基本类
型则 赋值为默认初识值, 比如int类型的属性, 会被赋值为0)
后端参数重命名
后端参数重命名也叫做后端参数映射。某些特殊的情况下前端传递的参数和我们后端接收的可以不
一致,比如前端传递了一个A给后端,而后端是使用B字段来接收的,这样就会出现参数接收不到
的情况,如果出现这种情况,我们就可以使用 @RequestParam 来重命名前后端的参数值.
java
@RequestMapping("/r6")
public String r6(@RequestParam(value = "q") String keyword){
return "接收参数:keyword ="+keyword;
}

此时, 如果浏览器使用keyword进行参数传递呢?

显然访问结果时null,表示无法找到
可以得出结论
- 使用 @RequestParam 进行参数重命名时, 请求参数只能和 @RequestParam 声明的名称一 致, 才能进行参数绑定和赋值.
- 使用 @RequestParam 进行参数重命名时, 参数就变成了必传参数.
非必传参数设置
如果存在一个非必传参数,我们可以通过设置 @RequestParam 中的 required=false 来避免不传
递时报错
java
@RequestMapping("/r6")
public String r6(@RequestParam(value = "q",required = false) String keyword){
return "接收参数:keyword ="+keyword;
}

可以看到添加required=false之后,time前面也加了key,变成了 value = "time" 注解属性赋值时, 没
有指明key的话, 默认为value属性. 如果需要有多个属性进行赋值时, 需要写上key
传递数组
Spring MVC 可以自动绑定数组参数的赋值
java
@RequestMapping("/r7")
public String r7(String[] arr){
return "接收参数:arr="+ Arrays.toString(arr);
}

传递集合
集合参数:和数组类似, 同一个请求参数名有为多个, 且需要使用 @RequestParam 绑定参数关系
java
@RequestMapping("/r8")
public String r8(@RequestParam List<Integer> list){
return "接收参数:list"+list;
}

传递JSON数据
JSON是一种数据格式, 有自己的格式和语法, 使用文本表示一个对象或数组的信息, JSON本质是字
符串. 主要负责在不同的语言中数据传递和交换
JSON与Javascript的关系
二者没有关系, 只是语法相似, js开发者能更快的上手而已, 但是他的语法本身比较简单, 所以也很好
学
JSON语法
JSON 是一个字符串,其格式非常类似于 JavaScript 对象字面量的格式
- 数据在键值对(Key/Value) 中
- 数据由逗号 , 分隔
- 对象用 {} 表示
- 数组用 \[\] 表示
- 值可以为对象, 也可以为数组, 数组中可以包含多个对象
JSON的两种结构
对象: 大括号 {} 保存的对象是一个无序的 键值对 集合. 一个对象以左括号 { 开始, 右括号 } 结束。每个"键"后跟一个冒号 : ,键值对使用逗号 , 分隔
数组: 中括号 \[\] 保存的数组是值(value)的有序集合. 一个数组以左中括号 开始, 右中括 号 结束,值之间使用逗号 , 分隔。

JSON字符串和Java对象互转
Spring MVC框架也集成了JSON的转换工具用来来完成JSON字符串和Java对象的互转
java
public class Person {
private String Name;
private int Id;
private String Password;
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public int getId() {
return Id;
}
public void setId(int Id) {
this.Id = Id;
}
public String getPassword() {
return Password;
}
public void setPassword(String password) {
Password = password;
}
}
java
public class Json {
private static ObjectMapper objectMapper = new ObjectMapper();
public static void main(String[] args) throws JsonProcessingException {
Person person = new Person();
person.setId(5);
person.setName("zhangsan");
person.setPassword("123456");
//对象转为JSON字符串
String jsonStr = objectMapper.writeValueAsString(person);
System.out.println("JSON字符串为:"+jsonStr);
//JSON字符串转为对象
Person p = objectMapper.readValue(jsonStr,Person.class);
System.out.println("转换的对象 id:"+p.getId()+",name:"+p.getName()+",password:"+p.getPassword());
}
}

使用ObjectMapper 对象提供的两个方法, 可以完成对象和JSON字符串的互转
|--------------------|--------------|
| writeValueAsString | 把对象转为JSON字符串 |
| readValue | 把字符串转为对象 |
JSON优点
- 简单易用:语法简单,读写快
- 跨平台:多语言通用,互通好
- 轻量级:体积小,传输快
- 易扩展:结构灵活,支持嵌套
- 安全性:纯文本,无可执行代码
传递JSON对象
接收JSON对象, 需要使用 @RequestBody 注解
@RequestBody
RequestBody: 请求正文,意思是这个注解作用在请求正文的数据绑定,请求参数必须在写在请求正 文中
后端实现
java
@RequestMapping("/r9")
public String r9(@RequestBody UserInfo userInfo){
return userInfo.toString();
}

通过观察响应报头,返回的响应格式也为json格式

当去除掉 @RequestBody

后端未能成功给Person对象赋值
@PathVariable
@PathVariable用来获取URL中参数
path variable: 路径变量。和字面表达的意思一样, 这个注解主要作用在请求URL路径上的数据绑定,以获取一篇文章编号为例:
当我们阅览文章时,会发现每一篇文章都有一段对应的编号

我们通过代码来实现对编号的获取(这里的文章编号是我们自己模拟的)
java
@RequestMapping("/article/{articleId}")
public String r10(@PathVariable Integer articleId){
return "获取文章ID:"+articleId;
}

注意:
java
@RequestMapping("/article/{TYPE}/{articleId}")
public String r11(@PathVariable Integer articleId,@PathVariable("TYPE") String type){
return "获取文章ID:"+articleId+", type:"+type;
}

如果方法参数名称和需要绑定的URL中的变量名称一致时, 可以简写, 不用给@PathVariable的属性赋值, 如上述例子中的article变量
如果方法参数名称和需要绑定的URL中的变量名称不一致时, 需要@PathVariable的属性value赋值, 如上述例子中的type变量
@RequestPart
@RequestPart用来上传指定名称的文件
java
@RequestMapping("/r12")
public String r12(@RequestPart("file11") MultipartFile file) throws IOException {
System.out.println(file.getOriginalFilename());
file.transferTo(new File("D:\\code\\"+file.getOriginalFilename()));
return "文件获取成功";
}


获取Cookie/Session
Cookie
HTTP 协议自身是属于 "无状态" 协议.
无状态" 的含义指的是: 默认情况下 HTTP 协议的客户端和服务器之间的这次通信, 和下次通信之间没有直接的联系
但是实际开发中, 我们很多时候是需要知道请求之间的关联关系的。此时在服务器这边就需要记录客户端信息,这个就是 Session 机制所做的工作
Session
Session是服务器为了保存用户信息而创建的一个特殊的对象

Session的本质就是一个 "哈希表", 存储了一些键值对结构

关于登录
- 当用户登陆的时候, 服务器在 Session 中新增一个新记录, 并把 sessionId返回给客户端. (通过 HTTP 响应中的 Set-Cookie 字段返回).
- 客户端后续再给服务器发送请求的时候, 需要在请求中带上 sessionId. (通过 HTTP 请求中的 Cookie 字段带上).
- 服务器收到请求之后, 根据请求中的 sessionId在 Session 信息中获取到对应的用户信息, 再进行后 续操作.找不到则重新创建Session, 并把SessionID返回

Cookie 和 Session 的区别
- Cookie是客户端用来保存信息的机制,Session是服务器保存用户的一种机制
- Cookie 和 Session之间主要是通过 SessionId 关联起来的, SessionId 是 Cookie 和 Session 之间的桥梁
- Cookie和Session常配合但非必须。Cookie可存客户端非身份/非sessionId数据;sessionId也可通过URL传递,不依赖Cookie/Set - Cookie。
获取Cookie
java
@RequestMapping("/r14")
public String r14(@CookieValue("java") String java){
return "从Cookie中获取Java的值:"+java;
}
此处是向Cookie传值,注意区分传值的区域

获取Session
获取Session有两种方式,一种是通过HttpSession来获取,另一种就是通过标@SessionAttribute来获取
java
@RequestMapping("/getSession1")
public String getSession2(HttpSession session){
String userName=(String) session.getAttribute("userName");
int age=(int) session.getAttribute("age");
return "登录用户名为:"+userName+",age:"+age;
}
@RequestMapping("/getSession2")
public String getSession3(@SessionAttribute("userName") String userName){
return "登录用户为:"+userName;
}
获取Session要提前设置,这里通过预先设置的setSession可以直接获取到
java
@RequestMapping("/setSession")
public String setSesssion(HttpServletRequest request){
HttpSession session=request.getSession();
session.setAttribute("userName","zhangsan");
session.setAttribute("age",17);
return "设置session成功";
}

获取Header
Header中具有以下几个参数

以获取Use-Agent为例:
java
@RequestMapping("/getHeader2")
public String getHeader2(@RequestHeader("User-Agent") String userAgent){
return "从header中获取userAgent"+userAgent;
}


通过观察,结果正确
响应
返回静态页面
要想返回静态页面,得先设置一个静态页面为index.html,将其放在static目录下

java
@RequestMapping("/resp")
@RestController
public class RespController {
/**
* 返回页面
*
* @return
*/
@RequestMapping("/r1")
public String returnPage() {
return "/aa/index.html";
}
}

页面未正确返回,http响应把 "/index.html" 当做了http响应正文的数据
那Spring MVC如何才能识别出来 index.html 是一个静态页面, 并进行返回呢?
当将@RestController 改为 @Controller
java
@RequestMapping("/resp")
@Controller
public class RespController {
@RequestMapping("/r1")
public String returnPage(){
return "/aa/index.html";
}

此时返回结果为页面数据
关于@RestController与@Controller
前面使用的 @RestController 其实是返回的数据,而@Controller是定义一个控制器, Spring 框架启动时加载, 把这个对象交给Spring管理
可以理解为:@RestController = @Controller + @ResponseBody
此处的@ResponseBody是定义返回的数据格式为非视图, 返回一个 text/html 信息
查看@RestController源码

所以如果想返回视图的话, 只需要把 @ResponseBody 去掉就可以了, 也就是 @Controller
返回数据@ResponseBody
@ResponseBody 表示返回数据,加上 @ResponseBody 注解, 该方法就会把 "/index.html" 当做一个数据返回给前端.
java
@ResponseBody
@RequestMapping("/r1")
public String returnPage(){
return "/aa/index.html";
}

@ResponseBody 既是类注解, 又是方法注解
如果作用在类上, 表示该类的所有方法返回的都是数据;如果作用在方法上, 表示该方法返回的是数据
如果一个类的方法里, 既有返回数据的又有返回页面的, 就把 @ResponseBody 注解添加到对应的方法上即可

返回HTML代码片段
后端返回数据时, 如果数据中有HTML代码, 也会被浏览器解析

通过Fiddler观察响应结果, Content-Type 为 text/html

返回JSON
后端方法返回结果为对象
java
@ResponseBody
@RequestMapping("/r5")
public UserInfo returnJson(){
UserInfo userInfo=new UserInfo("zhangs",10,1);
return userInfo;
}


Content-Type的响应格式
|------------------------|-----------------------|
| text/html | body 数据格式是 HTML |
| text/css | body 数据格式是 CSS |
| application/javascript | body 数据格式是 JavaScript |
| application/json | body 数据格式是 JSON |
注意:
- 如果请求的是js文件, Spring MVC会自动设置Content-Type为 application/javascript
- 如果请求的是css文件, Spring MVC会自动设置Content-Type为 text/css
设置状态码
Spring MVC会根据我们方法的返回结果自动设置响应状态码, 程序员也可以手动指定状态码
java
@ResponseBody
@RequestMapping("/r6")
public UserInfo setStatus(HttpServletResponse resp){
resp.setStatus(401);
UserInfo userInfo=new UserInfo("zhangs",10,1);
return userInfo;
}

设置Header
Http响应报头也会向客户端传递一些附加信息, 比如服务程序的名称,请求的资源已移动到新地址等, 如: Content-Type, Local等
java
@ResponseBody
@RequestMapping("/r7")
public String setHeader(HttpServletResponse resp){
resp.setHeader("myHeader","myHeader");
return "设置header成功";
}

应用分层
应用分层 是一种软件开发设计思想, 它将应用程序分成N个层次, 这N个层次分别负责各自的职责
三层架构
MVC 主要把整体的系统分成了 Model(模型), View(视图)和Controller (控制器)三个层次

但随着前后端的分离,又将后端进行整体架构的分层。通常将整体架构分为表现层、业务逻辑层和数据层,这种分层方式也称之为"三层架构
|-------|----------------------------|
| 表现层 | 就是展示数据结果和接受用户指令的,是最靠近用户的一层 |
| 业务逻辑层 | 负责处理业务逻辑, 里面有复杂业务的具体实现 |
| 数据层 | 负责存储和管理与应用程序相关的数据 |
这三个部分, 在Spring的实现中, 均有体现:

|------------|------------------------------------|
| Controller | 控制层。接收前端发送的请求,对请求进行处理,并响应数据 |
| Service | 业务逻辑层。处理具体的业务逻辑 |
| Dao | 数据访问层,也称为持久层。负责数据访问操作,包括数据的增、删、改、查 |
0总结
1.学习Spring MVC, 其实就是学习各种Web开发需要用的到注解
|-------------------|---------------------------------------------------|
| 注解 | 含义 |
| @RequestMapping | 路由映射 |
| @RequestParam | 后端参数重命名 |
| @RequestBody | 接收JSON类型的参数 |
| @PathVariable | 接收路径参数 |
| @RequestPart | 上传文件 |
| @ResponseBody | 返回数据 |
| @CookieValue | 从Cookie中获取值 |
| @SessionAttribute | 从Session中获取值 |
| @RequestHeader | 从Header中获取值 |
| @Controller | 定义一个控制器, Spring 框架启动时加载, 把这个对象交给Spring管理. 默认返回 视图 |
| @RestController | @ResponseBody + @Controller 返回数据 |
2.关于Cookie和Session
- 会话机制:Cookie(客户端) 与 Session(服务端) 通过 SessionId 关联。
- Spring MVC 用法 :直接在方法参数中注入
HttpServletRequest/Response。 - 核心操作:从 Request 获取 Cookie/Session,用 Response 设置状态码。
