参考视频:SpringMVC教程,SpringMVC从零到精通,老杜SpringMVC,动力节点SpringMVC

1. RESTFul编程风格

1.1 RESTFul是什么

RESTFul是WEB服务接口的一种设计风格。

RESTFul定义了一组约束条件和规范,可以让WEB服务接口更加简洁、易于理解、易于扩展、安全可靠。

RESTFul对一个WEB服务接口都规定了哪些东西?

  • 对请求的URL格式有约束和规范
  • 对HTTP的请求方式有约束和规范
  • 对请求和响应的数据格式有约束和规范
  • 对HTTP状态码有约束和规范
  • 等 ……

REST对请求方式的约束是这样的:

  • 查询必须发送GET请求
  • 新增必须发送POST请求
  • 修改必须发送PUT请求
  • 删除必须发送DELETE请求

REST对URL的约束是这样的:

  • 传统的URL:get请求,/springmvc/getUserById?id=1

  • REST风格的URL:get请求,/springmvc/user/1

  • 传统的URL:get请求,/springmvc/deleteUserById?id=1

  • REST风格的URL:delete请求, /springmvc/user/1

RESTFul对URL的约束和规范的核心是:通过采用 不同的请求方式 + URL 来确定WEB服务中的资源。

RESTful 的英文全称是 Representational State Transfer(表述性状态转移)。简称REST。

表述性(Representational)是:URI + 请求方式。

状态(State)是:服务器端的数据。

转移(Transfer)是:变化。

表述性状态转移是指:通过 URI + 请求方式 来控制服务器端数据的变化。

1.2 RESTFul风格与传统方式对比

传统的 URL 与 RESTful URL 的区别是传统的 URL 是基于方法名进行资源访问和操作,而 RESTful URL 是基于资源的结构和状态进行操作的。下面是一张表格,展示两者之间的具体区别:

传统的 URL RESTful URL
GET /getUserById?id=1 GET /user/1
GET /getAllUser GET /user
POST /addUser POST /user
POST /modifyUser PUT /user
GET /deleteUserById?id=1 DELETE /user/1

从上表中我们可以看出,传统的URL是基于动作的,而 RESTful URL 是基于资源和状态的,因此 RESTful URL 更加清晰和易于理解,这也是 REST 架构风格被广泛使用的主要原因之一。

1.3 RESTFul方式演示查询

RESTFul规范中规定,如果要查询数据,需要发送GET请求。

1.3.1 查询所有(GET /api/user)

1、准备springmvc.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
<?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:mvc="http://www.springframework.org/schema/mvc"
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/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

<!--组件扫描-->
<context:component-scan base-package="com.muyoukule.controller"/>

<!--视图解析器-->
<bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
<property name="characterEncoding" value="UTF-8"/>
<property name="order" value="1"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring6.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/thymeleaf/"/>
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>

<!--启用注解-->
<mvc:annotation-driven/>

<!--视图控制器映射-->
<mvc:view-controller path="/" view-name="index"/>
</beans>

2、准备首页index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>测试RESTFul编程风格</h1>
<hr>

<!--RESTful风格的:查看用户列表-->
<a th:href="@{/user}">查看用户列表</a><br>

</body>
</html>

3、控制器Controller:

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

@RequestMapping(value = "/user", method = RequestMethod.GET)
public String getAll() {
System.out.println("正在查询所有用户信息....");
return "ok";
}
}

4、视图页面:

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>ok</title>
</head>
<body>
<h1>OK!</h1>
</body>
</html>

5、启动服务器,测试:http://localhost:8080/springmvc

测试RESTFul方式查询所有 RESTFul方式查询所有ok

6、控制台打印:

1
正在查询所有用户信息....

1.3.2 根据id查询(GET /api/user/1)

1、index.html添加超链接

1
2
<!--RESTful风格的:根据id查询用户信息-->
<a th:href="@{/user/110}">查询id=110的这个用户信息</a><br>

2、编写Controller

