1. 简介

本教程将带你深入了解 Thymeleaf 中的变量使用方式。我们将通过一个 Spring Boot 示例项目,获取一组 Baeldung 文章列表,并在 Thymeleaf 的 HTML 模板中展示它们。

2. Maven 依赖

要使用 Thymeleaf,我们需要添加以下两个依赖项:

  • spring-boot-starter-thymeleaf
  • spring-boot-starter-web
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

3. Web 控制器

首先,我们创建一个 Web 控制器,其中包含一个 GET 接口,用于返回文章列表页面。

该方法使用 @GetMapping 注解,接收一个 Model 参数。这个 Model 对象用于存放可以在 Thymeleaf 模板中使用的全局变量。在我们的例子中,它仅包含一个变量:文章列表。

Article 类有两个字段:nameurl

public class Article {
    private String name;
    private String url;

    // constructor, getters and setters
}

控制器方法的返回值应为 Thymeleaf 模板的名称,该名称对应 src/main/resources/templates 目录下的 HTML 文件。在我们的例子中,文件路径为:src/main/resources/templates/articles-list.html

下面是控制器的完整代码:

@Controller
@RequestMapping("/api/articles")
public class ArticlesController {

    @GetMapping
    public String allArticles(Model model) {
        model.addAttribute("articles", fetchArticles());
        return "articles-list";
    }

    private List<Article> fetchArticles() {
        return Arrays.asList(
          new Article(
            "Introduction to Using Thymeleaf in Spring",
            "https://www.baeldung.com/thymeleaf-in-spring-mvc"
          ),
          // a few other articles
        );
    }
}

启动应用后,访问地址为:http://localhost:8080/api/articles

4. Thymeleaf 模板

接下来是 Thymeleaf 的 HTML 模板。它是一个标准的 HTML 文件,只需添加 Thymeleaf 的命名空间声明:

<html xmlns:th="http://www.thymeleaf.org">

我们将在后续示例中使用以下基础模板结构,仅替换 <main> 标签中的内容:

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Thymeleaf Variables</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
    <main>
        ...
    </main>
</body>
</html>

5. 定义变量

在 Thymeleaf 模板中定义变量有两种常见方式:

✅ 5.1 遍历时定义变量

在遍历数组时,可以将当前元素作为变量使用:

<div th:each="article : ${articles}">
    <a th:text="${article.name}" th:href="${article.url}"></a>
</div>

这样会生成多个 <div>,每个包含一个 <a> 标签,分别对应文章列表中的每篇文章。

✅ 5.2 使用 th:with 定义新变量

你也可以基于已有变量定义新变量。例如,获取文章列表中的第一个元素:

<div th:with="firstArticle=${articles[0]}">
    <a th:text="${firstArticle.name}" th:href="${firstArticle.url}"></a>
</div>

或者,提取文章名称为一个新变量:

<div th:each="article : ${articles}" th:with="articleName=${article.name}">
    <a th:text="${articleName}" th:href="${article.url}"></a>
</div>

在上面的示例中,${article.name}${articleName} 是等价的。

✅ 5.3 同时定义多个变量

你还可以一次定义多个变量,比如分别提取文章名和链接:

<div th:each="article : ${articles}" th:with="articleName=${article.name}, articleUrl=${article.url}">
    <a th:text="${articleName}" th:href="${articleUrl}"></a>
</div>

6. 变量作用域

✅ 6.1 全局变量

控制器中通过 Model 添加的变量具有全局作用域,可以在模板的任何位置使用。

✅ 6.2 局部变量

在模板中通过 th:with 或循环定义的变量是局部变量,只能在其定义的标签范围内使用。

例如,下面这段代码是 ✅ 正确的:

<div id="firstDiv" th:with="firstArticle=${articles[0]}">
    <a th:text="${firstArticle.name}" th:href="${firstArticle.url}"></a>
</div>

但如果在另一个 <div> 中尝试使用 firstArticle

<div id="firstDiv" th:with="firstArticle=${articles[0]}">
    <a th:text="${firstArticle.name}" th:href="${firstArticle.url}"></a>
</div>
<div id="secondDiv">
    <h2 th:text="${firstArticle.name}"></h2>
</div>

就会抛出如下异常 ❌:

org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'name' cannot be found on null

这是因为 <h2> 标签试图访问一个在其作用域之外的变量。

⚠️ 踩坑提示:如果确实需要跨标签使用变量,建议将这些标签包裹在一个父标签中,并在父标签中定义变量。

7. 修改变量值

在 Thymeleaf 中,你可以在局部作用域中覆盖变量的值:

<div id="mainDiv" th:with="articles = ${ { articles[0], articles[1] } }">
    <div th:each="article : ${articles}">
        <a th:text="${article.name}" th:href="${article.url}"></a>
    </div>
</div>

上面的例子中,我们将 articles 变量重新定义为只包含前两个元素的列表。

⚠️ 注意:在 mainDiv 外部,articles 变量仍然是控制器中传递的原始值。

8. 总结

通过本教程,我们学习了如何在 Thymeleaf 中定义和使用变量,包括作用域控制和变量覆盖等技巧。

如需获取完整源码,可以访问 GitHub 仓库:Spring Thymeleaf 示例代码


原始标题:Thymeleaf Variables