Spring MVC 基础

MVC设计模式

MVC是Model-View-Controller的缩写,是一种经典的软件设计模式,用于组织和管理应用程序的代码结构。

  • 模型(Model):模型代表应用程序的数据和业务逻辑。它负责处理数据以及与数据库、文件系统等进行交互,并提供对外的接口供控制器使用。模型通常会封装数据的读取、写入、更新和删除操作。
  • 视图(View):视图是用户界面的呈现层,负责展示模型中的数据给用户。它通常是用户可以看到和与之交互的部分,如网页、窗口、按钮等。视图可以直接从模型获取数据,但不能修改数据,只能通过控制器来间接操作模型。
  • 控制器(Controller):控制器充当模型和视图之间的协调者,处理用户的输入和交互。它接收用户的请求并根据请求调用合适的模型方法来处理数据操作,最后将结果传递给视图进行展示。控制器还负责将模型和视图解耦,确保它们之间的独立性。

Spring MVC简介

Spring MVC 是 Spring 框架的一个模块,是基于 MVC 设计模式的轻量级、灵活且功能强大的框架,用于构建可扩展和高效的 Web 应用

Spring MVC 核心组件

  • 前端控制器(DispatcherServlet):统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求
  • 处理器映射器(HandlerMapping):根据请求的url、method等信息查找Handler(控制器方法)
  • 处理器适配器(HandlerAdapter):通过HandlerAdapter对处理器(控制器方法)进行执行
  • 处理器(Handler):在DispatcherServlet的控制下Handler对具体的用户请求进行处理,通俗讲就是Controller层所写的业务代码
  • 视图解析器(ViewResolver):进行视图解析,得到相应的视图,如 JSP、Thymeleaf 或 FreeMarker 等
  • 视图(View):用于表示视图,可以是JSP、Thymeleaf 或 FreeMarker 等各种模板技术。

Spring MVC 执行流程

SpringMVC底层是Servlet,以Servlet为核心来接收请求,处理请求,显示处理结果给用户

  1. 客户端发送请求:客户端(浏览器)发送请求到服务器;
  2. 前端控制器拦截请求:Servlet 容器会将请求交给 Spring 的 前端控制器(DispatcherServlet),进行拦截请求,并委托给处理器映射器(HandlerMapping)进行处理
  3. 处理器映射器处理请求:处理器映射器(HandlerMapping)根据请求 URL 找到对应的 处理器(Controller)或 处理器链(HandlerExecutionChain)返回给 前端控制器(DispatcherServlet)
  4. 处理器适配器处理请求:前端控制器(DispatcherServlet)将找到的处理器(Controller)或 处理器链(HandlerExecutionChain)交给处理器适配器(HandlerAdapter)去调用合适的处理器(Controller)
  5. 处理器处理请求:处理器(Controller)执行业务逻辑,处理请求并生成模型数据,然后返回一个逻辑视图名(Logical View Name)或 ModelAndView 对象,返回给前端控制器(DispatcherServlet);
  6. 视图解析器解析视图:前端控制器(DispatcherServlet)将逻辑视图名或 ModelAndView 对象传递给视图解析器(ViewResolver),视图解析器根据逻辑视图名解析出一个具体的视图对象(如 JSP、Thymeleaf 或 FreeMarker 等)并响应给前端控制器(DispatcherServlet)
  7. 渲染视图并响应:前端控制器(DispatcherServlet)调用视图对象(View)的渲染方法,将模型数据填充到视图中,并生成最终的响应结果,将其返回给客户端(浏览器)。

Spring MVC 入门案例

添加SpringMVC依赖

创建Maven工程,在pom文件中指定打包方式,添加依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!-- 打包方式 -->
<packaging>war</packaging>

<dependencies>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.1</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- ServletAPI -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- Spring5和Thymeleaf整合包 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
</dependencies>

Controller控制层

创建Controller包,包下创建HelloController类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Controller
// @RequestMapping注解标识类,设置映射请求的请求路径的初始信息
@RequestMapping("/springMVC")
public class HelloController {
// @RequestMapping注解:处理请求和控制器方法之间的映射关系
// @RequestMapping注解的value属性可以通过请求地址匹配请求,/表示的当前工程的上下文路径
// localhost:8080/springMVC/
@RequestMapping("/")
public String index() {
System.out.println("访问首页成功");
//设置视图名称
return "index";
}
}

注册SpringMVC的前端控制器