1
2
3
4
5
@RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
public String getById(@PathVariable("id") String id){
System.out.println("正在根据用户id查询用户信息...,用户id是" + id);
return "ok";
}

3、启动服务器测试:

测试RESTFul方式根据id查询 RESTFul方式根据id查询ok

4、控制台打印:

1
正在根据用户id查询用户信息...,用户id是110

1.4 RESTFul方式演示增加(POST /api/user)

RESTFul规范中规定,如果要进行保存操作,需要发送POST请求。

1、准备User实体(采用Lombok简化)

1
2
3
4
5
6
7
8
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String username;
private Integer password;
private String age;
}

PS:如需使用Lombok,需要在pom.xml引入依赖

1
2
3
4
5
6
<!--lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</dependency>

2、index.html添加表单

1
2
3
4
5
6
7
<!--RESTful风格的:新增用户信息。新增必须发送POST请求,需要使用form表单-->
<form th:action="@{/user}" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
年龄:<input type="text" name="age"><br>
<input type="submit" value="保存">
</form>

3、编写Controller

1
2
3
4
5
@RequestMapping(value = "/user", method = RequestMethod.POST)
public String save(User user) {
System.out.println("正在保存user用户信息..." + user);
return "ok";
}

4、启动服务器测试:

测试RESTFul方式增加 RESTFul方式增加ok

5、控制台打印:

1
正在保存user用户信息...User(username=木又枯了, password=123, age=18)

1.5 RESTFul方式演示修改

RESTFul规范中规定,如果要进行保存操作,需要发送PUT请求。

如何发送PUT请求?

  • 首先你必须是一个POST请求。
  • 在发送POST请求的时候,提交这样的数据:_method=PUT
  • 在web.xml文件配置SpringMVC提供的过滤器:HiddenHttpMethodFilter

实践一下:

1、index.html添加表单

1
2
3
4
5
6
7
8
9
10
11
<!--RESTful风格的:修改用户信息。修改必须发送PUT请求,要发送PUT请求,首先你必须是一个POST请求-->
<form th:action="@{/user}" method="post">

<!--隐藏域-->
<input type="hidden" name="_method" value="put">

用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
年龄:<input type="text" name="age"><br>
<input type="submit" value="修改">
</form>

2、配置隐藏的HTTP请求方式过滤器

PS:如果添加了字符编码过滤器,一定要在字符编码过滤器后面配置,否则字符编码过滤器会失效

1
2
3
4
5
6
7
8
9
<!--添加一个过滤器,这个过滤器是springmvc提前写好的,直接用就行了,这个过滤器可以帮助你将请求POST转换成PUT请求/DELETE请求-->
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

3、编写Controller

1
2
3
4
5
@RequestMapping(value = "/api/user", method = RequestMethod.PUT)
public String update(String username){
System.out.println("修改用户信息,用户名:" + username);
return "ok";
}

4、测试结果:

测试RESTFul方式修改

5、控制台打印:

1
正在修改用户信息:User(username=木又枯了z, password=123321, age=20)

1.6 RESTFul方式演示删除

1、index.html添加删除按钮

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!--RESTful风格的:删除用户信息-->
<!--删除必须发送DELETE请求。和PUT请求实现方式相同。-->
<!--发送DELETE请求的前提是POST请求,并且需要通过隐藏域提交 _method=delete -->
<a th:href="@{/user/120}" onclick="del(event)">删除用户id=120的用户信息</a>

<form id="delForm" method="post">
<input type="hidden" name="_method" value="delete">
</form>

<script>
function del(event){
// 获取表单
let delForm = document.getElementById("delForm");
// 给form的action赋值
delForm.action = event.target.href;
// 发送POST请求提交表单
delForm.submit();
// 非常重要,你需要阻止超链接的默认行为。
event.preventDefault();
}
</script>

2、配置隐藏的HTTP请求方式过滤器

PS:如果添加了字符编码过滤器,一定要在字符编码过滤器后面配置,否则字符编码过滤器会失效

