当前位置:首页 > 技术 > 正文内容

Thymeleaf 3.0 官方使用指南中文版

访客 技术 2026年7月6日 1

Thymeleaf 快速入门与实践

1. Thymeleaf 简介

1.1 什么是 Thymeleaf?

Thymeleaf 是一款现代服务器端 Java 模板引擎,专为 Web 和独立环境设计。它能够处理 HTML、XML、JavaScript、CSS 以及纯文本等多种格式。

其核心理念是创建优雅且易于维护的模板。Thymeleaf 采用"自然模板"概念,将业务逻辑注入模板时,不会破坏模板作为设计原型的功能。这促进了设计与开发团队之间的沟通与协作。

Thymeleaf 从设计之初就考虑了对 Web 标准的支持,特别是 HTML5,允许你创建完全符合验证标准的模板。

1.2 支持的模板类型

Thymeleaf 开箱即用支持六种模板模式:

  • HTML
  • XML
  • TEXT
  • JAVASCRIPT
  • CSS
  • RAW

其中,HTML 和 XML 为标记模板模式,TEXT、JAVASCRIPT 和 CSS 为文本模板模式,RAW 则为无操作模式。

HTML 模式:接受任何类型的 HTML 输入,包括 HTML5、HTML4 和 XHTML。不会执行验证或格式检查,会尽可能保留原样。

XML 模式:要求输入必须是格式良好的 XML 代码,否则解析器会抛出异常。

TEXT 模式:允许使用特殊语法处理非标记性质的模板,如纯文本邮件。

JAVASCRIPT 模式:允许在 JS 文件中使用模型数据,并提供专门的转义和脚本处理。

CSS 模式:用于处理 CSS 文件,同样采用文本模式语法。

RAW 模式:不进行任何处理,用于将外部资源原样插入。

1.3 方言:标准方言

Thymeleaf 是一个高度可扩展的模板引擎。处理标记工件的对象称为处理器,而处理器的集合组成了方言。核心库提供了"标准方言",可满足大多数需求。

标准方言的大多数处理器是属性处理器,这使得浏览器能直接显示 HTML 模板文件,因为浏览器会忽略这些额外属性。例如,使用 JSP 的代码可能无法被浏览器直接显示:


<form:inputText name="userName" value="${user.name}" />

而 Thymeleaf 的等价实现,浏览器可以正常显示:


<input type="text" name="userName" value="James Carrot" th:value="${user.name}" />

这种"自然模板"能力,让设计与开发团队能处理同一个模板文件,减少了将原型转换为工作模板的工作量。

2. 示例项目:虚拟杂货店

本文的示例代码可在 Good Thymes Virtual Grocery GitHub 仓库中找到。

2.1 网站架构

演示应用是一个虚拟杂货店网站,它包含简单的模型实体:产品、客户、订单以及产品评论。

服务层包含 ProductService 等对象,用于提供数据。Web 层通过一个过滤器,根据请求 URL 将执行委托给 Thymeleaf 命令。


private boolean process(HttpServletRequest request, HttpServletResponse response)
        throws ServletException {
    try {
        if (request.getRequestURI().startsWith("/css") ||
                request.getRequestURI().startsWith("/images") ||
                request.getRequestURI().startsWith("/favicon")) {
            return false;
        }
        IGTVGController controller = this.application.resolveControllerForRequest(request);
        if (controller == null) {
            return false;
        }
        ITemplateEngine templateEngine = this.application.getTemplateEngine();
        response.setContentType("text/html;charset=UTF-8");
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);
        controller.process(request, response, this.servletContext, templateEngine);
        return true;
    } catch (Exception e) {
        try {
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        } catch (final IOException ignored) {
            // Ignore
        }
        throw new ServletException(e);
    }
}

IGTVGController 接口定义如下:


public interface IGTVGController {
    public void process(
            HttpServletRequest request, HttpServletResponse response,
            ServletContext servletContext, ITemplateEngine templateEngine);
}

2.2 配置模板引擎

GTVGApplication 类中初始化模板引擎:


public class GTVGApplication {
    private final TemplateEngine templateEngine;

    public GTVGApplication(final ServletContext servletContext) {
        super();
        ServletContextTemplateResolver templateResolver = 
                new ServletContextTemplateResolver(servletContext);
        templateResolver.setTemplateMode(TemplateMode.HTML);
        templateResolver.setPrefix("/WEB-INF/templates/");
        templateResolver.setSuffix(".html");
        templateResolver.setCacheTTLMs(Long.valueOf(3600000L));
        templateResolver.setCacheable(true);

        this.templateEngine = new TemplateEngine();
        this.templateEngine.setTemplateResolver(templateResolver);
    }
}

模板解析器

ServletContextTemplateResolver 从 Servlet 上下文中获取模板文件。通过配置前缀和后缀,可以将模板名称转换为实际资源路径。例如,模板名称 "product/list" 会被映射到 /WEB-INF/templates/product/list.html