修改src/main/webapp/WEB-INF/web.xml,注册SpringMVC的前端控制器DispatcherServlet(默认配置方式)此配置作用下,SpringMVC的配置文件默认位于WEB-INF下,默认名称为- servlet.xml,例如,以下配置所对应SpringMVC的配置文件位于WEB-INF下,文件名为springMVCservlet.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">

<!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 -->
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 通过初始化参数指定SpringMVC配置文件的位置和名称,加载配置文件 -->
<init-param>
<!-- contextConfigLocation为固定值 -->
<param-name>contextConfigLocation</param-name>
<!-- 使用classpath:表示从类路径查找配置文件,例如maven工程中的src/main/resources -->
<param-value>classpath:springMVC-servlet.xml</param-value>
</init-param>
<!--
作为框架的核心组件,在启动过程中有大量的初始化操作要做
而这些操作放在第一次请求时才执行会严重影响访问速度
因此需要通过此标签将启动控制DispatcherServlet的初始化时间提前到服务器启动时
-->
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<!--
设置springMVC的核心控制器所能处理的请求的请求路径
/ 匹配的请求可以是/login或.html或.js或.css方式的请求路径,但是/不能匹配.jsp请求路径的请求
/* 能够匹配所有请求
-->
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>

SpringMVC的配置文件

创建SpringMVC的配置文件,配置文件名称为web.xml文件中<servlet-name>标签中的名称-servlet.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

<!-- 自动扫描控制层组件 -->
<context:component-scan base-package="Controller"/>

<!-- 配置Thymeleaf视图解析器 -->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="order" value="1"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<!-- 视图前缀:跳转到/WEB-INF/templates/路径下某页面-->
<property name="prefix" value="/WEB-INF/templates/"/>
<!-- 视图后缀:匹配以.html结尾的页面 -->
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8" />
</bean>
</property>
</bean>
</property>
</bean>

</beans>

创建index.html页面

在/WEB-INF/templates/下创建index.html页面

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>Hello!Controller!</p>>
</body>
</html>

启动Tomcat

配置并启动Tomcat,访问localhost:8080/springMVC/后,页面显示Hello!Controller!,控制台打印访问首页成功

请求与响应的处理

请求方法的处理

使用@RequestMapping注解

@RequestMapping注解是Spring MVC框架中的一个注解,用于映射URL和方法之间的关系,可以应用于控制器类或方法上,同时可以接受多个参数实现更精确的请求映射,如下

  • value:指定映射的URL路径
  • method:指定处理请求的HTTP方法
  • params:指定请求中必须包含的参数及其值
  • headers:指定请求中必须包含的头部信息
  • consumes:指定请求的Content-Type(请求的媒体类型)
  • produces:指定响应的Content-Type(返回的媒体类型)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
@Controller
@RequestMapping("/")
public class TestController {

/**
* 当访问 /test1 时,会被此方法处理
*/
@RequestMapping(value = "/test1")
public void hello1() {

}

/**
* 当访问 /test2 或 /test3 时,都会被此方法处理
*/
@RequestMapping(value = {"/test2", "/test3"})
public void hello2() {

}

/**
* 只有通过 GET请求 访问 /test4 时,才会被此方法处理
*/
@RequestMapping(value = "/test4", method = RequestMethod.GET)
public void hello3() {

}

/**
* 只有通过 GET请求 或 POST请求 访问 /test5 时,才会被此方法处理
*/
@RequestMapping(value = "/test5", method = {RequestMethod.GET, RequestMethod.POST})
public void hello4() {

}

/**
* 只能通过 GET请求 访问 /test6,并且请求参数必须携带username,不能携带password,参数username必须为wen,参数gender不能为女
* 所设置的 params属性 必须同时满足,若其中一个不满足,此时页面回报错400,即资源未找到
*/
@RequestMapping(value = "/test6", method = RequestMethod.GET, params = {"username", "!password", "username=wen", "gender!=女"}
)
public void hello5() {

}

/**
* 只能通过 GET请求 访问 /test7,并且请求头必须携带username,不能携带password,参数username必须为wen,参数gender不能为女
* 所设置的 headers属性 必须同时满足,若其中一个不满足,此时页面回报错400,即资源未找到
*/
@RequestMapping(value = "/test7", method = RequestMethod.GET, headers = {"username", "!password", "username=wen", "gender!=女"})
public void hello6() {

}

/**
* 只有通过 POST请求 访问 /test8,并且Content-Type为application/json时,才会被此方法处理
*/
@RequestMapping(value = "/test8", method = RequestMethod.POST, consumes = "application/json")
public void hello7() {

}