1
2
3
4
5
6
7
8
9
<!--添加一个过滤器,这个过滤器是springmvc提前写好的,直接用就行了,这个过滤器可以帮助你将请求POST转换成PUT请求/DELETE请求-->
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

3、编写Controller

1
2
3
4
5
@RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)
public String del(@PathVariable("id") String id) {
System.out.println("正在删除用户:" + id);
return "ok";
}

4、测试结果:

5、控制台打印:

1
正在删除用户:120

1.7 HiddenHttpMethodFilter

HiddenHttpMethodFilter是Spring MVC框架提供的,专门用于RESTFul编程风格。

实现原理可以通过源码查看:

HiddenHttpMethodFilter源码(1) HiddenHttpMethodFilter源码(2)

通过源码可以看到,if语句中,首先判断是否为POST请求,如果是POST请求,调用request.getParameter(this.methodParam)。可以看到this.methodParam_method,这样就要求我们在提交请求方式的时候必须采用这个格式:_method=put。获取到请求方式之后,调用了toUpperCase转换成大写了。因此前端页面中小写的put或者大写的PUT都是可以的。if语句中嵌套的if语句说的是,只有请求方式是 PUTDELETEPATCH的时候会创建HttpMethodRequestWrapper对象。而HttpMethodRequestWrapper对象的构造方法是这样的:

HiddenHttpMethodFilter源码(3)

这样method就从POST变成了:PUT/DELETE/PATCH

PS:重点注意CharacterEncodingFilter和HiddenHttpMethodFilter的顺序!!!!

HiddenHttpMethodFilter源码中有这样一行代码:

1
String paramValue = request.getParameter(this.methodParam);

大家是否还记得,字符编码过滤器执行之前不能调用request.getParameter()方法,如果提前调用了,乱码问题就无法解决了。因为request.setCharacterEncoding()方法的执行必须在所有request.getParameter()方法之前执行。

因此这两个过滤器就有先后顺序的要求,在web.xml文件中,应该先配置CharacterEncodingFilter,然后再配置HiddenHttpMethodFilter。

2. 使用RESTFul实现用户管理系统

2.1 静态页面准备

文件包括:user.css、user_index.html、user_list.html、user_add.html、user_edit.html。代码如下:

2.1.1 user.css

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
.header {
background-color: #f2f2f2;
padding: 20px;
text-align: center;
}

ul {
list-style-type: none;
margin: 0;
padding: 0;
overflow: hidden;
background-color: #333;
}

li {
float: left;
}

li a {
display: block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}

li a:hover:not(.active) {
background-color: #111;
}

.active {
background-color: #4CAF50;
}

form {
width: 50%;
margin: 0 auto;
padding: 20px;
border: 1px solid #ddd;
border-radius: 4px;
}

label {
display: block;
margin-bottom: 8px;
}

input[type="text"], input[type="email"], select {
width: 100%;
padding: 6px 10px;
margin: 8px 0;
box-sizing: border-box;
border: 1px solid #555;
border-radius: 4px;
font-size: 16px;
}

button[type="submit"] {
padding: 10px;
background-color: #4CAF50;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
}

button[type="submit"]:hover {
background-color: #3e8e41;
}

table {
border-collapse: collapse;
width: 100%;
}

th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}

th {
background-color: #f2f2f2;
}

tr:nth-child(even) {
background-color: #f2f2f2;
}

.header {
background-color: #f2f2f2;
padding: 20px;
text-align: center;
}

a {
text-decoration: none;
color: #333;
}

.add-button {
margin-bottom: 20px;
padding: 10px;
background-color: #4CAF50;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
}

.add-button:hover {
background-color: #3e8e41;
}

2.1.2 user_index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户管理系统</title>
<link rel="stylesheet" href="user.css" type="text/css"></link>
</head>
<body>
<div class="header">
<h1>用户管理系统</h1>
</div>
<ul>
<li><a class="active" href="user_list.html">用户列表</a></li>
</ul>
</body>
</html>

用户管理系统user_index.html

