Thymeleaf 从入门到精通:Spring Boot 模板引擎实战指南

Thymeleaf 是 Spring Boot 官方推荐的模板引擎,相比传统的 JSP,它天然支持 HTML 原生语法(无需编译、可直接在浏览器预览),且与 Spring 生态深度整合,是现代 Java Web 开发中动态页面渲染的首选方案。本文将从基础配置、核心语法、高级特性到实战技巧,全方位讲解 Thymeleaf 的使用。

一、基础配置:Spring Boot 整合 Thymeleaf

1.1 依赖引入

pom.xml 中添加 Thymeleaf 启动器(Spring Boot 会自动管理版本,2.0.1 版本默认集成 Thymeleaf 3.0.9):

XML 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

1.2 核心自动配置原理

Spring Boot 通过 ThymeleafAutoConfiguration 自动配置 Thymeleaf 核心参数,核心配置类 ThymeleafProperties 定义了默认规则:

配置项 默认值 说明
prefix classpath:/templates/ 模板文件存放路径
suffix .html 模板文件后缀
encoding UTF-8 模板编码
cache true(生产)/false(开发) 模板缓存(开发时关闭避免重启)
mode HTML 模板解析模式(支持 HTML5、XML 等)

1.3 自定义配置(application.properties)

开发中建议关闭缓存,可自定义模板路径:

XML 复制代码
# 关闭模板缓存(开发必备)
spring.thymeleaf.cache=false
# 自定义模板前缀(默认 classpath:/templates/)
spring.thymeleaf.prefix=classpath:/templates/views/
# 自定义模板后缀(默认 .html)
spring.thymeleaf.suffix=.html
# 编码格式
spring.thymeleaf.encoding=UTF-8
# 解析模式(HTML5 兼容非严格语法)
spring.thymeleaf.mode=HTML5

1.4 最小化示例

步骤 1:创建模板文件

classpath:/templates/ 下新建 success.html,引入 Thymeleaf 命名空间(核心,否则语法不生效):

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Thymeleaf 示例</title>
</head>
<body>
    <h1 th:text="${welcomeMsg}">默认欢迎语</h1>
</body>
</html>
步骤 2:编写 Controller 传递数据
java 复制代码
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class ThymeleafController {

    @GetMapping("/success")
    public String success(Model model) {
        // 向模板传递数据(Model 本质是请求域)
        model.addAttribute("welcomeMsg", "Hello Thymeleaf!");
        // 返回模板名称(无需后缀,自动拼接 prefix + 名称 + suffix)
        return "success";
    }
}

启动项目访问 http://localhost:8080/success,页面会渲染出 Hello Thymeleaf!

二、核心语法:表达式与标签

2.1 标准表达式(核心)

Thymeleaf 表达式是动态渲染的核心,所有表达式都写在 th:* 属性中,支持以下 5 类核心表达式:

表达式类型 语法 作用
变量表达式 ${...} 读取 Model/Request/Session/Application 中的数据(Spring EL 语法)
选择表达式 *{...} 基于 th:object 选择对象,简化属性访问
国际化表达式 #{...} 读取国际化配置文件(.properties)中的文本
URL 表达式 @{...} 生成绝对 / 相对 URL,自动拼接上下文路径(无需手动加项目名)
片段表达式 ~{...} 引用模板片段(布局复用)
2.1.1 变量表达式 ${}

支持读取不同作用域的数据,优先级:Page(请求域)> Request > Session > Application

html 复制代码
<!-- 读取 Model 中的数据(请求域) -->
<div th:text="${user.username}">默认用户名</div>
<!-- 读取 Session 中的数据 -->
<div th:text="${session.user.age}">默认年龄</div>
<!-- 读取 Application 中的数据 -->
<div th:text="${application.appName}">默认应用名</div>
<!-- 支持复杂表达式(EL 语法) -->
<div th:text="${user.age > 18 ? '成年' : '未成年'}">默认状态</div>
2.1.2 选择表达式 *{}

通过 th:object 指定根对象,后续用 *{} 简化属性访问(等价于 ${对象.属性}):