/**
* 通过 GET请求 访问 /test9,会返回一个JSON格式的数据
*
* @return JSON数据
*/
@RequestMapping(value = "/test9", method = RequestMethod.GET, produces = "application/json")
@ResponseBody
public Map<String, Object> hello8() {
Map<String, Object> result = new HashMap<String, Object>();
result.put("message", "Hello, World!");
return result;
}

}

使用@RequestMapping衍生注解

@RequestMapping为了让开发人员能够更直观地定义处理不同HTTP请求方法的方法,还提供了一些衍生注解:

  • @GetMapping:用于处理HTTP GET请求。等同于@RequestMapping(method = RequestMethod.GET)。
  • @PostMapping:用于处理HTTP POST请求。等同于@RequestMapping(method = RequestMethod.POST)。
  • @PutMapping:用于处理HTTP PUT请求。等同于@RequestMapping(method = RequestMethod.PUT)。
  • @DeleteMapping:用于处理HTTP DELETE请求。等同于@RequestMapping(method = RequestMethod.DELETE)。
  • @PatchMapping:用于处理HTTP PATCH请求。等同于@RequestMapping(method = RequestMethod.PATCH)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Controller
public class TestController {

// 等同于 @RequestMapping(value = "/get", method = RequestMethod.GET)
@GetMapping("/get")
public void testGet() {

}

// 等同于 @RequestMapping(value = "/post", method = RequestMethod.POST)
@PostMapping("/post")
public void testPost() {

}

// 等同于 @RequestMapping(value = "/put", method = RequestMethod.PUT)
@PutMapping("/put")
public void testPut() {

}

// 等同于 @RequestMapping(value = "/delete", method = RequestMethod.DELETE)
@DeleteMapping("/delete")
public void testDelete() {

}

// 等同于 @RequestMapping(value = "/patch", method = RequestMethod.PATCH)
@PatchMapping("/patch")
public void testPatch() {

}

}

路径参数的处理

RESTful风格简介

Restful风格指的是网络应用中就是资源定位和资源操作的风格,将HTTP 协议里四个表示操作方式的动词GET、POST、PUT、DELETE,分别对应四种基本操作

请求对应操作简介
GET查询用来获取资源
POST新增用来新建资源
PUT修改用来更新资源
DELETE删除用来删除资源

REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性

请求方式传统风格REST风格对应操作
GETgetUserByIduser查询所有信息
GETgetUserById?id=1user/1查询指定信息
POSTsaveUseruser添加信息
DELETEdeleteUser?id=1user/1删除信息
PUTupdateUseruser修改更新信息

路径参数简介

路径参数是指URL中的一部分被用作参数的部分,可以使用占位符{parameter}来标识,比如/users/{id}中的{id}就是一个路径参数。在使用RESTful风格设计API时,一般会通过路径参数来表示资源的唯一标识、层级关系或传递参数信息

使用@PathVariable注解

@PathVariable注解用于获取URL路径中的参数,路径参数是指URL中的一部分被用作参数的部分,可以使用占位符{parameter}来标识,并在方法参数上使用@PathVariable注解来获取参数值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Controller
public class TestController {

/**
* 使用@PathVariable注解处理路径参数
*
* @param id 用户ID
* @return user
*/
@GetMapping("/user/{id}")
public void getUserById(@PathVariable("id") String id) {
System.out.println(id);
}

}

请求参数的处理

使用HttpServletRequest对象

HttpServletRequest对象是Servlet规范中定义的接口,它提供了丰富的方法来访问HTTP请求的信息,包括请求参数、请求头、请求方法等。通过HttpServletRequest对象,我们可以获取请求参数的值,并进行相应的处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@Controller
public class TestController {

/**
* 使用HttpServletRequest对象获取请求参数
*
* @param request HttpServletRequest对象
*/
@GetMapping("/getParameter")
public void getParameter(HttpServletRequest request) {
// 根据名称获取参数值(单个值)
String username = request.getParameter("username");
String password = request.getParameter("password");
System.out.println("username:" + username);
System.out.println("password:" + password);
}

/**
* 使用HttpServletRequest对象获取请求参数
*
* @param request HttpServletRequest对象
*/
@GetMapping("/getParameterValues")
public void getParameterValues(HttpServletRequest request) {
// 根据名称获取请求参数的所有值(数组)
String[] parameterValues = request.getParameterValues("parameterName");
if (parameterValues != null) {
for (String value : parameterValues) {
System.out.println("value:" + value);
}
}
}

/**
* 使用HttpServletRequest对象获取请求参数
*
* @param request HttpServletRequest对象
*/
@GetMapping("/getParameterMap")
public void getParameterMap(HttpServletRequest request) {
// 获取所有请求参数的映射(Map集合)
Map<String, String[]> parameterMap = request.getParameterMap();
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
String parameterName = entry.getKey();
String[] parameterValues = entry.getValue();
System.out.println("parameterName:" + parameterName);
for (String value : parameterValues) {
System.out.println("value:" + value);
}
}
}

}

