引言:为什么需要掌握Thymeleaf高级特性?
Thymeleaf作为一款面向Java的现代服务器端模板引擎,以其原生HTML支持、无缝集成Spring生态和强大的表达式能力,成为Spring Boot项目中视图层的首选方案。除了基础的变量渲染和标签使用外,Thymeleaf的高级特性(如模板布局、片段复用、动态表单处理) 和内置工具对象(如日期格式化、集合操作、字符串处理) 能显著提升开发效率,实现更优雅、可维护的模板代码。本文将深入剖析这些高级功能,结合实战示例带你从"会用"到"精通"。
一、Thymeleaf高级特性:构建灵活可复用的模板体系
1.1 模板布局与片段复用:告别重复代码
在Web开发中,页面的公共区域(如导航栏、页脚、侧边栏) 通常需要在多个页面中复用。Thymeleaf通过片段定义(th:fragment) 和片段引入(th:replace/th:insert/th:include) 解决这一问题,配合Layout Dialect布局方言可实现更强大的模板继承。
1.1.1 基础片段定义与引入
定义片段:使用th:fragment
标记可复用的HTML片段,通常放在fragments
目录下(如fragments/header.html
):
<!-- src/main/resources/templates/fragments/header.html -->
<header th:fragment="siteHeader(title)">
<nav class="navbar">
<div class="container">
<h1 th:text="${title}">默认标题</h1>
<ul>
<li><a th:href="@{/home}">首页</a></li>
<li><a th:href="@{/about}">关于我们</a></li>
</ul>
</div>
</nav>
</header>
引入片段:使用th:replace
(替换当前标签)、th:insert
(插入到当前标签内部)或th:include
(插入片段内容,已过时)引入片段:
<!-- src/main/resources/templates/index.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<!-- 引入头部片段,传递参数"我的网站" -->
<div th:replace="fragments/header :: siteHeader('我的网站')"></div>
<main>首页内容</main>
<!-- 引入页脚片段(假设已定义footer.html) -->
<div th:insert="fragments/footer :: siteFooter"></div>
</body>
</html>
关键区别:th:replace
会替换当前标签(如上述<div>
会被<header>
替换),而th:insert
会保留当前标签,将片段作为子元素插入。
1.1.2 布局继承:实现"母版页"效果
通过Layout Dialect(需额外依赖)可实现类似JSP的"母版页"功能,定义一个基础布局模板,子页面仅需填充特定区域。
步骤1:添加Layout Dialect依赖(Maven):
<dependency>
<groupId>nz.net.ultraq.thymeleaf</groupId>
<artifactId>thymeleaf-layout-dialect</artifactId>
<version>3.1.0</version> <!-- 适配Thymeleaf 3.x -->
</dependency>
步骤2:定义基础布局模板(base.html):
<!-- src/main/resources/templates/layouts/base.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="UTF-8">
<title layout:title-pattern="$CONTENT_TITLE - 我的网站">默认标题</title>
<link rel="stylesheet" th:href="@{/css/main.css}">
</head>
<body>
<header th:replace="fragments/header :: siteHeader"></header>
<!-- 内容区域:子页面将替换此标签 -->
<main layout:fragment="content"></main>
<footer th:replace="fragments/footer :: siteFooter"></footer>
</body>
</html>
步骤3:子页面继承布局并填充内容:
<!-- src/main/resources/templates/home.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layouts/base.html}"> <!-- 继承基础布局 -->
<head>
<title layout:title-pattern="$CONTENT_TITLE">首页</title> <!-- 页面标题 -->
</head>
<!-- 填充布局中的content片段 -->
<main layout:fragment="content">
<h2>欢迎来到首页</h2>
<p th:text="${welcomeMsg}">默认欢迎语</p>
</main>
</html>
效果:子页面会自动包含基础布局中的header、footer和CSS,仅需关注content
区域的个性化内容。
1.2 条件判断与循环:动态渲染页面元素
Thymeleaf提供了丰富的条件渲染和循环遍历能力,支持复杂逻辑的页面动态展示。
1.2.1 高级条件判断:th:if/th:unless/th:switch
除了基础的th:if="${user != null}"
,还可结合表达式优先级和逻辑运算符实现复杂条件:
<!-- 复杂条件判断 -->
<div th:if="${user != null and user.age >= 18 and user.status == 'ACTIVE'}">
成年活跃用户:<span th:text="${user.name}"></span>
</div>
<!-- th:unless(与th:if相反) -->
<div th:unless="${user == null}">
用户已登录
</div>
<!-- th:switch多条件分支 -->
<div th:switch="${user.role}">
<p th:case="'ADMIN'">管理员权限</p>
<p th:case="'USER'">普通用户权限</p>
<p th:case="*">未知权限</p> <!-- 默认分支 -->
</div>
1.2.2 循环遍历与状态变量
使用th:each
遍历集合时,可通过状态变量获取循环信息(索引、计数、是否首尾等):
<!-- 遍历用户列表,获取状态变量 -->
<ul>
<li th:each="user, userStat : ${users}"
th:classappend="${userStat.odd} ? 'odd-row' : 'even-row'">
<!-- 状态变量属性:index(0开始), count(1开始), size(总元素数), first(是否首元素), last(是否尾元素), odd/even(奇偶行) -->
<span th:text="${userStat.count}">序号</span>:
<span th:text="${user.name}">用户名</span>
<span th:if="${userStat.first}">(首行)</span>
<span th:if="${userStat.last}">(尾行)</span>
</li>
</ul>
状态变量默认命名:若未显式指定(如userStat
),Thymeleaf会自动生成变量名 + Stat
的状态变量(如user.userStat
)。
1.3 表达式高级应用:简化数据绑定与URL处理
Thymeleaf的表达式体系(变量表达式${}
, 选择表达式*{}
, 链接表达式@{}
等)支持多种高级用法,提升模板灵活性。
1.3.1 选择表达式(*{}):简化对象属性访问
当需要频繁访问同一对象的多个属性时,*{}
可简化代码(需配合th:object
指定上下文对象):
<!-- 传统方式:重复写user. -->
<div th:if="${user != null}">
<p>姓名:<span th:text="${user.name}"></span></p>
<p>年龄:<span th:text="${user.age}"></span></p>
<p>邮箱:<span th:text="${user.email}"></span></p>
</div>
<!-- 选择表达式:通过th:object指定对象,后续用*{}访问属性 -->
<div th:if="${user != null}" th:object="${user}">
<p>姓名:<span th:text="*{name}"></span></p>
<p>年龄:<span th:text="*{age}"></span></p>
<p>邮箱:<span th:text="*{email}"></span></p>
</div>
1.3.2 链接表达式(@{…}):动态URL生成与参数拼接
@{}
不仅能生成相对路径,还支持参数拼接、URL重写(自动添加session ID)和路径变量:
<!-- 基础相对路径 -->
<a th:href="@{/home}">首页</a>
<!-- 带查询参数 -->
<a th:href="@{/search(keyword=${query}, page=1)}">搜索</a>
<!-- 生成:/search?keyword=xxx&page=1 -->
<!-- 路径变量(RESTful风格) -->
<a th:href="@{/users/{id}(id=${user.id})}">查看用户</a>
<!-- 生成:/users/123(假设user.id=123) -->
<!-- 绝对路径 -->
<img th:src="@{https://example.com/logo.png}" alt="Logo">
1.4 国际化(i18n)支持:多语言页面无缝切换
Thymeleaf通过#messages
工具对象和Spring的国际化机制,可轻松实现多语言页面。
1.4.1 配置国际化资源
-
创建消息属性文件(放在
src/main/resources/i18n
目录):messages.properties
(默认语言):welcome=欢迎访问我的网站 user.name=用户名 user.age=年龄 greeting=你好,{0}!今天是{1}
messages_en.properties
(英文):welcome=Welcome to My Website user.name=Username user.age=Age greeting=Hello, {0}! Today is {1}
-
Spring Boot配置(
application.yml
):spring: messages: basename: i18n/messages # 资源文件基础路径 encoding: UTF-8 fallback-to-system-locale: false # 不回退到系统默认语言
1.4.2 在模板中使用国际化消息
通过#{key}
获取消息,支持参数化和动态语言切换:
<!-- 基础国际化消息 -->
<h1 th:text="#{welcome}">欢迎</h1>
<!-- 参数化消息({0}、{1}为占位符) -->
<p th:text="#{greeting(${user.name}, #dates.format(today, 'yyyy-MM-dd'))}">
你好,用户!今天是2023-01-01
</p>
<!-- 切换语言链接(通过lang参数指定) -->
<a th:href="@{/home(lang=zh_CN)}">中文</a>
<a th:href="@{/home(lang=en)}">English</a>
1.5 高级表单处理:数据绑定与验证反馈
Thymeleaf与Spring MVC无缝集成,支持表单对象绑定、字段状态显示和验证错误提示。
1.5.1 表单对象绑定(th:object/th:field)
使用th:object
绑定表单对象,th:field
自动生成name
、id
和value
属性(支持双向绑定):
<!-- Controller传递User对象到模板:model.addAttribute("user", new User()); -->
<form th:action="@{/user/save}" th:object="${user}" method="post">
<!-- 文本输入框:自动绑定user.name -->
<div>
<label th:text="#{user.name}">用户名</label>
<input type="text" th:field="*{name}" />
</div>
<!-- 年龄输入框:自动绑定user.age -->
<div>
<label th:text="#{user.age}">年龄</label>
<input type="number" th:field="*{age}" />
</div>
<!-- 单选框:绑定user.gender(值为"male"或"female") -->
<div>
<label>性别</label>
<input type="radio" th:field="*{gender}" th:value="'male'" /> 男
<input type="radio" th:field="*{gender}" th:value="'female'" /> 女
</div>
<!-- 下拉框:绑定user.role -->
<div>
<label>角色</label>
<select th:field="*{role}">
<option th:value="'USER'">普通用户</option>
<option th:value="'ADMIN'">管理员</option>
</select>
</div>
<button type="submit">提交</button>
</form>
1.5.2 表单验证与错误提示
结合Spring Validation注解(如@NotBlank
、@Min
),通过#fields
工具对象显示验证错误:
步骤1:实体类添加验证注解:
public class User {
@NotBlank(message = "{user.name.notblank}") // 引用国际化消息
private String name;
@Min(value = 18, message = "{user.age.min}")
private Integer age;
// getter/setter
}
步骤2:模板中显示错误信息:
<form th:action="@{/user/save}" th:object="${user}" method="post">
<div>
<label th:text="#{user.name}">用户名</label>
<input type="text" th:field="*{name}" />
<!-- 显示name字段错误 -->
<p th:if="${#fields.hasErrors('name')}" th:text="#{${#fields.errors('name')}}">
用户名不能为空
</p>
</div>
<div>
<label th:text="#{user.age}">年龄</label>
<input type="number" th:field="*{age}" />
<!-- 显示age字段错误 -->
<p th:if="${#fields.hasErrors('age')}" th:text="#{${#fields.errors('age')}}">
年龄不能小于18
</p>
</div>
<!-- 显示所有错误(非字段级) -->
<div th:if="${#fields.hasGlobalErrors()}">
<p th:each="error : ${#fields.globalErrors()}" th:text="#{${error.message}}">
全局错误信息
</p>
</div>
<button type="submit">提交</button>
</form>
二、Thymeleaf核心工具对象:简化数据处理
Thymeleaf提供了一系列内置工具对象,封装了常用的Java操作(日期、数字、字符串、集合等),可直接在模板中调用,避免在Controller中处理格式化逻辑。
2.1 日期时间工具:#dates
用于日期格式化、日期比较和获取日期属性(年、月、日等),支持java.util.Date
和java.time.LocalDateTime
(Thymeleaf 3.x+)。
方法示例 | 描述 | 模板使用 | 输出结果 |
---|---|---|---|
format(date, pattern) | 格式化日期 | th:text="${#dates.format(today, 'yyyy-MM-dd HH:mm:ss')}" | 2025-07-16 17:56:11 |
year(date) | 获取年份 | th:text="${#dates.year(today)}" | 2025 |
create(year, month, day) | 创建日期 | th:text="${#dates.create(2025, 7, 16)}" | Wed Jul 16 00:00:00 CST 2025 |
isAfter(date1, date2) | 判断date1是否在date2之后 | th:if="${#dates.isAfter(order.createTime, order.payTime)}" | - |
示例代码:
<div>
<p>当前时间:<span th:text="${#dates.format(now, 'yyyy年MM月dd日 HH:mm:ss')}"></span></p>
<p>订单创建年份:<span th:text="${#dates.year(order.createTime)}"></span></p>
<p th:if="${#dates.isBefore(order.createTime, #dates.addDays(now, -7))}">
订单已超过7天未支付
</p>
</div>
2.2 数字处理工具:#numbers
用于数字格式化(整数、小数、百分比、货币)和数字运算。
方法示例 | 描述 | 模板使用 | 输出结果 |
---|---|---|---|
formatDecimal(num, integerDigits, decimalDigits) | 格式化小数 | th:text="${#numbers.formatDecimal(123.456, 3, 2)}" | 123.46 (3位整数,2位小数) |
formatPercent(num, decimalDigits) | 格式化百分比 | th:text="${#numbers.formatPercent(0.35, 1)}" | 35.0% |
formatCurrency(num) | 格式化货币(依赖本地化) | th:text="${#numbers.formatCurrency(99.99)}" | ¥99.99 (中文环境) |
示例代码:
<div>
<p>商品价格:<span th:text="${#numbers.formatCurrency(product.price)}"></span></p>
<p>折扣率:<span th:text="${#numbers.formatPercent(product.discount, 0)}"></span></p>
<p>评分(保留1位小数):<span th:text="${#numbers.formatDecimal(product.score, 1, 1)}"></span></p>
</div>
2.3 字符串工具:#strings
提供字符串常用操作(判空、拼接、截取、替换、大小写转换等)。
方法示例 | 描述 | 模板使用 | 输出结果 |
---|---|---|---|
isEmpty(str) | 判断是否为空(null或空字符串) | th:if="${#strings.isEmpty(user.nickname)}" | - |
concat(str1, str2) | 拼接字符串 | th:text="${#strings.concat(user.firstName, ' ', user.lastName)}" | 张 三 |
substring(str, start, end) | 截取子串 | th:text="${#strings.substring('HelloWorld', 0, 5)}" | Hello |
toUpperCase(str) | 转大写 | th:text="${#strings.toUpperCase(user.name)}" | ZHANGSAN |
replace(str, old, new) | 替换字符串 | th:text="${#strings.replace(article.content, '旧文本', '新文本')}" | - |
示例代码:
<div>
<p th:if="${#strings.isEmpty(user.nickname)}">
<span th:text="${#strings.defaultString(user.nickname, '匿名用户')}"></span>
</p>
<p>用户名(大写):<span th:text="${#strings.toUpperCase(user.name)}"></span></p>
<p>简介(前20字):<span th:text="${#strings.substring(article.summary, 0, 20)} + '...'"></span></p>
</div>
2.4 集合工具:#lists、#sets、#maps
分别用于List、Set和Map的操作,如判空、获取大小、排序、过滤等。
#lists常用方法:
<div>
<!-- 判断列表是否为空 -->
<p th:if="${#lists.isEmpty(users)}">暂无用户数据</p>
<!-- 获取列表大小 -->
<p>用户总数:<span th:text="${#lists.size(users)}"></span></p>
<!-- 排序(需元素实现Comparable) -->
<ul th:each="user : ${#lists.sort(users)}">
<li th:text="${user.name}"></li>
</ul>
<!-- 截取子列表(前5条) -->
<ul th:each="item : ${#lists.subList(products, 0, 5)}">
<li th:text="${item.name}"></li>
</ul>
</div>
#maps常用方法:
<div>
<!-- 判断Map是否为空 -->
<p th:if="${#maps.isEmpty(configs)}">暂无配置数据</p>
<!-- 获取Map大小 -->
<p>配置项数量:<span th:text="${#maps.size(configs)}"></span></p>
<!-- 判断是否包含key -->
<p th:if="${#maps.containsKey(configs, 'site.title')}">
网站标题:<span th:text="${configs['site.title']}"></span>
</p>
</div>
2.5 其他实用工具对象
- #bools:布尔值处理,如
#bools.isTrue(flag)
、#bools.toInteger(flag)
(true→1,false→0)。 - #arrays:数组操作,如
#arrays.length(array)
、#arrays.contains(array, 'value')
。 - #uris:URL编码,如
th:href="@{/search(keyword=${#uris.escapeQueryParam(query)})}"
(避免URL参数特殊字符问题)。
三、自定义工具对象:扩展Thymeleaf能力
除了内置工具,还可通过Spring Bean定义自定义工具类,让Thymeleaf模板直接调用。
3.1 定义自定义工具类
@Component // 注册为Spring Bean
public class ThymeleafUtils {
// 示例:格式化手机号(138****1234)
public String formatPhone(String phone) {
if (phone == null || phone.length() != 11) {
return phone;
}
return phone.substring(0, 3) + "****" + phone.substring(7);
}
// 示例:计算两个日期相差天数
public long daysBetween(Date start, Date end) {
if (start == null || end == null) {
return 0;
}
return (end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24);
}
}
3.2 在模板中使用自定义工具
通过@beanName.method()
直接调用Spring Bean的方法:
<div>
<p>手机号:<span th:text="${@thymeleafUtils.formatPhone(user.phone)}"></span></p>
<p>会员有效期:<span th:text="${@thymeleafUtils.daysBetween(now, user.vipExpireTime)} + '天'"></span></p>
</div>
总结:Thymeleaf高级特性与工具对象的价值
Thymeleaf的高级特性(模板布局、片段复用、动态表单)解决了页面复用和动态渲染问题,让模板更清晰、可维护;内置工具对象(#dates、#numbers、#strings等)简化了数据处理逻辑,避免在Controller中编写格式化代码;自定义工具则进一步扩展了模板能力,满足项目特定需求。
掌握这些功能后,你将能构建更优雅、高效的Thymeleaf模板,充分发挥其在Spring Boot项目中的优势。建议结合官方文档(Thymeleaf官方指南)深入学习,探索更多高级用法。
参考资料:
- Thymeleaf官方文档:https://www.thymeleaf.org/documentation.html
- Thymeleaf Layout Dialect:https://ultraq.github.io/thymeleaf-layout-dialect/
- Spring Boot + Thymeleaf实战:https://spring.io/guides/gs/serving-web-content/### 2.2 数字工具:#numbers
用于数字格式化(整数、小数、百分比、货币)和数值计算,支持自定义格式。
方法示例 | 描述 | 模板使用 | 输出结果 |
---|---|---|---|
formatDecimal(number, integerDigits, decimalDigits) | 格式化小数 | ${#numbers.formatDecimal(123.456, 3, 2)} | 00123.46 |
formatCurrency(number) | 格式化货币 | ${#numbers.formatCurrency(999.99)} | ¥999.99 |
formatPercent(number, decimalDigits) | 格式化百分比 | ${#numbers.formatPercent(0.123, 1)} | 12.3% |
sequence(from, to, step) | 生成数字序列 | ${#numbers.sequence(1, 5)} | [1,2,3,4,5] |
示例代码:
<div>
<p>商品价格:<span th:text="${#numbers.formatCurrency(product.price)}"></span></p>
<p>折扣率:<span th:text="${#numbers.formatPercent(order.discount, 2)}"></span></p>
<p>评分:<span th:text="${#numbers.formatDecimal(product.rating, 1, 1)}"></span>/5.0</p>
<!-- 生成1-10的序号列表 -->
<ul>
<li th:each="i : ${#numbers.sequence(1, 10)}" th:text="${i}">序号</li>
</ul>
</div>
2.3 字符串工具:#strings
提供字符串常用操作(判空、截取、拼接、替换等),避免模板中复杂的字符串处理逻辑。
方法示例 | 描述 | 模板使用 | 输出结果 |
---|---|---|---|
isEmpty(str) | 判断是否为空字符串 | ${#strings.isEmpty(username)} | true/false |
abbreviate(str, maxLength) | 截断字符串并添加省略号 | ${#strings.abbreviate('Hello World', 8)} | Hello... |
toUpperCase(str) | 转大写 | ${#strings.toUpperCase(name)} | ALICE |
concat(str1, str2) | 拼接字符串 | ${#strings.concat(firstName, ' ', lastName)} | Alice Smith |
contains(str, fragment) | 判断是否包含子串 | ${#strings.contains(desc, 'Thymeleaf')} | true/false |
示例代码:
<div>
<p th:if="${#strings.isEmpty(user.nickname)}">
<span th:text="${#strings.defaultString(user.nickname, '匿名用户')}"></span>
</p>
<p>商品描述:<span th:text="${#strings.abbreviate(product.desc, 50)}"></span></p>
<p th:text="${#strings.concat('欢迎 ', user.name, ' 登录系统')}"></p>
<div th:if="${#strings.containsIgnoreCase(tag, 'spring')}">
<span class="tag">Spring相关</span>
</div>
</div>
2.4 集合工具:#lists、#maps
2.4.1 列表工具(#lists)
处理List集合的常用操作(判空、排序、截取、统计等)。
方法示例 | 描述 | 模板使用 | 输出结果 |
---|---|---|---|
isEmpty(list) | 判断列表是否为空 | ${#lists.isEmpty(users)} | true/false |
size(list) | 获取列表大小 | ${#lists.size(products)} | 10 |
sort(list) | 自然排序 | ${#lists.sort(numbers)} | [1,3,5] |
contains(list, element) | 判断是否包含元素 | ${#lists.contains(roles, 'ADMIN')} | true/false |
示例代码:
<div th:if="${not #lists.isEmpty(products)}">
<p>商品总数:<span th:text="${#lists.size(products)}"></span></p>
<ul>
<li th:each="product : ${#lists.sort(products)}" th:text="${product.name}">商品名称</li>
</ul>
<p th:if="${#lists.contains(products, featuredProduct)}">
包含推荐商品
</p>
</div>
2.4.2 映射工具(#maps)
处理Map集合的键值对操作。
方法示例 | 描述 | 模板使用 | 输出结果 |
---|---|---|---|
size(map) | 获取Map大小 | ${#maps.size(config)} | 5 |
containsKey(map, key) | 判断是否包含键 | ${#maps.containsKey(settings, 'darkMode')} | true/false |
values(map) | 获取所有值 | ${#maps.values(userPreferences)} | [true, 'blue', 16] |
示例代码:
<div th:if="${#maps.size(settings) > 0}">
<p>系统配置项:<span th:text="${#maps.size(settings)}"></span></p>
<div th:if="${#maps.containsKey(settings, 'theme')}">
当前主题:<span th:text="${settings.theme}"></span>
</div>
<ul>
<li th:each="value : ${#maps.values(settings)}" th:text="${value}">配置值</li>
</ul>
</div>
三、综合实战案例:用户管理系统页面
3.1 需求描述
实现一个用户管理页面,包含以下功能:
- 复用公共导航栏和页脚
- 展示用户列表(带分页、奇偶行样式)
- 用户搜索(支持关键词过滤)
- 添加用户表单(带数据验证)
- 国际化支持(中英文切换)
3.2 实现步骤
步骤1:创建基础布局和片段
导航栏片段(fragments/header.html):
<header th:fragment="siteHeader">
<nav class="navbar">
<div class="container">
<a th:href="@{/}" class="logo">UserAdmin</a>
<div class="menu">
<a th:href="@{/users}">用户管理</a>
<a th:href="@{/roles}">角色管理</a>
<a th:href="@{/users(lang=zh_CN)}">中文</a>
<a th:href="@{/users(lang=en)}">English</a>
</div>
</div>
</nav>
</header>
步骤2:用户列表页面(users.html)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layouts/base.html}">
<head>
<title layout:title-pattern="$CONTENT_TITLE">用户管理</title>
</head>
<main layout:fragment="content">
<div class="container">
<div class="search-bar">
<form th:action="@{/users}" method="get">
<input type="text" name="keyword" th:value="${keyword}"
placeholder="#{search.placeholder}">
<button type="submit">#{search.button}</button>
</form>
</div>
<div class="user-table">
<table>
<thead>
<tr>
<th>ID</th>
<th th:text="#{user.name}">用户名</th>
<th th:text="#{user.email}">邮箱</th>
<th th:text="#{user.status}">状态</th>
<th th:text="#{user.registerTime}">注册时间</th>
</tr>
</thead>
<tbody>
<tr th:each="user, stat : ${users}"
th:classappend="${stat.odd} ? 'odd' : 'even'">
<td th:text="${user.id}">1</td>
<td th:text="${user.name}">张三</td>
<td th:text="${user.email}">zhangsan@example.com</td>
<td>
<span th:if="${user.active}" class="status active"
th:text="#{status.active}">启用</span>
<span th:unless="${user.active}" class="status inactive"
th:text="#{status.inactive}">禁用</span>
</td>
<td th:text="${#dates.format(user.registerTime, 'yyyy-MM-dd')}">
2025-01-01
</td>
</tr>
<tr th:if="${#lists.isEmpty(users)}">
<td colspan="5" th:text="#{table.empty}">暂无数据</td>
</tr>
</tbody>
</table>
</div>
<div class="pagination">
<a th:href="@{/users(page=${page-1}, keyword=${keyword})}"
th:unless="${page == 0}">#{pagination.prev}</a>
<span th:text="#{pagination.page(${page+1}, ${totalPages})}">第1页/共5页</span>
<a th:href="@{/users(page=${page+1}, keyword=${keyword})}"
th:unless="${page+1 >= totalPages}">#{pagination.next}</a>
</div>
</div>
</main>
</html>
步骤3:添加用户表单(user-form.html)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorate="~{layouts/base.html}">
<head>
<title layout:title-pattern="$CONTENT_TITLE">添加用户</title>
</head>
<main layout:fragment="content">
<div class="container">
<h2 th:text="#{user.add}">添加用户</h2>
<form th:action="@{/users/save}" th:object="${user}" method="post" class="user-form">
<div class="form-group">
<label th:text="#{user.name}">用户名</label>
<input type="text" th:field="*{name}" class="form-control">
<p th:if="${#fields.hasErrors('name')}" class="error"
th:text="#{${#fields.errors('name')}}">用户名不能为空</p>
</div>
<div class="form-group">
<label th:text="#{user.email}">邮箱</label>
<input type="email" th:field="*{email}" class="form-control">
<p th:if="${#fields.hasErrors('email')}" class="error"
th:text="#{${#fields.errors('email')}}">邮箱格式不正确</p>
</div>
<div class="form-group">
<label th:text="#{user.age}">年龄</label>
<input type="number" th:field="*{age}" class="form-control">
<p th:if="${#fields.hasErrors('age')}" class="error"
th:text="#{${#fields.errors('age')}}">年龄必须大于18</p>
</div>
<div class="form-actions">
<button type="submit" class="btn primary" th:text="#{button.save}">保存</button>
<a th:href="@{/users}" class="btn secondary" th:text="#{button.cancel}">取消</a>
</div>
</form>
</div>
</main>
</html>
四、总结与进阶学习
4.1 核心知识点回顾
本文详细介绍了Thymeleaf的高级特性和工具对象,包括:
- 模板布局:通过片段复用和Layout Dialect实现页面结构复用
- 动态渲染:条件判断、循环遍历及状态变量的高级应用
- 表达式语言:选择表达式、链接表达式的简化用法
- 国际化:多语言配置与消息参数化
- 表单处理:数据绑定、验证反馈与错误提示
- 工具对象:#dates、#numbers、#strings等内置工具的实用方法
4.2 进阶学习资源
- 官方文档:Thymeleaf官方指南
- Layout Dialect:Thymeleaf Layout Dialect
- Spring集成:Spring Boot + Thymeleaf实战
- 性能优化:模板缓存配置、片段表达式预编译
4.3 常见问题解决方案
- 模板渲染慢:启用Thymeleaf缓存(
spring.thymeleaf.cache=true
) - 布局冲突:使用
th:remove="all-but-first"
清理重复元素 - 复杂逻辑处理:避免在模板中写复杂逻辑,通过Controller准备数据
- 国际化乱码:确保消息文件编码为UTF-8,并在配置中显式指定
通过掌握这些高级特性和工具对象,你可以构建更灵活、可维护的Thymeleaf模板,提升Spring Boot项目的视图层开发效率。建议结合实际项目多练手,深入理解Thymeleaf的设计思想。## 补充:Thymeleaf其他实用高级特性
内联文本与JavaScript集成
Thymeleaf支持内联表达式,简化模板中的文本渲染和JavaScript变量绑定:
<!-- 内联文本(等价于th:text) -->
<p>欢迎 [[${user.name}]] 登录系统</p>
<!-- JavaScript变量绑定 -->
<script th:inline="javascript">
const user = /*[[${user}]]*/ {}; // 将Java对象转换为JSON
const baseUrl = /*[[@{/api}]]*/ ''; // 动态生成基础URL
console.log('用户名:', user.name);
console.log('API地址:', baseUrl);
</script>
模板注释与条件注释
Thymeleaf提供两种注释方式,支持开发调试和条件渲染:
<!-- 标准HTML注释:会被浏览器解析,但Thymeleaf会忽略其中的表达式 -->
<!-- 用户ID: ${user.id} -->
<!--/* Thymeleaf解析注释:仅模板引擎可见,不会输出到HTML */-->
<!--/* 这部分内容不会出现在最终HTML中 */-->
<!--[if IE]>
<!-- IE条件注释:仅IE浏览器可见 -->
<link rel="stylesheet" th:href="@{/css/ie-compatible.css}">
<![endif]-->
片段表达式的参数传递与默认值
片段引入时可传递动态参数,并设置默认值:
<!-- 定义带默认参数的片段 -->
<nav th:fragment="breadcrumb(homeText='首页', currentText='当前页')">
<ol>
<li><a th:text="${homeText}" th:href="@{/}"></a></li>
<li class="active" th:text="${currentText}"></li>
</ol>
</nav>
<!-- 引入片段并覆盖参数 -->
<div th:replace="fragments/breadcrumb :: breadcrumb(homeText='控制台', currentText='用户列表')"></div>
模板缓存与性能优化
在Spring Boot中配置Thymeleaf缓存,提升生产环境性能:
# application.yml
spring:
thymeleaf:
cache: true # 生产环境启用缓存
mode: HTML # 使用HTML5模式,避免严格XML校验
encoding: UTF-8
prefix: classpath:/templates/
suffix: .html