模板引擎

创建 TemplateEngine 实例并为其设置模板解析器即可。

3. 文本处理

3.1 国际化的欢迎页面

创建首页 /WEB-INF/templates/home.html


<html xmlns:th="http://www.thymeleaf.org">
  <head>
    <title>Good Thymes Virtual Grocery</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" type="text/css" media="all" 
          href="../../css/gtvg.css" th:href="@{/css/gtvg.css}" />
  </head>
  <body>
    <p th:text="#{home.welcome}">Welcome to our grocery store!</p>
  </body>
</html>

这里使用了 th:text 属性和 #{...} 表达式来获取国际化消息。

也可以使用 HTML5 友好的 data- 前缀语法:


<p data-th-text="#{home.welcome}">Welcome to our grocery store!</p>

使用 Context

创建 HomeController 并使用 WebContext


public class HomeController implements IGTVGController {
    public void process(
            final HttpServletRequest request, final HttpServletResponse response,
            final ServletContext servletContext, final ITemplateEngine templateEngine)
            throws Exception {
        WebContext ctx = 
                new WebContext(request, response, servletContext, request.getLocale());
        templateEngine.process("home", ctx, response.getWriter());
    }
}

WebContext 允许访问请求参数、会话和应用程序属性。例如,${param.x}${session.x} 等。

3.2 变量与未转义文本

使用 th:utext 输出未转义的 HTML:


<p th:utext="#{home.welcome}">Welcome to our grocery store!</p>

在控制器中添加变量:


ctx.setVariable("today", dateFormat.format(cal.getTime()));

在模板中显示:


<p>Today is: <span th:text="${today}">13 February 2011</span></p>

4. 标准表达式语法

Thymeleaf 标准表达式支持多种类型:

  • 简单表达式${...}*{...}#{...}@{...}~{...}
  • 字面量:文本、数字、布尔、null、文字标记
  • 运算符:字符串拼接、算术、布尔、比较、条件等

4.1 消息表达式

支持带参数的消息:


home.welcome=¡Bienvenido a nuestra tienda de comestibles, {0}!

<p th:utext="#{home.welcome(${session.user.name})}">Welcome!</p>

4.2 变量表达式

基于 OGNL 表达式访问对象属性:


${person.father.name}
${person['father']['name']}
${personsArray[0].name}
${person.createCompleteNameWithSeparator('-')}

表达式基本对象

  • #ctx:上下文对象
  • #vars:上下文变量
  • #locale:语言环境
  • #request#response#session#servletContext:Web 上下文对象

表达式工具对象

  • #dates#calendars:日期格式化
  • #numbers:数字格式化
  • #strings:字符串操作
  • #lists#sets#maps:集合操作
  • #aggregates:聚合函数

例如,在模板中直接格式化日期:


<span th:text="${#calendars.format(today,'dd MMMM yyyy')}">13 May 2011</span>

4.3 选择表达式

使用 th:object 设置选定对象,然后用 *{...} 访问属性:


<div th:object="${session.user}">
    <p>Name: <span th:text="*{firstName}">Sebastian</span>.</p>
    <p>Surname: <span th:text="*{lastName}">Pepper</span>.</p>
</div>

4.4 链接表达式

使用 @{...} 语法:


<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">view</a>

支持路径变量:


<a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">view</a>

4.5 片段表达式

用于在模板中引用和移动片段:


<div th:insert="~{commons :: main}">...</div>

4.6 文字与运算符

字符串拼接:+|Welcome, ${user.name}!|

条件表达式:${row.even}? 'even' : 'odd'

默认值表达式(Elvis 操作符):*{age}?: '(no age specified)'

无操作令牌 (_):保留原型文本作为默认值。

4.7 数据转换

双括号语法 ${{...}} 调用转换服务:


<td th:text="${{user.lastAccessDate}}">...</td>

5. 设置属性值

5.1 `th:attr` 属性


<form action="subscribe.html" th:attr="action=@{/subscribe}">
    <input type="submit" value="Subscribe!" th:attr="value=#{subscribe.submit}"/>
</form>

5.2 特定属性处理器

更推荐使用特定属性,如 th:valueth:actionth:href 等。


<input type="submit" value="Subscribe!" th:value="#{subscribe.submit}"/>

5.3 追加和前置


<input type="button" value="Do it!" class="btn" th:attrappend="class=${' ' + cssStyle}" />

5.4 布尔属性


<input type="checkbox" name="active" th:checked="${user.active}" />

6. 迭代

6.1 基本用法

使用 th:each


<tr th:each="prod : ${prods}">
    <td th:text="${prod.name}">Onions</td>
    <td th:text="${prod.price}">2.41</td>
</tr>

6.2 迭代状态

状态变量提供 indexcountevenoddfirstlast 等属性:


<tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'">
    ...
</tr>

6.3 惰性数据加载