使用@RequestParam注解

@RequestParam注解用于获取请求参数请求体参数。可以应用在方法的参数上,用于指定要获取的参数名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Controller
public class TestController {

/**
* 使用@RequestParam注解处理 请求参数 或 请求体参数
* value参数:指定要绑定的请求参数的名称
* required参数:指定该请求参数是否是必需的,true必需、false可选,默认为true
* defaultValue参数:指定当请求中缺少该参数时的默认值,required为true,与required属性不能同时使用
* required参数为true时,defaultValue参数和required参数不能同时使用
* required参数为false时,defaultValue参数和required参数可以同时使用
*
* @param param 请求参数内容
*/
@GetMapping("/getUserName")
public void getUserName(@RequestParam(value = "param", required = false, defaultValue = "wen") String param) {
System.out.println("请求参数内容:" + param);
}

}

请求体的处理

使用HttpServletRequest对象

HttpServletRequest对象是Servlet规范中定义的接口,它提供了丰富的方法来访问HTTP请求的信息,包括请求参数、请求头、请求方法等。通过HttpServletRequest对象,我们可以获取请求参数的值,并进行相应的处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
@Controller
public class TestController {

/**
* 使用getInputStream()获取请求体的内容
*
* @param request HttpServletRequest对象
*/
@GetMapping("/getInputStream")
public void getInputStream(HttpServletRequest request) {
try {
InputStream inputStream = request.getInputStream();
// 使用inputStream进行字节流的读取和处理
byte[] buffer = new byte[1024];
int length;
StringBuilder requestBody = new StringBuilder();
while ((length = inputStream.read(buffer)) != -1) {
// 处理读取到的字节数据
requestBody.append(new String(buffer, 0, length));
}
inputStream.close();

// 打印请求体内容
System.out.println("请求体内容:" + requestBody.toString());
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* 使用getReader()获取请求体的内容
*
* @param request HttpServletRequest对象
*/
@GetMapping("/getReader")
public void getReader(HttpServletRequest request) {
try {
BufferedReader reader = request.getReader();
// 使用reader进行字符流的读取和处理
StringBuilder requestBody = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
// 处理读取到的文本行数据
requestBody.append(line);
}
reader.close();

// 打印请求体内容
System.out.println("请求体内容:" + requestBody.toString());
} catch (IOException e) {
e.printStackTrace();
}
}

}

使用@RequestParam注解

@RequestParam注解用于获取请求参数请求体参数。可以应用在方法的参数上,用于指定要获取的参数名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Controller
public class TestController {

/**
* 使用@RequestParam注解处理 请求参数 或 请求体参数
* value参数:指定要绑定的请求参数的名称
* required参数:指定该请求参数是否是必需的,true必需、false可选,默认为true
* defaultValue参数:指定当请求中缺少该参数时的默认值,required为true,与required属性不能同时使用
* required参数为true时,defaultValue参数和required参数不能同时使用
* required参数为false时,defaultValue参数和required参数可以同时使用
*
* @param requestBody 请求体参数内容
*/
@GetMapping("/getUserName")
public void getUserName(@RequestParam(value = "requestBody", required = false, defaultValue = "wen") String requestBody) {
System.out.println("请求体参数内容:" + requestBody);
}

}

使用@RequestBody注解

@RequestBody注解用于将HTTP请求的内容绑定到方法的参数上,通常用于接收请求体中的JSON或XML数据,并将其转换为对象。它可以应用在方法的参数上,指示要绑定的请求体参数类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
@Controller
public class TestController {

/**
* 使用 @RequestBody 注解处理请求体中的 JSON 数据,映射为 List<String> 集合类型
*
* @param listJson 请求体中的 JSON 数据 ["item1", "item2", "item3"],映射为 List<String> 集合类型
*/
@PostMapping("/listJsonData")
public void listJsonData(@RequestBody List<String> listJson) {
// 处理 JSON 数组数据
System.out.println("listJson = " + listJson); // List集合 ["item1", "item2", "item3"]
// 可以根据需要进行进一步的 JSON 解析和处理
}

/**
* 使用 @RequestBody 注解处理请求体中的 JSON 数据,映射为 Map<String, Object> 类型
*
* @param mapJson 请求体中的 JSON 数据 {"key1":"value1", "key2":"value2"},映射为 Map<String, Object> 集合类型
*/
@PostMapping("/mapJsonData")
public void mapJsonData(@RequestBody Map<String, Object> mapJson) {
// 处理 JSON 对象数据
System.out.println("mapJson = " + mapJson); // Map集合 {"key1":"value1", "key2":"value2"}
// 可以根据需要进行进一步的 JSON 解析和处理
}

/**
* 使用 @RequestBody 注解处理请求体中的 JSON 数据,映射为 User 类型的对象
*
* @param userJson 请求体中的 JSON 数据{id=1, username='john', password='123'} ,映射为 User 类型的对象
*/
@PostMapping("/userJsonData")
public void userJsonData(@RequestBody User userJson) {
// 处理 JSON 对象数据
System.out.println("userJson = " + userJson); // User对象 User{id=1, username='john', password='123'}
// 可以根据需要进行进一步的 JSON 解析和处理
}

/**
* 使用 @RequestBody 注解处理请求体中的 JSON 对象(数组)
*
* @param userArrayJson 请求体中的 JSON 数据 [User{id=1, username='john', password='123'}],映射为 List<User> 类型
*/
@PostMapping("/userArrayJsonData")
public void userArrayJsonData(@RequestBody User[] userArrayJson) {
System.out.println("userArrayJson = " + Arrays.toString(userArrayJson));// User数组对象 [User{id=1, username='john', password='123'}]
// 可以根据需要进行进一步的 JSON 解析和处理
}

/**
* 使用 @RequestBody 注解处理请求体中的 JSON 对象(集合)
*
* @param userListJson 请求体中的 JSON 数据 [User{id=1, username='john', password='123'}],映射为 List<User> 类型
*/
@PostMapping("/userListJsonData")
public void userListJsonData(@RequestBody List<User> userListJson) {
System.out.println("userListJson = " + userListJson);// User列表对象 [User{id=1, username='john', password='123'}]
// 可以根据需要进行进一步的 JSON 解析和处理
}

/**
* 使用@RequestBody注解处理请求体中的 文本数据
*
* @param requestBody 请求体中的文本数据
*/
@PostMapping("/textData")
public void textData(@RequestBody String requestBody) {
System.out.println("请求体内容:" + requestBody);
}

/**
* 使用@RequestBody注解处理请求体中的 XML 数据
*
* @param requestBody 请求体中的 XML 数据
*/
@PostMapping("/xmlData")
public void xmlData(@RequestBody String requestBody) {
// 处理 XML 数据
System.out.println("请求体内容:" + requestBody);
// 可以使用 XML 解析库对请求体进行解析和处理
}

/**
* 使用@RequestBody注解处理请求体中的二进制数据
*
* @param requestBody 请求体中的二进制数据
*/
@PostMapping("/binaryData")
public void binaryData(@RequestBody byte[] requestBody) {
// 处理二进制数据
System.out.println("请求体内容长度:" + requestBody.length);
// 可以对二进制数据进行解析和处理
}

/**
* 使用 @RequestParam 注解处理请求体中的文件数据(multipart/form-data)
*
* @param file 文件参数
*/
@PostMapping("/fileData")
public void fileData(@RequestParam("file") MultipartFile file) {
// 处理文件数据
if (!file.isEmpty()) {
try {
byte[] fileBytes = file.getBytes();
// 对文件数据进行处理
System.out.println("文件名称:" + file.getOriginalFilename());
System.out.println("文件大小:" + file.getSize() + " bytes");
// ...
} catch (IOException e) {
e.printStackTrace();
}
}
}

}

请求头的处理

使用HttpServletRequest对象

HttpServletRequest对象是Servlet规范中定义的接口,它提供了丰富的方法来访问HTTP请求的信息,包括请求参数、请求头、请求方法等。通过HttpServletRequest对象,我们可以获取请求参数的值,并进行相应的处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Controller
public class TestController {

/**
* 使用HttpServletRequest对象获取 请求头User-Agent
*
* @param request HttpServletRequest对象
*/
@GetMapping("/getParameter")
public void getParameter(HttpServletRequest request) {
// 根据名称获取参数值(单个值)
String userAgent = request.getHeader("User-Agent");
System.out.println(userAgent);
}

}

使用@RequestHeader注解

@RequestHeader注解用于从请求头中获取参数值。可以应用在方法的参数上,指定要获取的参数名称,并将对应的值作为方法参数进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Controller
public class TestController {

/**
* 使用@RequestHeader注解处理 请求头部User-Agent
*
* @param userAgent 请求头User-Agent
*/
@GetMapping("/getUserAgent")
public void getUserAgent(@RequestHeader("User-Agent") String userAgent) {
System.out.println(userAgent);
}

}

Cookie的处理

使用HttpServletRequest对象

HttpServletRequest对象是Servlet规范中定义的接口,它提供了丰富的方法来访问HTTP请求的信息,包括请求参数、请求头、请求方法等。通过HttpServletRequest对象,我们可以获取请求参数的值,并进行相应的处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Controller
public class TestController {

@GetMapping("/hello")
public void sayHello(HttpServletRequest request) {
// 获取所有的 Cookie
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
String name = cookie.getName();
String value = cookie.getValue();
System.out.println("Cookie Name: " + name);
System.out.println("Cookie Value: " + value);
}
} else {
System.out.println("No cookies found in the request");
}
}

}