html 复制代码
<div th:object="${user}">
    <!-- 等价于 ${user.username} -->
    <span th:text="*{username}">用户名</span>
    <!-- 等价于 ${user.age} -->
    <span th:text="*{age}">年龄</span>
    <!-- 支持条件判断 -->
    <span th:text="*{gender == 1 ? '男' : '女'}">性别</span>
</div>
2.1.3 国际化表达式 #{}

步骤 1 :创建国际化配置文件(放在 classpath:/i18n/ 下):

  • messages.properties(默认):welcome=欢迎访问
  • messages_zh_CN.properties(中文):welcome=欢迎访问
  • messages_en_US.properties(英文):welcome=Welcome

步骤 2:配置国际化文件路径:

复制代码
spring.messages.basename=i18n.messages
spring.messages.encoding=UTF-8

步骤 3:模板中使用:

html 复制代码
<!-- 基础使用 -->
<h1 th:text="#{welcome}">默认欢迎语</h1>
<!-- 带参数的国际化(配置文件:greet=你好,{0}!) -->
<h2 th:text="#{greet(${user.username})}">默认问候</h2>
2.1.4 URL 表达式 @{}

解决硬编码 URL 的问题,自动拼接上下文路径(如项目名),支持参数:

html 复制代码
<!-- 相对路径(推荐) -->
<a th:href="@{/user/list}">用户列表</a>
<!-- 带参数的 URL(GET 请求) -->
<a th:href="@{/user/detail(id=${user.id}, name=${user.username})}">用户详情</a>
<!-- 绝对路径 -->
<a th:href="@{http://www.baidu.com}">百度</a>
<!-- 表单提交地址 -->
<form th:action="@{/user/save}" method="post">
    <input type="submit" value="提交">
</form>
2.1.5 片段表达式 ~{}

用于复用模板片段(如页眉、页脚),结合 th:replace/th:include/th:insert 使用(下文详解)。

2.2 表达式支持的语法扩展

Thymeleaf 表达式内部支持丰富的运算和逻辑,满足复杂场景:

类型 语法示例
字面量 文本:'张三'、数字:18、布尔:true/false、空:null
文本操作 拼接:'姓名:' + ${user.name}、替换:` 姓名:${user.name} (更简洁)
算术运算 +、-、*、/、%(如 ${user.age + 1}
布尔运算 and/or/not(如 ${user.age > 18 and user.gender == 1}
比较运算 >、<、>=、<=(gt/lt/ge/le)==、!=(eq/ne)(如 ${user.score ge 60}
条件运算 三元:${user.age > 18 ? '成年' : '未成年'}、默认值:${user.nickname ?: '匿名'}

示例

html 复制代码
<!-- 文本替换(比 + 更简洁) -->
<div th:text="|用户${user.id}:${user.username}|"></div>
<!-- 条件运算 + 默认值 -->
<div th:text="${user.score >= 85 ? '优秀' : (user.score >= 60 ? '及格' : '不及格')}"></div>
<!-- 默认值(变量为空时显示) -->
<div th:text="${user.nickname ?: '未设置昵称'}"></div>

2.3 常用标签(实战高频)

Thymeleaf 通过 th:* 属性覆盖原生 HTML 标签的行为,以下是开发中最常用的标签:

标签 核心作用 实战示例
th:text 替换文本(转义 HTML) <p th:text="${user.desc}">默认描述</p><h1> 会被转义为 &lt;h1&gt;
th:utext 替换文本(不转义 HTML) <p th:utext="${user.htmlDesc}">默认描述</p><h1> 会渲染为标题)
th:each 循环遍历(集合 / 数组) <tr th:each="u, stat : ${userList}">(stat 是状态变量)
th:if 条件渲染(为 true 时显示) <div th:if="${user.age > 18}">成年</div>
th:unless 条件渲染(为 false 时显示) <div th:unless="${user.age > 18}">未成年</div>
th:switch/th:case 多分支判断 <div th:switch="${user.role}"><p th:case="admin">管理员</p></div>
th:value 设置表单元素 value <input th:value="${user.username}" />
th:selected 设置下拉框选中状态 <option th:selected="${u.id eq selectedId}">${u.name}</option>
th:class 设置 CSS 类名(支持动态判断) <div th:class="${user.age > 18 ? 'adult' : 'teen'}">
th:src 设置图片 / 脚本路径(结合 @{}) <img th:src="@{/img/avatar.png}" />
th:onclick 设置点击事件(支持动态参数) <button th:onclick="editUser(${user.id})">编辑</button>
th:fragment 定义模板片段(复用) <div th:fragment="header">页眉</div>
th:replace 替换标签引入片段(推荐) <div th:replace="~{common :: header}"></div>
重点标签详解:
1. th:each 循环(带状态变量)

th:each 遍历集合时,可添加状态变量(stat),包含以下常用属性:

  • index:索引(从 0 开始)
  • count:计数(从 1 开始)
  • size:集合总大小
  • even/odd:是否偶数 / 奇数行(用于隔行变色)
  • first/last:是否第一个 / 最后一个元素

示例

html 复制代码
<table border="1">
    <tr>
        <th>序号</th>
        <th>用户名</th>
        <th>密码</th>
    </tr>
    <tr th:each="u, stat : ${uList}" th:class="${stat.odd ? 'odd-row' : 'even-row'}">
        <td th:text="${stat.count}"></td>
        <td th:text="${u.username}"></td>
        <td th:text="${u.password}"></td>
    </tr>
</table>
<style>
    .odd-row { background-color: #f5f5f5; }
    .even-row { background-color: #ffffff; }
</style>
2. th:replace/th:include/th:insert(布局复用)

三者均用于引入模板片段,核心区别:

  • th:replace:替换整个标签(推荐,语义最清晰)
  • th:include:引入片段的内容(已废弃)
  • th:insert:将片段插入到当前标签内部

步骤 1 :定义片段(classpath:/templates/common/fragments.html):

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<body>
    <!-- 定义页眉片段 -->
    <div th:fragment="header">
        <h1>网站页眉</h1>
        <nav>
            <a th:href="@{/home}">首页</a> |
            <a th:href="@{/user}">用户中心</a>
        </nav>
    </div>

    <!-- 定义页脚片段(带参数) -->
    <div th:fragment="footer(copyright)">
        <p th:text="${copyright}">默认版权信息</p>
    </div>
</body>
</html>

步骤 2:引入片段:

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>布局示例</title>
</head>
<body>
    <!-- 引入页眉(无参数) -->
    <div th:replace="~{common/fragments :: header}"></div>

    <div>页面主体内容</div>

    <!-- 引入页脚(传参数) -->
    <div th:replace="~{common/fragments :: footer('2026 © 我的网站')}"></div>
</body>
</html>

三、高级特性(实战必备)

3.1 作用域操作

Thymeleaf 支持直接操作 Request/Session/Application 作用域,无需通过 Controller:

html 复制代码
<!-- 设置 Request 域 -->
<div th:with="requestMsg='请求域消息'"></div>
<!-- 读取 Request 域 -->
<div th:text="${#request.getAttribute('requestMsg')}"></div>

<!-- 设置 Session 域 -->
<div th:with="${session.sessionMsg = '会话域消息'}"></div>
<!-- 读取 Session 域 -->
<div th:text="${session.sessionMsg}"></div>

<!-- 设置 Application 域 -->
<div th:with="${application.appMsg = '应用域消息'}"></div>
<!-- 读取 Application 域 -->
<div th:text="${application.appMsg}"></div>

3.2 工具类(# 前缀)

Thymeleaf 提供了丰富的内置工具类,覆盖字符串、日期、集合等操作,无需在 Controller 处理:

工具类 作用 示例
#strings 字符串操作 #strings.isEmpty(user.name)#strings.toUpperCase(user.name)
#dates 日期格式化 #dates.format(user.birthday, 'yyyy-MM-dd')
#numbers 数字格式化 #numbers.formatDecimal(user.score, 1, 2)(保留 2 位小数)
#lists 集合操作 #lists.isEmpty(userList)#lists.size(userList)
#objects 对象操作 #objects.nullSafe(user.nickname, '匿名')

示例

html 复制代码
<!-- 字符串判空 + 转大写 -->
<div th:text="${#strings.isEmpty(user.username) ? '无用户名' : #strings.toUpperCase(user.username)}"></div>

<!-- 日期格式化(避免前端处理时间戳) -->
<div th:text="${#dates.format(user.createTime, 'yyyy-MM-dd HH:mm:ss')}"></div>

<!-- 数字格式化(保留 2 位小数) -->
<div th:text="${#numbers.formatDecimal(user.score, 0, 2)}"></div>

<!-- 集合判空 -->
<div th:if="${#lists.isEmpty(userList)}">暂无数据</div>

3.3 表单处理(数据绑定)

Thymeleaf 支持与 Spring MVC 表单绑定,自动回显数据、显示校验错误:

步骤 1:编写实体类
java 复制代码
public class UserForm {
    private String username;
    private String password;
    private Integer age;

    // getter/setter 省略
}
步骤 2:Controller 传递表单对象
java 复制代码
@GetMapping("/form")
public String form(Model model) {
    // 初始化表单对象(用于回显)
    UserForm form = new UserForm();
    form.setUsername("默认用户名");
    model.addAttribute("userForm", form);
    return "form";
}

@PostMapping("/form/save")
public String save(@ModelAttribute UserForm userForm) {
    // 处理表单提交
    System.out.println(userForm);
    return "success";
}
步骤 3:模板中绑定表单
html 复制代码
<form th:action="@{/form/save}" th:object="${userForm}" method="post">
    <!-- 绑定用户名 -->
    <div>
        用户名:<input type="text" th:field="*{username}" />
    </div>
    <!-- 绑定密码 -->
    <div>
        密码:<input type="password" th:field="*{password}" />
    </div>
    <!-- 绑定年龄 -->
    <div>
        年龄:<input type="number" th:field="*{age}" />
    </div>
    <button type="submit">提交</button>
</form>

th:field="*{username}" 等价于 name="username" value="${userForm.username}",自动完成数据绑定和回显。

3.4 异常处理与错误页面

Spring Boot 中可结合 Thymeleaf 实现自定义错误页面:

  1. classpath:/templates/error/ 下创建 404.html500.html(Spring Boot 会自动识别);
  2. 模板中可通过 ${#request.getAttribute('javax.servlet.error.status_code')} 获取错误码。

示例(404.html)

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>404</title>
</head>
<body>
    <h1>页面找不到啦 😭</h1>
    <p>错误码:<span th:text="${#request.getAttribute('javax.servlet.error.status_code')}"></span></p>
    <a th:href="@{/home}">返回首页</a>
</body>
</html>

四、实战技巧与避坑指南

4.1 开发调试技巧

  1. 关闭缓存 :开发时务必设置 spring.thymeleaf.cache=false,否则修改模板后需重启项目;
  2. 静态资源访问 :Thymeleaf 模板中引用 CSS/JS/ 图片,需用 @{/static/css/style.css}(Spring Boot 默认静态资源路径 classpath:/static/);
  3. 热部署 :结合 spring-boot-devtools 实现模板修改后自动刷新(无需重启)。

4.2 常见坑点

  1. 命名空间缺失 :忘记加 xmlns:th="http://www.thymeleaf.org",导致表达式不生效;
  2. URL 路径错误 :硬编码 URL 未用 @{},导致项目部署后上下文路径不一致;
  3. 文本转义 :需要渲染 HTML 时用 th:utext,而非 th:text(否则 HTML 标签被转义);
  4. 循环状态变量th:each 的状态变量是可选的,但需注意顺序(变量名在前,状态变量在后)。

4.3 性能优化

  1. 生产环境开启缓存spring.thymeleaf.cache=true,减少模板解析开销;
  2. 模板片段复用 :通过 th:fragment 复用页眉、页脚等公共部分,减少重复代码;
  3. 避免复杂表达式:复杂逻辑尽量在 Controller 处理,模板只负责渲染。

五、完整实战代码

5.1 实体类(User.java)

java 复制代码
public class User {
    private String username;
    private String password;
    private Integer age;
    private Integer score;
    private Integer gender;
    private Date birthday;

    // getter/setter 省略
}

5.2 Controller(ThymeleafDemoController.java)

java 复制代码
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Controller
public class ThymeleafDemoController {

    @GetMapping("/demo")
    public String demo(Model model) {
        // 单个用户
        User user = new User();
        user.setUsername("张三");
        user.setPassword("123456");
        user.setAge(20);
        user.setScore(88.5);
        user.setGender(1);
        user.setBirthday(new Date());

        // 用户列表
        List<User> userList = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            User u = new User();
            u.setUsername("用户" + i);
            u.setPassword("pwd" + i);
            u.setAge(18 + i);
            u.setScore(60 + i * 5);
            u.setGender(i % 2);
            u.setBirthday(new Date());
            userList.add(u);
        }

        model.addAttribute("user", user);
        model.addAttribute("userList", userList);
        return "demo";
    }
}

5.3 模板(demo.html)

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Thymeleaf 实战示例</title>
    <style>
        .odd { background-color: #f5f5f5; }
        .even { background-color: #ffffff; }
    </style>
</head>
<body>
    <h1>单个用户信息</h1>
    <div th:object="${user}">
        <p>用户名:<span th:text="*{username}"></span></p>
        <p>年龄:<span th:text="*{age}"></span>(<span th:text="*{age > 18 ? '成年' : '未成年'}"></span>)</p>
        <p>性别:<span th:switch="*{gender}">
            <span th:case="1">男</span>
            <span th:case="0">女</span>
        </span></p>
        <p>分数:<span th:text="${#numbers.formatDecimal(user.score, 0, 2)}"></span></p>
        <p>生日:<span th:text="${#dates.format(user.birthday, 'yyyy-MM-dd')}"></span></p>
    </div>

    <hr>

    <h1>用户列表</h1>
    <table border="1" width="80%">
        <tr>
            <th>序号</th>
            <th>用户名</th>
            <th>年龄</th>
            <th>分数等级</th>
        </tr>
        <tr th:each="u, stat : ${userList}" th:class="${stat.odd ? 'odd' : 'even'}">
            <td th:text="${stat.count}"></td>
            <td th:text="${u.username}"></td>
            <td th:text="${u.age}"></td>
            <td th:text="${u.score >= 85 ? '优秀' : (u.score >= 60 ? '及格' : '不及格')}"></td>
        </tr>
    </table>
</body>
</html>

六、总结

  1. 核心基础 :Thymeleaf 与 Spring Boot 整合只需引入依赖,默认配置即可使用,模板放在 classpath:/templates/ 下,需引入 xmlns:th 命名空间;
  2. 核心语法 :掌握 ${}(变量)、*{}(选择)、@{}(URL)、~{}(片段)四类表达式,以及 th:each/th:if/th:replace 高频标签;
  3. 实战技巧 :利用内置工具类(#strings/#dates)简化前端处理,通过模板片段实现布局复用,生产环境开启缓存优化性能。

Thymeleaf 的核心优势是 "自然模板"(可直接在浏览器打开预览)和 Spring 生态深度整合,掌握以上内容足以应对 90% 以上的 Web 开发场景。

相关推荐
焦糖玛奇朵婷2 小时前
就医陪诊小程序|从软件开发视角看实用度✨
java·大数据·jvm·算法·小程序
Yvonne爱编码2 小时前
深入剖析 Java 中的深拷贝与浅拷贝:原理、实现与最佳实践
java·开发语言
是三好2 小时前
Spring全家桶
java·后端·spring
西门吹雪分身2 小时前
JUC之线程中断
java
CSD资源分享2 小时前
Claude Code 国内API配置完整指南
java·windows·claude·claude code
索荣荣2 小时前
Java关键字终极指南:从入门到精通
java·开发语言
砚边数影2 小时前
线性回归实战(一):房价预测数据集入库KingbaseES,表结构设计
java·数据库·人工智能·深度学习·机器学习·线性回归·金仓数据库
czlczl200209252 小时前
工作流 Flowable 全流程
java·spring boot·后端
李少兄2 小时前
IntelliJ IDEA 全局搜索完全指南:从高效使用到快捷键失效排查
java·intellij-idea·策略模式