2.1.3 user_list.html

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户列表</title>
<link rel="stylesheet" href="user.css" type="text/css"></link>
</head>
<body>
<div class="header">
<h1>用户列表</h1>
</div>
<div class="add-button-wrapper">
<a class="add-button" href="user_add.html">新增用户</a>
</div>
<table>
<thead>
<tr>
<th>编号</th>
<th>用户名</th>
<th>性别</th>
<th>邮箱</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>张三</td>
<td></td>
<td>zhangsan@powernode.com</td>
<td>
修改
删除
</td>
</tr>
<tr>
<td>2</td>
<td>李四</td>
<td></td>
<td>lisi@powernode.com</td>
<td>
修改
删除
</td>
</tr>
</tbody>
</table>
</body>
</html>

用户管理系统user_list.html

2.1.4 user_add.html

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>新增用户</title>
<link rel="stylesheet" href="user.css" type="text/css"></link>
</head>
<body>
<h1>新增用户</h1>
<form>
<label>用户名:</label>
<input type="text" name="username" required>

<label>性别:</label>
<select name="gender" required>
<option value="">-- 请选择 --</option>
<option value="1"></option>
<option value="0"></option>
</select>

<label>邮箱:</label>
<input type="email" name="email" required>

<button type="submit">保存</button>
</form>
</body>
</html>

用户管理系统user_add.html

2.1.5 user_edit.html

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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>修改用户</title>
<link rel="stylesheet" href="user.css" type="text/css"></link>
</head>
<body>
<h1>修改用户</h1>
<form>
<label>用户名:</label>
<input type="text" name="username" value="张三" required>

<label>性别:</label>
<select name="gender" required>
<option value="">-- 请选择 --</option>
<option value="1" selected></option>
<option value="0"></option>
</select>

<label>邮箱:</label>
<input type="email" name="email" value="zhangsan@powernode.com" required>

<button type="submit">修改</button>
</form>
</body>
</html>

用户管理系统user_edit.html

2.2 SpringMVC环境搭建

1、创建module:usermgt

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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.muyoukule</groupId>
<artifactId>usermgt</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 打包方式设置为war方式 -->
<packaging>war</packaging>

<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
<!-- Spring MVC依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.1.4</version>
</dependency>
<!--日志框架Logback依赖-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.3</version>
</dependency>
<!--Servlet依赖-->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
<!--Spring6和Thymeleaf整合依赖-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring6</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
<!--lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</dependency>
</dependencies>

</project>

2、添加web支持

3、配置web.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
<?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">

<!--字符编码过滤器-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!--HTTP请求方式过滤器-->
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!--前端控制器-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>

注意两个过滤器Filter的配置顺序:配置 CharacterEncodingFilter配置 HiddenHttpMethodFilter

4、配置springmvc.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
<?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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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 http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

<!--组件扫描-->
<context:component-scan base-package="com.muyoukule"/>

<!--视图解析器-->
<bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
<property name="characterEncoding" value="UTF-8"/>
<property name="order" value="1"/>
<property name="templateEngine">
<bean class="org.thymeleaf.spring6.SpringTemplateEngine">
<property name="templateResolver">
<bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/thymeleaf/"/>
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>

<!--开启注解-->
<mvc:annotation-driven/>

<!--开启默认Servlet-->
<mvc:default-servlet-handler/>
</beans>

5、创建packag

6、在WEB-INF目录下新建:thymeleaf目录

最终创建好的项目结构如下:

用户管理界面项目结构

2.3 显示首页

1、在应用的根下新建目录:static/css,将user.css文件拷贝进去。

2、将user_index.html拷贝到WEB-INF/thymeleaf目录下:

代码有两处需要修改:

修改user_index.html代码

3、在springmvc.xml文件中配置视图控制器映射:

1
2
<!--视图控制器映射-->
<mvc:view-controller path="/" view-name="user_index"/>

4、部署,配置启动服务器,测试:

测试user_index.html页面

2.4 实现用户列表

1、修改user_index.html中的超链接:

1
<li><a class="active" th:href="@{/user}">用户列表</a></li>

2、编写pojo:User