使用@CookieValue注解

@CookieValue注解用于从请求的Cookie中获取参数值。可以应用在方法的参数上,指定要获取的Cookie名称,并将对应的值作为方法参数进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Controller
public class TestController {

/**
* 使用@CookieValue注解处理Cookie值
*
* @param sessionId sessionId
*/
@GetMapping("/getCookieValue")
public void getCookieValue(@CookieValue("sessionId") String sessionId) {
System.out.println(sessionId);
}

}

响应的处理

响应视图

当控制器方法返回一个视图名称时,Spring MVC会根据配置的视图解析器来解析该视图名称,并找到相应的视图模板文件。然后,框架将模型中的数据填充到模板中,生成最终的响应内容,并将其返回给客户端。

1
2
3
4
5
6
7
8
9
@Controller
public class TestController {

@GetMapping("/index")
public String home() {
return "index";
}

}

响应转发

当控制器方法返回一个请求转发路径时,Spring MVC会将请求转发到指定的路径。这样可以实现在服务器内部跳转,不改变客户端的URL。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Controller
public class TestController {

/**
* 处理转发请求,并将请求转发到新的URL
*
* @return 转发到百度www.baidu.com
*/
@GetMapping("/forward")
public String forward() {
return "forward:/www.baidu.com"; // 转发到新的URL
}

}

