Thymeleaf 模板引擎讲解

一、什么是 Thymeleaf?

Thymeleaf 是一个现代化的服务器端 Java 模板引擎,主要用于 Web 和独立环境。

主要特点:

  • 自然模板:可直接在浏览器中打开查看静态效果

  • 与 Spring 完美集成:Spring 官方推荐模板引擎

  • 功能强大:支持各种表达式和标签

  • 可扩展:支持自定义方言

二、基础语法

1. 引入命名空间

html 复制代码
<html xmlns:th="http://www.thymeleaf.org">

2. 常用属性前缀

前缀 作用 示例
th:text 文本替换 <p th:text="${message}"></p>
th:utext 不转义HTML <p th:utext="${htmlContent}"></p>
th:value 设置值 <input th:value="${user.name}">
th:href 链接 <a th:href="@{/user/list}">用户列表</a>
th:src 资源路径 <img th:src="@{/images/logo.png}">
th:each 循环 <tr th:each="user : ${users}">
th:if 条件判断 <div th:if="${user.active}">激活</div>
th:unless 否定条件 <div th:unless="${user.active}">未激活</div>
th:switch 多条件选择 <div th:switch="${user.role}">
th:case 条件分支 <p th:case="'admin'">管理员</p>

三、表达式语法

1. 变量表达式 ${}

html 复制代码
<p th:text="${user.name}">默认文本</p>
<p th:text="${user?.address?.city}">安全导航</p>

2. 选择表达式 *{}

html 复制代码
<div th:object="${user}">
    <p th:text="*{name}"></p>
    <p th:text="*{age}"></p>
</div>

3. 链接表达式 @{}

html 复制代码
<!-- 绝对路径 -->
<a th:href="@{http://www.example.com}">外部链接</a>

<!-- 相对路径 -->
<a th:href="@{/user/list}">用户列表</a>

<!-- 带参数 -->
<a th:href="@{/user/detail(id=${user.id})}">用户详情</a>

<!-- 路径变量 -->
<a th:href="@{/user/{id}/detail(id=${user.id})}">详情</a>

<!-- 多个参数 -->
<a th:href="@{/user/edit(id=${user.id},type=${type})}">编辑</a>

4. 消息表达式 #{} (国际化)

html 复制代码
<p th:text="#{welcome.message}">Welcome</p>
<p th:text="#{user.name(${username})}">Hello, John</p>

5. 片段表达式 ~{}

html 复制代码
<!-- 引入公共片段 -->
<div th:replace="~{fragments/header :: header}"></div>

<!-- 只引入片段的一部分 -->
<div th:replace="~{commons :: sidebar}"></div>

四、常用功能示例

1. 循环遍历

html 复制代码
<table>
    <tr th:each="user, stat : ${users}">
        <td th:text="${stat.index}">序号</td>
        <td th:text="${user.name}">姓名</td>
        <td th:text="${user.email}">邮箱</td>
        <td>
            <span th:text="${stat.odd}? '奇数行' : '偶数行'"></span>
            <span th:text="${stat.first}? '第一行' : ''"></span>
        </td>
    </tr>
</table>

<!-- 循环状态变量 -->
<!-- stat.index:0开始的索引 -->
<!-- stat.count:1开始的计数 -->
<!-- stat.size:总数量 -->
<!-- stat.current:当前对象 -->
<!-- stat.even/odd:是否偶数/奇数 -->
<!-- stat.first/last:是否第一/最后 -->

2. 条件判断

html 复制代码
<!-- if/unless -->
<div th:if="${user.active}">
    用户已激活
</div>
<div th:unless="${user.active}">
    用户未激活
</div>

<!-- switch/case -->
<div th:switch="${user.role}">
    <p th:case="'admin'">管理员</p>
    <p th:case="'user'">普通用户</p>
    <p th:case="*">其他角色</p>
</div>

3. 表单处理