1
2
3
4
5
6
7
8
9
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String username;
private String email;
private Integer gender;
}

3、编写UserDao,提供selectAll方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Repository
public class UserDao {
private static List<User> users = new ArrayList<>();

static {
User user1 = new User(10001L, "张三", "zhangsan@example.com", 1);
User user2 = new User(10002L, "李四", "lisi@example.com", 1);
User user3 = new User(10003L, "王五", "wangwu@example.com", 1);
User user4 = new User(10004L, "赵六", "zhaoliu@example.com", 0);
User user5 = new User(10005L, "钱七", "qianqi@example.com", 0);
users.add(user1);
users.add(user2);
users.add(user3);
users.add(user4);
users.add(user5);
}

public List<User> selectAll() {
return users;
}
}

4、编写控制器UserController:

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

@Autowired
private UserDao userDao;

@GetMapping("/user")
public String list(Model model) {
// 获取所有的用户
List<User> users = userDao.selectAll();
// 存储到request域
model.addAttribute("users", users);
// 跳转视图
return "user_list";
}
}

5、将user_list.html拷贝到thymeleaf目录下,并进行代码修改,显示用户列表:

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
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>用户列表</title>
<link rel="stylesheet" th:href="@{/static/css/user.css}" type="text/css"></link>
</head>
<body>
<div class="header">
<h1>用户列表</h1>
</div>
<div class="add-button-wrapper">
<a class="add-button" href="user_add.html">新增用户</a>
</div>
<table>
<thead>
<tr>
<th>编号</th>
<th>用户名</th>
<th>性别</th>
<th>邮箱</th>
<th>操作</th>
</tr>
</thead>
<tbody>

<tr th:each="user : ${users}">
<td th:text="${user.id}"></td>
<td th:text="${user.username}"></td>
<td th:text="${user.gender == 1 ? '男' : '女'}"></td>
<td th:text="${user.email}"></td>
<td>
<a href="">修改</a>
<a href="">删除</a>
</td>
</tr>

</tbody>
</table>
</body>
</html>

6、测试结果:

测试用户查询功能成功

2.5 实现新增功能

2.5.1 跳转到新增页面

1、在用户列表user_index.html页面,修改新增用户的超链接:

1
<a class="add-button" th:href="@{/toAdd}">新增用户</a>

2、将user_add.html拷贝到thymeleaf目录下,并进行代码修改如下:

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
<!DOCTYPE html>
<html lang="en" xmlns:th="http:www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>新增用户</title>
<link rel="stylesheet" th:href="@{/static/css/user.css}" type="text/css"></link>
</head>
<body>
<h1>新增用户</h1>
<form>
<label>用户名:</label>
<input type="text" name="username" required>

<label>性别:</label>
<select name="gender" required>
<option value="">-- 请选择 --</option>
<option value="1"></option>
<option value="0"></option>
</select>

<label>邮箱:</label>
<input type="email" name="email" required>

<button type="submit">保存</button>
</form>
</body>
</html>

3、在springmvc.xml文件中配置视图控制器映射

1
<mvc:view-controller path="/toAdd" view-name="user_add"/>

4、启动服务器测试:

测试user_add.html页面

2.5.2 实现新增功能

1、前端页面发送POST请求,提交表单,修改user_add.html代码如下:

1
<form th:action="@{/user}" method="post"></form>

2、编写控制器UserController:

1
2
3
4
5
6
7
@RequestMapping(value = "/user", method = RequestMethod.POST)
public String save(User user){
// 调用userDao保存用户信息
userDao.insert(user);
// 重定向到用户列表页面(重新让浏览器发送一次全新的请求,去请求列表页面)
return "redirect:/user";
}

PS:保存成功后,采用重定向的方式跳转到用户列表。

3、编写UserDao:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public Long generateId(){
// 使用Stream API
Long maxId = users.stream().map(user -> user.getId()).reduce((id1, id2) -> id1 > id2 ? id1 : id2).get();
return maxId + 1;
}

public void insert(User user){
// 生成id
Long id = generateId();
// 给user对象id属性赋值
user.setId(id);
// 保存
users.add(user);
}