响应重定向

当控制器方法返回一个重定向路径时,Spring MVC会将请求重定向到指定的路径。这样可以实现在客户端进行URL跳转。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Controller
public class TestController {

/**
* 处理重定向请求,并将请求重定向到新的URL
*
* @return 重定向到百度www.baidu.com
*/
@GetMapping("/redirect")
public String redirect() {
return "redirect:/www.baidu.com"; // 重定向到新的URL
}

}

响应JSON数据

当控制器方法需要返回JSON数据时,可以在方法中使用 @ResponseBody 注解来指定返回的数据会以JSON格式进行序列化,并直接返回给客户端。注意:每个方法只能使用一次@ResponseBody注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@Controller
public class TestController {

/**
* 将 List集合 转换为 Json字符串 并响应到浏览器
*
* @return 包含List集合的JSON字符串
*/
@ResponseBody
@GetMapping("/listJson")
public List<String> getListJson() {
ArrayList<String> listJson = new ArrayList<>();
listJson.add("My first json");
listJson.add("My second json");
listJson.add("My third json");
return listJson;
}

/**
* 将 Map集合 转换为 Json字符串 并响应到浏览器
*
* @return 包含Map集合的JSON字符串
*/
@ResponseBody
@GetMapping("/mapJson")
public Map<String, Object> getMapJson() {
// 处理逻辑
Map<String, Object> mapJson = new HashMap<String, Object>();
mapJson.put("name", "John");
mapJson.put("age", 25);
return mapJson;
}

/**
* 将 实体类User 转换为 Json字符串 并响应到浏览器
*
* @return 包含User对象的JSON字符串
*/
@ResponseBody
@RequestMapping("/userJson")
public User getUserJson() {
return new User(1, "小明", "123456");
}

}

响应XML数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Controller
public class TestController {

/**
* 获取数据并以XML格式返回
*
* @return 包含数据的Map对象
*/
@GetMapping(value = "/xml", produces = MediaType.APPLICATION_XML_VALUE)
public Map<String, Object> getXmlData() {
Map<String, Object> data = new HashMap<String, Object>();
data.put("id", 1);
data.put("name", "John");
return data;
}

}

响应状态码

默认情况下,Spring MVC的响应状态码由框架自动设置。但是,我们也可以在控制器方法中使用 @ResponseStatus 注解来指定特定的响应状态码。

1
2
3
4
5
6
7
8
9
10
@Controller
public class TestController {

@GetMapping("/status")
@ResponseStatus(HttpStatus.OK)
public void getStatus() {

}

}