实现 ILazyContextVariable 接口,可在需要时才加载数据:


context.setVariable("users", new LazyContextVariable<List<User>>() {
    @Override
    protected List<User> loadValue() {
        return databaseRepository.findAllUsers();
    }
});

7. 条件判断

7.1 `th:if` 和 `th:unless`


<a href="comments.html" th:href="@{/product/comments(prodId=${prod.id})}" 
   th:if="${not #lists.isEmpty(prod.comments)}">view</a>

7.2 `th:switch` / `th:case`


<div th:switch="${user.role}">
    <p th:case="'admin'">Admin</p>
    <p th:case="*">Other</p>
</div>

8. 模板布局

8.1 定义和引用片段

使用 th:fragment 定义片段:


<div th:fragment="copy">
    &copy; 2011 The Good Thymes Virtual Grocery
</div>

在另一个模板中通过 th:insertth:replace 引入:


<div th:insert="~{footer :: copy}"></div>

8.2 参数化片段


<div th:fragment="frag (onevar,twovar)">
    <p th:text="${onevar} + ' - ' + ${twovar}">...</p>
</div>

调用时传递参数:


<div th:replace="::frag (${value1},${value2})">...</div>

8.3 灵活布局

片段可以作为参数传递,实现高度灵活的布局:


<head th:fragment="common_header(title,links)">
    <title th:replace="${title}">Default Title</title>
    <th:block th:replace="${links}" />
</head>

调用时使用片段表达式:


<head th:replace="base :: common_header(~{::title},~{::link})">
    <title>Awesome - Main</title>
    <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
</head>

9. 局部变量

使用 th:with 定义局部变量:


<div th:with="firstPer=${persons[0]}">
    <span th:text="${firstPer.name}">Julius Caesar</span>
</div>

10. 属性优先级

Thymeleaf 属性按优先级执行,顺序为:片段包含、遍历、条件判断、变量定义、属性修改、文本替换等。

11. 注释与块

  • 标准注释<!-- ... -->,不会处理。
  • 解析器级注释<!--/* ... */-->,在解析时删除。
  • 原型注释<!--/*/ ... /*/-->,处理时取消注释。
  • th:block:属性容器,执行后消失。

12. 内联

12.1 表达式内联


<p>Hello, [[${session.user.name}]]!</p>
<p>The message is [[${msg}]]</p>

[[...]] 转义输出,[(...)] 不转义。

12.2 JavaScript 内联


<script th:inline="javascript">
    var username = [[${session.user.name}]];
</script>

12.3 CSS 内联


<style th:inline="css">
    .[[${classname}]] { text-align: [[${align}]]; }
</style>

13. 文本模板模式

文本模式 (TEXT、JAVASCRIPT、CSS) 无需标签,但提供 [# ...] 标签语法:


[# th:each="item : ${items}"]
  - [(${item})]
[/]

14. 更多示例页面

14.1 订单列表


<tr th:each="o : ${orders}" th:class="${oStat.odd}? 'odd'">
    <td th:text="${#calendars.format(o.date,'dd/MMM/yyyy')}">13 jan 2011</td>
    <td th:text="${o.customer.name}">Frederic Tomato</td>
    <td th:text="${#aggregates.sum(o.orderLines.{purchasePrice * amount})}">23.32</td>
</tr>

14.2 订单详情


<body th:object="${order}">
    <div th:object="*{customer}">
        <p>Name: <span th:text="*{name}">Frederic Tomato</span></p>
    </div>
</body>

15. 配置详解

15.1 模板解析器

Thymeleaf 提供了多种预置模板解析器,如 ClassLoaderTemplateResolverFileTemplateResolverUrlTemplateResolverStringTemplateResolver。它们支持配置前缀、后缀、编码、缓存等。

可以链式使用多个解析器,设置优先级和前序关系。

15.2 消息解析器

标准消息解析器按语言顺序查找 properties 文件。可以通过 setMessageResolver() 配置。

15.3 转换服务

通过 IStandardConversionService 接口自定义转换。

15.4 日志

Thymeleaf 基于 SLF4J 日志框架,提供配置日志级别和特定类别日志的能力。

16. 模板缓存

Thymeleaf 默认缓存解析后的模板,可通过 setCacheable(false) 禁用。可通过 StandardCacheManager 配置缓存大小。

17. 解耦模板逻辑

通过 .th.xml 文件将逻辑与模板分离,使用 <attr sel="..." th:.../> 在外部注入属性。

附录 A:表达式基本对象

#ctx#locale#request#session#servletContext 等,提供对上下文和 Web 对象的便捷访问。

附录 B:表达式工具对象

#dates#numbers#strings#lists#maps#aggregates 等,提供丰富的实用方法。

附录 C:标记选择器语法

支持类似 CSS 和 XPath 的选择器,如 //div[@class='content']div.content#oneidth:ref 等。

标签: ThymeleafJava

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。