html 复制代码
<form th:action="@{/user/save}" th:object="${user}" method="post">
    <!-- 隐藏字段 -->
    <input type="hidden" th:field="*{id}">
    
    <!-- 文本输入 -->
    <input type="text" th:field="*{name}" 
           class="form-control"
           th:classappend="${#fields.hasErrors('name')} ? 'is-invalid'">
    <div th:if="${#fields.hasErrors('name')}" 
         th:errors="*{name}" class="invalid-feedback"></div>
    
    <!-- 单选按钮 -->
    <input type="radio" th:field="*{gender}" value="M"> 男
    <input type="radio" th:field="*{gender}" value="F"> 女
    
    <!-- 复选框 -->
    <input type="checkbox" th:field="*{agree}"> 同意协议
    
    <!-- 下拉选择 -->
    <select th:field="*{city}">
        <option value="">请选择</option>
        <option th:each="city : ${cities}"
                th:value="${city.code}"
                th:text="${city.name}">
        </option>
    </select>
    
    <!-- 日期选择 -->
    <input type="date" th:field="*{birthday}">
    
    <button type="submit">保存</button>
</form>

4. 日期格式化

html 复制代码
<!-- 使用 #temporals 工具 -->
<p th:text="${#temporals.format(user.createTime, 'yyyy-MM-dd HH:mm')}"></p>
<p th:text="${#temporals.day(user.birthday)}"></p>
<p th:text="${#temporals.month(user.birthday)}"></p>
<p th:text="${#temporals.year(user.birthday)}"></p>

<!-- 在属性中使用 -->
<input type="date" th:value="${#temporals.format(user.birthday, 'yyyy-MM-dd')}">

5. 字符串操作

html 复制代码
<p th:text="${#strings.toUpperCase(user.name)}"></p>
<p th:text="${#strings.toLowerCase(user.name)}"></p>
<p th:text="${#strings.substring(user.name, 0, 3)}"></p>
<p th:text="${#strings.length(user.name)}"></p>
<p th:text="${#strings.replace(user.name, 'a', 'b')}"></p>
<p th:text="${#strings.isEmpty(user.name)}"></p>
<p th:text="${#strings.contains(user.name, 'admin')}"></p>

6. 集合操作

html 复制代码
<p th:text="${#lists.size(userList)}"></p>
<p th:text="${#lists.isEmpty(userList)}"></p>
<p th:text="${#lists.contains(userList, targetUser)}"></p>

<!-- 集合工具 -->
<p th:if="${#lists.contains(user.roles, 'ADMIN')}">有管理员权限</p>

五、布局和片段

1. 定义片段

fragments/header.html:

html 复制代码
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head th:fragment="head(title, css)">
    <meta charset="UTF-8">
    <title th:text="${title}">默认标题</title>
    <link rel="stylesheet" th:href="@{/css/style.css}">
    <th:block th:replace="${css}"></th:block>
</head>

<body>
    <header th:fragment="header">
        <nav>
            <a th:href="@{/}">首页</a>
            <a th:href="@{/user/list}">用户管理</a>
        </nav>
    </header>
    
    <footer th:fragment="footer">
        <p>&copy; 2024 公司名称</p>
    </footer>
</body>
</html>

2. 使用片段

page.html:

html 复制代码
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
      th:replace="~{layouts/base :: layout(~{::head}, ~{::body})}">
<head th:replace="~{fragments/header :: head('用户列表', ~{::link})}">
    <link rel="stylesheet" th:href="@{/css/user.css}">
</head>

<body>
    <div th:replace="~{fragments/header :: header}"></div>
    
    <main>
        <h1>用户列表</h1>
        <!-- 页面内容 -->
    </main>
    
    <div th:replace="~{fragments/header :: footer}"></div>
</body>
</html>

六、实用工具对象

