Thymeleaf 是一个现代化的服务器端 Java 模板引擎,支持 HTML、XML、JavaScript、CSS 甚至纯文本。以下是 Thymeleaf 语法的全面详解,包含所有属性和复杂用法。
一、基础语法
1. 标准表达式语法
Thymeleaf 提供了多种表达式类型:
<p th:text="${message}">默认消息</p>
1.1 变量表达式 ${...}
用于访问变量和模型属性:
<span th:text="${user.name}">用户名</span>
1.2 选择表达式 *{...}
用于选择当前选择的对象:
<div th:object="${user}">
<p th:text="*{name}">姓名</p>
<p th:text="*{age}">年龄</p>
</div>
1.3 消息表达式 #{...}
用于国际化消息:
<p th:text="#{welcome.message}">欢迎消息</p>
1.4 链接表达式 @{...}
用于 URL 链接:
<a th:href="@{/user/details(id=${user.id})}">用户详情</a>
1.5 片段表达式 ~{...}
用于模板片段:
<div th:insert="~{commons :: footer}"></div>
2. 字面量
- 文本字面量:
'单引号内容'
- 数字字面量:
123
,3.14
- 布尔字面量:
true
,false
- null 字面量:
null
<span th:text="'固定文本'"></span>
<span th:text="2019"></span>
<span th:text="true"></span>
3. 文本操作
- 字符串连接:
+
- 字面量替换:
|...|
<span th:text="'欢迎 ' + ${user.name}"></span>
<span th:text="|欢迎 ${user.name}|"></span>
4. 算术运算
- 基本运算:
+
,-
,*
,/
,%
<span th:text="${count + 1}"></span>
5. 布尔运算
- 比较:
>
,<
,>=
,<=
,==
,!=
- 逻辑运算:
and
,or
,!
,not
<div th:if="${user.age > 18}">成年人</div>
<div th:unless="${not user.active}">活跃用户</div>
6. 条件运算
- if-then:
(if) ? (then)
- if-then-else:
(if) ? (then) : (else)
- 默认值:
(value) ?: (defaultvalue)
<span th:text="${user.admin} ? '管理员' : '普通用户'"></span>
<span th:text="${user.name} ?: '匿名用户'"></span>
二、Thymeleaf 属性详解
1. 核心属性
th:text
设置元素的文本内容,会转义 HTML 标签:
<p th:text="${htmlContent}">默认文本</p>
th:utext
设置元素的文本内容,不转义 HTML 标签:
<p th:utext="${htmlContent}">默认文本</p>
th:value
设置表单元素的值:
<input type="text" th:value="${user.name}" />
th:with
定义局部变量:
<div th:with="first=${user.firstName}, last=${user.lastName}">
<span th:text="${first}"></span>
<span th:text="${last}"></span>
</div>
th:attr
设置任意属性:
<img th:attr="src=@{/images/logo.png}, title=${logoTitle}, alt=${logoAlt}" />
2. 条件属性
th:if
/ th:unless
条件显示元素:
<div th:if="${user.admin}">管理员面板</div>
<div th:unless="${user.blocked}">正常用户</div>
th:switch
/ th:case
多条件选择:
<div th:switch="${user.role}">
<p th:case="'admin'">管理员</p>
<p th:case="'manager'">经理</p>
<p th:case="*">普通用户</p>
</div>
3. 循环属性
th:each
循环遍历:
<ul>
<li th:each="item : ${items}" th:text="${item.name}">项目名称</li>
</ul>
循环状态变量:
<table>
<tr th:each="user, stat : ${users}">
<td th:text="${stat.index}">序号</td>
<td th:text="${user.name}">姓名</td>
<td th:text="${stat.odd} ? '奇数行' : '偶数行'">行类型</td>
</tr>
</table>
4. 模板布局属性
th:insert
插入模板片段:
<div th:insert="~{fragments/header :: main-header}"></div>
th:replace
替换当前元素为模板片段:
<footer th:replace="~{fragments/footer :: main-footer}"></footer>
th:include
包含模板片段的内容(已废弃,推荐使用 th:insert
或 th:replace
)
5. 表单属性
th:field
绑定表单字段:
<input type="text" th:field="*{name}" />
th:action
设置表单提交地址:
<form th:action="@{/user/save}" method="post">
<!-- 表单内容 -->
</form>
th:object
设置表单绑定对象:
<form th:object="${user}" method="post">
<input type="text" th:field="*{name}" />
<input type="text" th:field="*{email}" />
</form>
6. 链接和 URL 属性
th:href
设置链接地址:
<a th:href="@{/user/{id}/profile(id=${user.id})}">用户资料</a>
th:src
设置资源地址:
<img th:src="@{/images/logo.png}" />
7. 片段表达式属性
th:remove
移除模板片段:
<table>
<tr th:remove="all">
<td>将被移除的行</td>
</tr>
<tr th:remove="all-but-first">
<td>第一行保留</td>
</tr>
</table>
可选值:
all
:移除当前标签及其所有子标签body
:不移除当前标签但移除所有子标签tag
:移除当前标签但不移除子标签all-but-first
:移除除第一个外的所有子标签none
:什么都不移除
8. 其他属性
th:classappend
追加 CSS 类:
<div th:classappend="${user.active} ? 'active' : 'inactive'"></div>
th:styleappend
追加样式:
<div th:styleappend="'color:' + ${user.color}"></div>
th:lang
设置语言:
<html th:lang="${#locale.language}"></html>
th:title
设置 title 属性:
<img th:title="${imageTitle}" />
th:alt
设置 alt 属性:
<img th:alt="${imageAltText}" />
th:onclick
设置 onclick 事件:
<button th:onclick="'alert(\'' + ${message} + '\')'">点击</button>
th:inline
设置内联模式:
<script th:inline="javascript">
var user = [[${user}]];
console.log(user.name);
</script>
支持的值:
text
:文本内联javascript
:JavaScript 内联none
:禁用内联dart
:Dart 内联(已废弃)
三、复杂用法
1. 复杂表达式和实用对象
Thymeleaf 提供了一系列实用对象:
<p th:text="${#strings.isEmpty(user.name)}">检查是否为空</p>
<p th:text="${#lists.size(user.roles)}">角色数量</p>
<p th:text="${#dates.format(user.birthday, 'yyyy-MM-dd')}">生日</p>
<p th:text="${#numbers.formatDecimal(price, 1, 2)}">格式化数字</p>
<p th:text="${#calendars.createNow()}">当前时间</p>
常用实用对象:
#strings
:字符串工具#numbers
:数字工具#bools
:布尔工具#arrays
:数组工具#lists
:列表工具#sets
:集合工具#maps
:映射工具#dates
:日期工具(java.util.Date)#calendars
:日历工具(java.util.Calendar)#temporals
:Java8 时间工具(java.time.*)#objects
:对象工具#ids
:ID生成工具
2. 内联 JavaScript 和 CSS
JavaScript 内联:
<script th:inline="javascript">
var user = [[${user}]];
var message = [[#{welcome.message}]];
console.log(user.name + ": " + message);
</script>
CSS 内联:
<style th:inline="text">
.[[${mainClass}]] {
color: [[${mainColor}]];
}
</style>
3. 模板布局
定义片段:
fragments/header.html
:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head th:fragment="common-header">
<meta charset="UTF-8"/>
<title th:text="${title}">默认标题</title>
</head>
使用片段:
<head th:replace="~{fragments/header :: common-header}">
<!-- 将被替换 -->
</head>
4. 参数化片段
定义带参数的片段:
<div th:fragment="alert (type, message)">
<div class="alert alert-[[${type}]]">
[[${message}]]
</div>
</div>
使用带参数的片段:
<div th:replace="~{:: alert('success', '操作成功')}"></div>
5. 高级表单处理
多复选框绑定:
<input type="checkbox" th:field="*{roles}" th:value="'ADMIN'" /> 管理员
<input type="checkbox" th:field="*{roles}" th:value="'USER'" /> 普通用户
<input type="checkbox" th:field="*{roles}" th:value="'GUEST'" /> 访客
单选按钮绑定:
<input type="radio" th:field="*{gender}" th:value="'MALE'" /> 男
<input type="radio" th:field="*{gender}" th:value="'FEMALE'" /> 女
6. 复杂条件判断
使用 Elvis 运算符:
<span th:text="${user.name} ?: '匿名用户'"></span>
安全导航运算符:
<span th:text="${user?.address?.street}"></span>
7. 集合投影和选择
集合投影:
<div th:each="name : ${#strings.listSplit(user.fullName, ' ')}">
<span th:text="${name}"></span>
</div>
集合选择:
<div th:each="user : ${users.?[age > 18]}">
<span th:text="${user.name}">成年人</span>
</div>
8. 预处理表达式
在表达式执行前进行预处理:
<p th:text="${__#{${user.lang}+'.welcome.message'}__}">欢迎消息</p>
9. 自定义属性处理器
通过方言扩展可以创建自定义属性处理器:
public class MyDialect extends AbstractProcessorDialect {
public MyDialect() {
super("My Dialect", "my", 1000);
}
@Override
public Set<IProcessor> getProcessors(String dialectPrefix) {
Set<IProcessor> processors = new HashSet<>();
processors.add(new MyAttributeTagProcessor(dialectPrefix));
return processors;
}
}
然后在模板中使用:
<div my:customattr="${value}">自定义属性</div>
四、性能优化技巧
-
缓存模板:在生产环境中启用模板缓存
spring.thymeleaf.cache=true
-
使用片段缓存:
<div th:replace="~{fragments/menu :: main-menu}" th:cacheable="true"></div>
-
避免复杂表达式:尽量减少模板中的复杂逻辑
-
合理使用内联:只在必要时使用内联表达式
-
预编译模板:在构建时预编译模板
五、常见问题解决方案
-
表达式不解析:
- 确保 HTML 文件有
xmlns:th="http://www.thymeleaf.org"
声明 - 检查表达式语法是否正确
- 确保 HTML 文件有
-
表单绑定失败:
- 确保表单有
th:object
属性 - 检查字段名称是否匹配
- 确保表单有
-
国际化消息不显示:
- 检查消息文件位置和命名
- 确保有正确的区域设置解析器
-
片段无法加载:
- 检查片段路径是否正确
- 确保片段文件存在
-
性能问题:
- 启用模板缓存
- 减少模板中的复杂逻辑
六、最佳实践
-
保持模板简洁:将复杂逻辑移到控制器或服务层
-
合理组织模板:
- 使用片段组织可重用部分
- 创建布局模板
-
使用注释:
<!--/* 这是服务器端可见的注释 */--> <!-- 这是客户端可见的注释 -->
-
安全性考虑:
- 使用
th:text
而不是th:utext
除非必要 - 对用户输入进行适当转义
- 使用
-
测试模板:
- 编写模板测试用例
- 验证不同条件下的输出
通过掌握这些 Thymeleaf 语法和技巧,您可以创建灵活、高效且易于维护的模板。记住,模板的主要职责是展示数据,复杂的业务逻辑应该放在 Java 代码中。
喜欢的点个关注,想了解更多的可以关注微信公众号 “Eric的技术杂货库” ,提供更多的干货以及资料下载保存!