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
类有两个字段:name
和 url
:
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 示例代码。