工具 用途 示例
#ctx 上下文信息 ${#ctx.locale}
#vars 所有变量 ${#vars.containsKey('user')}
#locale 本地化信息 ${#locale.language}
#request HttpServletRequest ${#request.getAttribute('foo')}
#response HttpServletResponse ${#response.getContentType()}
#session HttpSession ${#session.getAttribute('user')}
#servletContext ServletContext ${#servletContext.contextPath}
#dates 日期工具(已废弃) 使用 #temporals
#calendars 日历工具 ${#calendars.createNow()}
#numbers 数字格式化 ${#numbers.formatDecimal(price, 1, 2)}
#strings 字符串工具 ${#strings.toUpperCase(name)}
#objects 对象工具 ${#objects.nullSafe(obj, 'default')}
#bools 布尔工具 ${#bools.isTrue(value)}
#arrays 数组工具 ${#arrays.length(array)}
#lists List工具 ${#lists.size(list)}
#sets Set工具 ${#sets.size(set)}
#maps Map工具 ${#maps.size(map)}
#aggregates 聚合工具 ${#aggregates.sum(array)}
#messages 国际化消息 ${#messages.msg('key')}
#ids ID生成 ${#ids.seq('user')}
#conversions 转换服务 ${#conversions.convert(obj, String)}

七、最佳实践

1. 避免在HTML中写太多逻辑

html 复制代码
<!-- 不推荐 -->
<div th:if="${user.active and (user.role == 'ADMIN' or user.role == 'MANAGER')}">
    高级用户
</div>

<!-- 推荐:在控制器中处理复杂逻辑 -->
<div th:if="${isAdvancedUser}">
    高级用户
</div>

2. 使用片段提高复用性

html 复制代码
<!-- 创建通用的表单字段片段 -->
<div th:fragment="input-field(field, label, type='text')">
    <label th:text="${label}"></label>
    <input th:type="${type}" 
           th:field="*{${field}}"
           th:classappend="${#fields.hasErrors(field)} ? 'is-invalid'">
    <div th:if="${#fields.hasErrors(field)}" 
         th:errors="*{${field}}"
         class="invalid-feedback"></div>
</div>

<!-- 使用 -->
<div th:replace="~{fragments/form :: input-field('username', '用户名')}"></div>
<div th:replace="~{fragments/form :: input-field('password', '密码', 'password')}"></div>

3. 安全性考虑

html 复制代码
<!-- 转义HTML防止XSS -->
<p th:text="${userInput}"></p>  <!-- 自动转义 -->
<p th:utext="${trustedHtml}"></p>  <!-- 不转义,谨慎使用 -->

<!-- 在URL中编码参数 -->
<a th:href="@{/search(keyword=${userInput})}">搜索</a>

八、常见问题

1. 表达式不生效

  • 检查是否引入命名空间

  • 检查是否使用正确的表达式语法

  • 检查数据是否确实传入模型

2. 链接错误

html 复制代码
<!-- 错误 -->
<a th:href="/user/list"></a>

<!-- 正确 -->
<a th:href="@{/user/list}"></a>

3. 静态资源无法加载

html 复制代码
<!-- 使用 @{} 表达式 -->
<script th:src="@{/js/main.js}"></script>
<link th:href="@{/css/style.css}" rel="stylesheet">

Thymeleaf 功能非常丰富,以上是最常用的部分。实际开发中可以根据需求查阅官方文档获取更多高级功能。

相关推荐
刘 大 望1 小时前
JVM(Java虚拟机)
java·开发语言·jvm·数据结构·后端·java-ee
超级种码1 小时前
JVM 字节码指令活用手册(基于 Java 17 SE 规范)
java·jvm·python
MrEnginx1 小时前
解决Windows提示无法加载文件 xxx.ps1,因为在此系统上禁止运行脚本
windows
元亓亓亓1 小时前
LeetCode热题100--155. 最小栈--中等
java·算法·leetcode
TG:@yunlaoda360 云老大1 小时前
阿里云国际站代理商 RDS有什么优势呢?
服务器·阿里云·云计算
SadSunset1 小时前
(3)第一个spring程序
java·后端·spring
高山上有一只小老虎1 小时前
小红的双生串
java·算法
TDengine (老段)2 小时前
人力减 60%:时序数据库 TDengine 助力桂冠电力实现 AI 智能巡检
java·大数据·数据库·人工智能·时序数据库·tdengine·涛思数据
Forest_HAHA2 小时前
<14>_Linux高级IO
linux·服务器