1. 为什么今天还在聊Thymeleaf?
如果你是一个Java Web开发者,尤其是使用Spring Boot的,那么“Thymeleaf”这个名字你一定不陌生。但你可能也听过这样的声音:“现在都前后端分离了,谁还用模板引擎啊?” 或者 “Vue/React不香吗,为什么还要学这个?” 作为一个从JSP时代一路走过来的老码农,我想说,Thymeleaf不仅没有过时,反而在特定的场景下,它依然是那个最优雅、最省心的解决方案。今天,我们就抛开那些“非此即彼”的争论,深入聊聊Thymeleaf到底是什么,它解决了哪些实际问题,以及如何把它用得“飞起”。
简单来说,Thymeleaf是一个现代化的、服务于Web和独立环境的Java模板引擎。它的核心卖点是“自然模板”——模板文件本身就是有效的HTML5,可以直接在浏览器里打开预览,然后通过Thymeleaf的属性(如 th:text )在服务器端动态渲染内容。这听起来可能有点抽象,但它的好处是实实在在的:前端设计师和后端开发者可以基于同一份HTML文件协作,无需为了预览效果而启动一个笨重的服务器,极大地提升了开发效率和体验。
那么,哪些场景下Thymeleaf依然是首选呢?首先, 快速原型和内部管理系统 。当你需要快速搭建一个功能完整、带表单、列表、分页的后台管理界面时,用Thymeleaf配合Spring Boot,可能比折腾一套前后端分离的架子要快上好几倍。其次, 服务端渲染的邮件模板、报表导出 。比如,你需要生成一份样式复杂的PDF合同或带图表的数据报告,Thymeleaf结合像 Flying Saucer 这样的PDF渲染库,简直是天作之合。最后,对于 SEO有强需求的、内容为主的网站 ,服务端渲染(SSR)依然是保证内容第一时间被搜索引擎抓取的最佳实践,Thymeleaf在这里扮演着关键角色。
所以,无论你是刚入门Java Web的新手,想找一个直观易懂的视图层技术;还是经验丰富的老手,在寻找一个可靠的报表生成或邮件模板方案,Thymeleaf都值得你花时间深入了解。接下来,我会带你从零开始,不仅学会它的基本语法,更会深入到布局、PDF生成、分页等高级实战技巧,这些都是我踩过无数坑后总结出来的干货。
2. 环境搭建与第一个“Hello Thymeleaf”
理论说再多,不如动手跑一遍。我们以最流行的Spring Boot 3.x为例,搭建一个最简单的Thymeleaf项目。这里我假设你已经有基本的Spring Boot和Maven/Gradle知识。
2.1 项目初始化与依赖引入
首先,通过 Spring Initializr 创建一个新项目,选择:
- Project : Maven
- Language : Java
- Spring Boot : 3.2.x (最新稳定版)
- Dependencies : 添加
Spring Web和Thymeleaf
生成的 pom.xml 中,关键的依赖是这样的:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 其他依赖... -->
</dependencies>
注意 :Spring Boot 3.x 默认集成的Thymeleaf版本是3.1.x,这是一个非常成熟稳定的版本。如果你需要用到一些新特性,可以手动指定版本,但通常不建议,以免出现兼容性问题。
2.2 理解默认约定与目录结构
Spring Boot为Thymeleaf提供了“约定大于配置”的极致体验。默认情况下,你不需要任何配置。模板文件应该放在 src/main/resources/templates/ 目录下,这是Thymeleaf的默认模板解析路径。静态资源(CSS, JS, 图片)则放在 src/main/resources/static/ 目录下。
让我们创建一个简单的控制器 HelloController.java :
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller // 注意是@Controller,不是@RestController
public class HelloController {
@GetMapping("/hello")
public String sayHello(Model model) {
model.addAttribute("message", "Hello, Thymeleaf!");
model.addAttribute("currentTime", java.time.LocalDateTime.now());
return "hello"; // 这对应 templates/hello.html
}
}
关键点解析:
-
@Controller:这个注解表明这个类是一个Web控制器,它的方法返回值通常对应一个视图(HTML页面)的名称。 -
Model参数 :这是一个键值对容器,用于在控制器和视图之间传递数据。我们通过model.addAttribute(key, value)放入数据。 - 返回值
"hello":这个字符串会被Thymeleaf的TemplateResolver解析,自动去寻找templates/hello.html文件。
2.3 创建第一个模板:hello.html
现在,在 src/main/resources/templates/ 下创建 hello.html :
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Thymeleaf入门</title>
</head>
<body>
<h1 th:text="${message}">这里的内容会被替换</h1>
<p>当前时间是:<span th:text="${currentTime}">2023-01-01T00:00:00</span></p>
<p>这是一个静态段落,不会被Thymeleaf处理。</p>
<hr>
<h2>体验“自然模板”</h2>
<p>你现在可以直接在浏览器中打开这个HTML文件(不通过服务器),你会看到“这里的内容会被替换”和“2023-01-01T00:00:00”这些静态文本。这就是设计稿。</p>
<p>当你通过 <a href="/https://localhost:8080/hello" target="_blank">http://localhost:8080/hello</a> 访问时,Thymeleaf引擎会动态地将 <code>th:text</code> 属性中的表达式求值,并替换标签体的内容。</p>
</body>
</html>
核心语法解析:
-
xmlns:th="http://www.thymeleaf.org":在<html>标签中引入Thymeleaf的命名空间。这是必须的,它告诉浏览器和编辑器,以th:开头的属性是Thymeleaf的指令。 -
th:text="${expression}":这是最常用的属性之一。${...}是变量表达式,用于获取上下文中的变量值。th:text会计算表达式的值,并将其设置为该HTML元素的文本内容,同时会转义HTML特殊字符(如<,>),防止XSS攻击。 - 静态后备内容 :在
h1和span标签体内,我们写了“这里的内容会被替换”和“2023-01-01T00:00:00”。这些内容在直接预览时显示,在服务器渲染时被替换。这是“自然模板”的精髓。
启动你的Spring Boot应用,访问 http://localhost:8080/hello ,你会看到动态渲染的页面。而直接用浏览器打开 hello.html 文件,看到的是静态原型。这种无缝切换,正是Thymeleaf提升协作效率的秘密。
3. Thymeleaf核心语法与表达式深度解析
掌握了基本用法后,我们来系统学习Thymeleaf的语法体系。它的强大,很大程度上源于其丰富而灵活的表达式。
3.1 四大核心表达式
Thymeleaf主要有四种表达式,用于在模板的不同位置插入动态内容。
-
变量表达式
${...}这是最常用的表达式,用于访问容器(Model、Session等)中的变量。它支持OGNL(Object-Graph Navigation Language)和Spring EL(Spring Expression Language),因此功能非常强大。<p>用户名:<span th:text="${user.name}">张三</span></p> <p>年龄:<span th:text="${user.age}">25</span></p> <!-- 支持方法调用 --> <p>姓名大写:<span th:text="${user.name.toUpperCase()}">ZHANGSAN</span></p> <!-- 支持条件判断(三元运算符) --> <p>状态:<span th:text="${user.isActive} ? '活跃' : '禁用'">活跃</span></


842

被折叠的 条评论
为什么被折叠?