使用ResponseEntity类封装响应

ResponseEntity是Spring框架提供的一个类,用于封装HTTP响应的内容、状态码和头部信息。它可以灵活地构建响应对象,并提供了一些便捷的方法来处理HTTP响应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@Controller
public class TestController {

/**
* 返回一个成功的响应,无具体数据
* @return 成功响应
*/
@GetMapping("/ok")
public ResponseEntity<String> ok() {
// 执行一些逻辑操作
return ResponseEntity.ok().build();
}

/**
* 返回一个成功的响应,并携带数据
* @return 包含数据的成功响应
*/
@GetMapping("/success")
public ResponseEntity<String> success() {
// 执行一些逻辑操作
String data = "访问成功!";
return ResponseEntity.ok(data);
}

/**
* 返回一个自定义的成功响应,并携带数据
* @return 自定义的成功响应
*/
@PostMapping("/requestSuccess")
public ResponseEntity<String> requestSuccess() {
// 执行一些逻辑操作来创建用户
String data = "自定义数据";
return ResponseEntity.status(HttpStatus.CREATED).body(data);
}

/**
* 返回一个错误的响应,并携带错误消息
* @return 错误响应
*/
@GetMapping("/error")
public ResponseEntity<String> error() {
// 执行一些逻辑操作来获取指定ID的资源
String errorMessage = "访问失败!";
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorMessage);
}

}

文件上传下载

文件上传

前端文件上传表单页面

1
2
3
4
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<button type="submit">上传</button>
</form>

后端处理文件上传请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Controller
public class FileUpload {

@ResponseBody
@PostMapping("/upload")
public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return ResponseEntity.badRequest().body("请选择要上传的文件");
}

try {
// 获取文件名
String fileName = file.getOriginalFilename();

// 处理文件重名问题
String substring = fileName.substring(fileName.lastIndexOf("."));
fileName = UUID.randomUUID().toString() + substring;

// 获取文件保存的路径(假设为uploads目录)
String filePath = "/path/to/uploads/";

// 创建文件对象
File dest = new File(filePath + fileName);

// 将上传文件保存到目标位置
file.transferTo(dest);

return ResponseEntity.ok("文件上传成功");
} catch (IOException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("文件上传失败:" + e.getMessage());
}
}

}

文件下载

前端下载链接

1
2
3
<a href="/download1">下载文件</a>
<a href="/download2">下载文件</a>
<a href="/download3">下载文件</a>

