产生背景

  1. JavaServerPages 的 .jsp 文件,代码中既有页面布局的静态元素,又有交互后端数据的动态元素,代码维护不方便
  2. 浏览器解释 html 时会忽略未定义的标签属性,因此某些标签可以根据后端返回值动态变换
  3. Spring 提供了 Model 对象的 addAttribute(String attributeName, Object attributeValue) 方法,在控制器(Controller)方法中传递数据到视图
  4. Springboot 使用 “习惯优于配置” ,集成了 ThymeLeaf 引擎,可以通过 spring-boot-starter-thymeleaf 实现自动配置

交互方式

在后端依赖文件引入

使用Maven的项目,在pom.xml文件加入

1
2
3
4
5
6
7
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>

使用Gradle的项目,在 gradle.build 文件加入

1
2
3
dependencies{
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
}

在html文件中加入标签

对于使用 Thymeleaf 的模板文件,Thymeleaf 的语法是基于 XML 的,因此需要在文件的根元素 <html> 中加上 xmlns 声明

1
2
3
4
5
6
7
8
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Thymeleaf Example</title>
</head>
<body>
<h1 th:text="${message}">Default message</h1>
</body>
</html>

对于标准的 HTML5 文件(即 <!DOCTYPE html>),它本身并不需要严格遵循 XHTML 的语法要求,因此可以省略命名空间声明,直接使用 Thymeleaf 标签和功能。

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"> <!-- 设置字符编码 -->
<title>Thymeleaf Example</title>
</head>
<body>
<h1 th:text="${message}">Default message</h1>
</body>
</html>

可以看出获取变量值用 $ 符号,对于javaBean的话使用 变量名.属性名 方式获取,这点和 EL 表达式一样。另外 $ 表达式只能写在th标签内部,否则不生效。上面例子就是使用 th:text 标签的值替换 h1 标签里面的值,原有的 Default message 仅用于前端开发时展示用

Thymeleaf语法

1
th:属性名="标准表达式"

代表性属性名

属性名 功能
text 普通字符串
utext 转义文本
value 设置文本框的值
if/each 条件表达式
th:with 定义常量
action 指定表单的提交地址
object 设置绑定到表单元素的 Java 对象
field 设置元素的 id 属性或扮演 id 属性角色的属性
th:(~)append 追加(attr属性、class类、style样式)

标准表达式

Thymeleaf 使用的标准表达式有四种类型:

类型名 书写方法
变量表达式 ${…}
选择变量表达式 *{…}
消息表达式 #{…}
链接 URL 表达式 @{…}

此外,可进行算数、比较、条件、真假运算

例如 th:with="isEven=(${prodStat.count} % 2 == 0)"

分类 实例
if-then (if) ? (then)
if-then-else (if) ? (then) : (else)
Default (value) ?: (defaultvalue)
1
2
3
4
5
6
7
8
9
10
11
12
条件表达式if/unless
<a href="comments.html" th:href="@{/product/comments(prodId=${prod.id})}" th:if="${not #lists.isEmpty(prod.comments)}">view</a>
<a href="comments.html" th:href="@{/comments(prodId=${prod.id})}" th:unless="${#lists.isEmpty(prod.comments)}">view</a>

<div th:switch="${user.role}">
<p th:case="'admin'">User is an administrator</p> <p th:case="#{roles.manager}">User is a manager</p>
</div>

<div th:switch="${user.role}">
<p th:case="'admin'">User is an administrator</p> <p th:case="#{roles.manager}">User is a manager</p> <p th:case="*">User is some other thing</p>
</div>

使用案例

消息表达式

1
2
3
4
5
6
7
8
<span th:text="'Welcome to our application, ' + ${user.name} + '!'">

<!-- 更简单的表达式,但是|…|中只能包含变量表达式${…},不能包含其他常量、条件表达式等 -->
<span th:text="|Welcome to our application, ${user.name}!|">


<p th:utext="#{home.welcome(${session.user.name})}"> Welcome to our grocery store, Sebastian Pepper!</p>
<p th:utext="#{${welcomeMsgKey}(${session.user.name})}"> Welcome to our grocery store, Sebastian Pepper!</p>

选择变量表达式

1
2
3
4
5
6
<div th:object="${session.user}">
<p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text=*{nationality}">Saturn</span>.</p>
</div>

等价于

1
2
3
4
5
6
7
8
9
10
11
12
<div>
<p>Name: <span th:text="${session.user.firstName}">Sebastian</span>.</p>
<p>Surname: <span th:text="${session.user.lastName}">Pepper</span>.</p>
<p>Nationality: <span th:text="${session.user.nationality}">Saturn</span>.</p>
</div>

<!-- 也可用选择变量表达式 -->
<div>
<p>Name: <span th:text="*{session.user.name}">Sebastian</span>.</p>
<p>Surname: <span th:text="*{session.user.surname}">Pepper</span>.</p>
<p>Nationality: <span th:text="*{session.user.nationality}">Saturn</span>.</p>
</div>

在标签中加入 th:block 使编译前不可见

1
2
3
4
5
6
7
8
9
<table>
<tr>
<td th:text="${user.login}">...</td>
<td th:text="${user.name}">...</td> </tr>
<tr>
<td colspan="2" th:text="${user.address}">...</td>
</tr>
<!--/*/ </th:block> /*/-->
</table>

引入外部链接

1
2
3
<a th:href="@{http://www.baidu.com}">绝对路径</a>
<a th:href="@{/}">相对路径</a>
<a th:href="@{css/bootstrap.min.css}">Content路径,默认访问static下的css文件夹</a>
  • 表格处理
1
2
3
4
5
<form method="post" th:action="@{/example}" th:object="${User}">
<input type="text" th:field="*{name}"/>
<input type="text" th:field="*{age}"/>
<input type="submit" value="submit"/>
</form>

th:action 指定提交表单的方式。th:object 指定要绑定的对象,th:field 则映射到绑定对象的字段。
因此,th:object 和 th:field 通常作为一个集合使用。

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

@PostMapping("/example")
public String submitForm(@ModelAttribute("User") User user) {
// 这里,user对象会自动包含表单提交的数据
// 你可以直接访问user.getName() 和 user.getAge()

System.out.println(user.getName()); // 输出表单提交的name
System.out.println(user.getAge()); // 输出表单提交的age

return "result"; // 返回到视图页面
}
}

通过这种方式在Spring Controller方法中接收一个完整的User对象,而不必手动接收每个单独的字段(nameage)。Spring的@ModelAttribute会自动绑定表单中的数据到对应的Java对象上。