PS:单独写了一个方法生成id,内部使用了Stream API。

4、启动服务器测试:

实现新增用户功能

2.6 实现新增功能

2.6.1 跳转到修改页面

1、修改user_list.html中修改超链接:

1
<a th:href="@{'/user/' + ${user.id}}">修改</a>

2、编写Controller:

1
2
3
4
5
6
7
8
9
@GetMapping("/user/{id}")
public String toUpdate(@PathVariable("id") Long id, Model model){
// 根据id查询用户信息
User user = userDao.selectById(id);
// 将对象存储到request域
model.addAttribute("user", user);
// 跳转视图
return "user_edit";
}

3、编写UserDao:

1
2
3
public User selectById(Long id){
return users.stream().filter(user -> user.getId().equals(id)).findFirst().get();
}

4、将user_edit.html拷贝thymeleaf目录下,并修改代码如下:

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
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>修改用户</title>
<link rel="stylesheet" th:href="@{/static/css/user.css}" type="text/css"></link>
</head>
<body>
<h1>修改用户</h1>
<form>
<label>用户名:</label>
<input type="text" name="username" th:value="${user.username}" required>

<label>性别:</label>
<select name="gender" required>
<option value="">-- 请选择 --</option>
<option value="1" th:field="${user.gender}"></option>
<option value="0" th:field="${user.gender}"></option>
</select>

<label>邮箱:</label>
<input type="email" name="email" th:value="${user.email}" required>

<button type="submit">修改</button>
</form>
</body>
</html>

5、启动服务器测试:

测试user_edit.html页面

2.6.2 实现修改功能

1、将user_edit.html页面中的form表单修改一下,添加action,添加method,隐藏域的方式提交请求方式put,隐藏域的方式提交id:

1
2
3
4
5
6
<form th:action="@{/user}" method="post">
<!--隐藏域的方式设置请求方式为put请求-->
<input type="hidden" name="_method" value="put">
<!--隐藏域的方式提交id-->
<input type="hidden" name="id" th:value="${user.id}">
</form>

2、编写Controller:

1
2
3
4
5
6
7
@PutMapping("/user")
public String modify(User user){
// 更新数据
userDao.update(user);
// 重定向
return "redirect:/user";
}

3、编写UserDao:

1
2
3
4
5
6
7
8
public void update(User user) {
for (int i = 0; i < users.size(); i++) {
if (user.getId().equals(users.get(i).getId())) {
users.set(i, user);
break;
}
}
}

4、启动服务器测试:

测试修改用户信息

修改用户信息成功

2.7 实现删除功能

删除应该发送DELETE请求,要模拟DELETE请求,就需要使用表单方式提交。因此我们点击删除超链接时需要采用表单方式提交。

1、在user_list.html页面添加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
<!--为删除提供一个鼠标单击事件-->
<a th:href="@{'/user/' + ${user.id}}" onclick="del(event)">删除</a>

<!-- 写在body标签里面 -->
<!--为删除操作准备一个form表单,点击删除时提交form表单-->
<div style="display: none">
<form method="post" id="delForm">
<input type="hidden" name="_method" value="delete"/>
</form>
</div>

<script>
function del(event){
// 获取表单
let delForm = document.getElementById("delForm");
// 设置表单action
delForm.action = event.target.href;
if(window.confirm("您确定要删除吗?")){
// 提交表单
delForm.submit();
}
// 阻止超链接默认行为
event.preventDefault();
}
</script>

2、编写Controller:

1
2
3
4
5
6
7
@RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)
public String del(@PathVariable("id") Long id){
// 调用dao删除用户
userDao.deleteById(id);
// 重定向到列表
return "redirect:/user";
}

3、编写UserDao:

1
2
3
4
5
6
7
8
public void deleteById(Long id) {
for (int i = 0; i < users.size(); i++) {
if (id.equals(users.get(i).getId())) {
users.remove(i);
break;
}
}
}

4、启动服务器测试:

测试删除用户功能

删除用户成功