后端处理文件下载请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
@RestController
public class DownloadFile {

/**
* 使用 HttpServletResponse 方式下载文件
*
* @param response HTTP响应对象
*/
@GetMapping("/download1")
public void downloadFile1(HttpServletResponse response) {
// 获取文件保存的路径和文件名
String filePath = "/path/to/uploads/";
String fileName = "example.txt";

File file = new File(filePath + fileName);

try (FileInputStream fis = new FileInputStream(file)) {
// 设置响应头
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);

// 发送文件内容给客户端
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) {
response.getOutputStream().write(buffer, 0, len);
}
response.flushBuffer();
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* 使用 ResponseEntity<byte[]> 方式下载文件
*
* @return 包含文件内容的 ResponseEntity 对象
*/
@GetMapping("/download2")
public ResponseEntity<byte[]> downloadFile2() {
// 获取文件保存的路径和文件名
String filePath = "/path/to/uploads/";
String fileName = "example.txt";

// 创建文件对象
File file = new File(filePath + fileName);

try {
// 读取文件内容
byte[] fileContent = new byte[(int) file.length()];
FileInputStream fis = new FileInputStream(file);
fis.read(fileContent);
fis.close();

// 设置响应头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", fileName);

return new ResponseEntity<>(fileContent, headers, HttpStatus.OK);
} catch (IOException e) {
e.printStackTrace();
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}

/**
* 使用 ResponseEntity<FileSystemResource> 方式下载文件
*
* @return 包含文件资源的 ResponseEntity 对象
*/
@GetMapping("/download3")
public ResponseEntity<FileSystemResource> downloadFile3() {
// 获取文件保存的路径和文件名
String filePath = "/path/to/uploads/";
String fileName = "example.txt";

// 创建文件对象
File file = new File(filePath + fileName);

// 设置响应头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", fileName);

try {
FileSystemResource resource = new FileSystemResource(file);
return new ResponseEntity<>(resource, headers, HttpStatus.OK);
} catch (Exception e) {
e.printStackTrace();
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}

}

拦截器(Interceptor)

拦截器简介

拦截器(Interceptor)是表现层框架(如 Spring MVC、Struts)自身提供的组件,在 Spring MVC 中,拦截器定义在 org.springframework.web.servlet 包下,用于拦截用户请求,并在请求到达目标处理程序(Controller)之前或之后执行相应的逻辑

拦截器实现

在 Spring MVC 中,拦截器通过实现 HandlerInterceptor 接口来定义,并重写其中的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class MyInterceptor implements HandlerInterceptor {
/**
* 在处理程序执行之前进行预处理操作,如权限验证、日志记录等
*
* @param request 当前请求的HttpServletRequest对象
* @param response 当前请求的HttpServletResponse对象
* @param handler 当前请求的处理程序对象
* @return 是否继续执行后续的拦截器和处理程序,返回true表示继续执行,返回false表示请求将被拦截
* @throws Exception 异常信息
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 在这里可以进行一些预处理操作,如权限验证、日志记录等
return true;
}

/**
* 在处理程序执行之后、视图渲染之前进行后处理操作
*
* @param request 当前请求的HttpServletRequest对象
* @param response 当前请求的HttpServletResponse对象
* @param handler 当前请求的处理程序对象
* @param modelAndView 当前请求的ModelAndView对象,可以在此处修改视图或数据
* @throws Exception 异常信息
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 在这里可以进行一些后处理操作,如修改视图或数据
}

/**
* 在整个请求完成后进行清理操作,无论处理程序是否发生异常
*
* @param request 当前请求的HttpServletRequest对象
* @param response 当前请求的HttpServletResponse对象
* @param handler 当前请求的处理程序对象
* @param ex 处理程序抛出的异常,如果没有异常则为null
* @throws Exception 异常信息
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 在这里可以进行一些清理操作,如释放资源等
}

}

在Spring MVC中使用MyInterceptor拦截器,还需要在配置文件中进行相应的配置

1
2
3
4
5
6
7
8
9
10
<!-- 配置拦截器 -->
<mvc:interceptors>
<!-- 注册自定义拦截器 -->
<mvc:interceptor>
<!-- 拦截的路径 -->
<mvc:mapping path="/**"/>
<!-- 自定义拦截器类 -->
<bean class="com.wen.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>

拦截器链

拦截器链是指在一个请求被处理的过程中,多个拦截器按照一定的顺序依次执行的机制,执行顺序与它们在配置文件或代码中的注册顺序一致,XML配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- 配置拦截器链 -->
<mvc:interceptors>
<!-- 注册拦截器1 -->
<mvc:interceptor>
<mvc:mapping path="/path1/*"/>
<bean class="com.example.Interceptor1"/>
</mvc:interceptor>

<!-- 注册拦截器2 -->
<mvc:interceptor>
<mvc:mapping path="/path2/*"/>
<bean class="com.example.Interceptor2"/>
</mvc:interceptor>

<!-- 注册拦截器3 -->
<mvc:interceptor>
<mvc:mapping path="/path3/*"/>
<bean class="com.example.Interceptor3"/>
</mvc:interceptor>
</mvc:interceptors>

监听器、过滤器、拦截器的区别

功能定位

  • 监听器:主要用于监听和响应特定事件,对事件进行处理。
  • 过滤器:用于对请求进行预处理和后处理,对请求和响应进行筛选和修改。
  • 拦截器:在请求和响应的处理过程中,拦截并插入额外的逻辑来实现附加功能。

使用场景

  • 监听器:适用于需要侦听和响应应用程序中的事件的情况,如服务器启动/关闭、会话的创建/销毁等。
  • 过滤器:常用于对请求进行过滤和处理,如身份验证、请求参数处理、字符编码等。
  • 拦截器:通常用于在应用程序级别上实现横切关注点,如日志记录、权限验证、性能监控等。

执行顺序

  • 监听器:由事件的发生顺序决定的,是被动的组件,它通过事件监听机制等待特定事件的发生,并在事件触发时执行相应的处理逻辑
  • 过滤器:过滤器通常形成一个过滤器链(Filter Chain),按照配置的顺序依次执行
  • 拦截器:由框架或应用程序维护拦截器链,通过拦截器链中的调用顺序依次执行拦截器的前置和后置逻辑

技术实现

  • 监听器:使用事件监听机制,通过实现特定接口或注解来定义监听器,并在配置文件或代码中进行注册。
  • 过滤器:使用Servlet规范中的Filter接口,通过编写过滤器类并在配置文件中进行配置和映射。
  • 拦截器:通常由框架或应用程序提供,通过定义拦截器类,并在配置或代码中进行配置和使用。