本文目录导读:

我来为您提供一个完整的Thymeleaf案例,包括Spring Boot集成和常用功能展示。
项目结构
thymeleaf-demo/
├── pom.xml
├── src/main/java/com/example/thymeleaf/
│ ├── ThymeleafApplication.java
│ ├── controller/
│ │ ├── UserController.java
│ │ └── ProductController.java
│ └── model/
│ ├── User.java
│ └── Product.java
└── src/main/resources/
├── application.yml
└── templates/
├── index.html
├── user/
│ └── user-list.html
└── product/
└── product-list.html
pom.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.14</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>thymeleaf-demo</artifactId>
<version>1.0.0</version>
<name>thymeleaf-demo</name>
<description>Thymeleaf示例项目</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
模型类
User.java
package com.example.thymeleaf.model;
import java.time.LocalDateTime;
public class User {
private Long id;
private String username;
private String email;
private Integer age;
private String role;
private boolean active;
private LocalDateTime createTime;
// 构造器
public User() {}
public User(Long id, String username, String email, Integer age,
String role, boolean active, LocalDateTime createTime) {
this.id = id;
this.username = username;
this.email = email;
this.age = age;
this.role = role;
this.active = active;
this.createTime = createTime;
}
// Getter和Setter
// ... 省略的getter/setter方法
}
Product.java
package com.example.thymeleaf.model;
import java.math.BigDecimal;
public class Product {
private Long id;
private String name;
private String category;
private BigDecimal price;
private Integer stock;
private String description;
public Product() {}
public Product(Long id, String name, String category,
BigDecimal price, Integer stock, String description) {
this.id = id;
this.name = name;
this.category = category;
this.price = price;
this.stock = stock;
this.description = description;
}
// Getter和Setter
// ... 省略的getter/setter方法
}
控制器
UserController.java
package com.example.thymeleaf.controller;
import com.example.thymeleaf.model.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Controller
@RequestMapping("/users")
public class UserController {
// 模拟数据
private static List<User> users = new ArrayList<>(Arrays.asList(
new User(1L, "张三", "zhangsan@example.com", 25, "管理员", true, LocalDateTime.now().minusDays(30)),
new User(2L, "李四", "lisi@example.com", 30, "用户", true, LocalDateTime.now().minusDays(20)),
new User(3L, "王五", "wangwu@example.com", 22, "用户", false, LocalDateTime.now().minusDays(10)),
new User(4L, "赵六", "zhaoliu@example.com", 35, "访客", true, LocalDateTime.now().minusDays(5)),
new User(5L, "钱七", "qianqi@example.com", 28, "用户", false, LocalDateTime.now())
));
// 用户列表
@GetMapping
public String listUsers(Model model) {
model.addAttribute("users", users);
model.addAttribute("totalUsers", users.size());
model.addAttribute("activeUsers", users.stream().filter(User::isActive).count());
return "user/user-list";
}
// 用户详情
@GetMapping("/{id}")
public String userDetail(@PathVariable Long id, Model model) {
User user = users.stream()
.filter(u -> u.getId().equals(id))
.findFirst()
.orElse(null);
model.addAttribute("user", user);
return "user/user-detail";
}
// 添加用户表单
@GetMapping("/add")
public String showAddForm(Model model) {
model.addAttribute("user", new User());
model.addAttribute("roles", Arrays.asList("管理员", "用户", "访客"));
return "user/user-form";
}
// 保存用户
@PostMapping
public String saveUser(@ModelAttribute User user) {
user.setId((long) (users.size() + 1));
user.setCreateTime(LocalDateTime.now());
users.add(user);
return "redirect:/users";
}
}
ProductController.java
package com.example.thymeleaf.controller;
import com.example.thymeleaf.model.Product;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@Controller
@RequestMapping("/products")
public class ProductController {
private static List<Product> products = new ArrayList<>();
static {
products.add(new Product(1L, "笔记本电脑", "电子产品", new BigDecimal("5999.00"), 100, "高性能便携设备"));
products.add(new Product(2L, "无线鼠标", "配件", new BigDecimal("99.50"), 500, "无线连接设备"));
products.add(new Product(3L, "机械键盘", "配件", new BigDecimal("399.00"), 200, "专业游戏键盘"));
products.add(new Product(4L, "显示器", "电子产品", new BigDecimal("1599.99"), 50, "27英寸4K显示器"));
products.add(new Product(5L, "USB扩展坞", "配件", new BigDecimal("199.00"), 300, "多功能接口扩展"));
}
@GetMapping
public String productList(Model model) {
model.addAttribute("products", products);
// 计算统计数据
BigDecimal totalValue = products.stream()
.map(p -> p.getPrice().multiply(BigDecimal.valueOf(p.getStock())))
.reduce(BigDecimal.ZERO, BigDecimal::add);
model.addAttribute("totalProducts", products.size());
model.addAttribute("totalValue", totalValue);
return "product/product-list";
}
}
模板页面
index.html
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">Thymeleaf案例演示</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 10px;
padding: 30px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
h1 {
color: #333;
margin-bottom: 20px;
text-align: center;
}
.menu {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-top: 30px;
}
.menu-item {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
padding: 20px;
border-radius: 8px;
text-decoration: none;
transition: transform 0.3s;
text-align: center;
}
.menu-item:hover {
transform: translateY(-5px);
box-shadow: 0 5px 20px rgba(102, 126, 234, 0.5);
}
.menu-item h3 {
font-size: 24px;
margin-bottom: 10px;
}
.menu-item p {
opacity: 0.9;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<h1>Thymeleaf 案例演示</h1>
<p style="text-align: center; color: #666; margin-bottom: 30px;">
本案例展示了 Thymeleaf 的主要功能:条件判断、循环、表达式、格式化等
</p>
<div class="menu">
<a th:href="@{/users}" class="menu-item">
<h3>👥 用户管理</h3>
<p>浏览用户列表,查看详情,操作示例</p>
</a>
<a th:href="@{/products}" class="menu-item">
<h3>📦 商品管理</h3>
<p>查看商品信息,计算总价值等</p>
</a>
</div>
</div>
</body>
</html>
user-list.html
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">用户列表</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
background: #f5f5f5;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 10px;
box-shadow: 0 5px 20px rgba(0,0,0,0.1);
padding: 30px;
}
h1 {
color: #333;
margin-bottom: 20px;
}
.stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
padding: 20px;
border-radius: 8px;
text-align: center;
}
.stat-card h2 {
font-size: 36px;
margin-bottom: 5px;
}
.stat-card p {
opacity: 0.9;
}
.add-btn {
display: inline-block;
background: #4CAF50;
color: white;
padding: 10px 20px;
border-radius: 5px;
text-decoration: none;
margin-bottom: 20px;
transition: background 0.3s;
}
.add-btn:hover {
background: #45a049;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
th {
background-color: #f2f2f2;
font-weight: bold;
color: #333;
}
tr:hover {
background-color: #f8f9fa;
}
.badge {
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: bold;
}
.badge-success {
background-color: #4CAF50;
color: white;
}
.badge-danger {
background-color: #f44336;
color: white;
}
.btn {
padding: 6px 12px;
margin-right: 5px;
border-radius: 4px;
text-decoration: none;
font-size: 13px;
display: inline-block;
}
.btn-info {
background-color: #2196F3;
color: white;
}
.btn-danger {
background-color: #f44336;
color: white;
}
.back-link {
display: inline-block;
margin-bottom: 20px;
color: #667eea;
text-decoration: none;
}
.empty-state {
text-align: center;
padding: 40px;
color: #999;
}
</style>
</head>
<body>
<div class="container">
<h1>👥 用户列表</h1>
<a th:href="@{/}" class="back-link">← 返回首页</a>
<!-- 统计卡片 -->
<div class="stats">
<div class="stat-card">
<h2 th:text="${totalUsers}">0</h2>
<p>用户总数</p>
</div>
<div class="stat-card">
<h2 th:text="${activeUsers}">0</h2>
<p>活跃用户</p>
</div>
<div class="stat-card">
<h2 th:text="${totalUsers - activeUsers}">0</h2>
<p>非活跃用户</p>
</div>
</div>
<a th:href="@{/users/add}" class="add-btn">➕ 添加用户</a>
<!-- 用户表格 -->
<table>
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>邮箱</th>
<th>年龄</th>
<th>角色</th>
<th>状态</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<!-- 条件判断 -->
<tr th:if="${!#lists.isEmpty(users)}" th:each="user : ${users}"
th:classappend="${userStat.count % 2 == 0 ? 'even' : 'odd'}">
<td th:text="${user.id}">1</td>
<td th:text="${user.username}">张三</td>
<td th:text="${user.email}">email@example.com</td>
<td th:text="${user.age}">25</td>
<!-- 条件判断选择器 -->
<td>
<span th:switch="${user.role}" class="badge"
th:classappend="${user.role == '管理员'} ? 'badge-danger' : ''">
<span th:case="'管理员'" th:text="${user.role}">管理员</span>
<span th:case="'用户'" th:text="${user.role}">用户</span>
<span th:case="*" th:text="${user.role}">其他</span>
</span>
</td>
<td>
<!-- if条件判断 -->
<span th:if="${user.active}" class="badge badge-success">活跃</span>
<span th:if="${!user.active}" class="badge badge-danger">禁用</span>
</td>
<!-- 日期格式化 -->
<td th:text="${#temporals.format(user.createTime, 'yyyy-MM-dd HH:mm')}">2024-01-01 10:00</td>
<td>
<a th:href="@{/users/{id}(id=${user.id})}" class="btn btn-info">查看</a>
<a th:href="@{/users/delete/{id}(id=${user.id})}" class="btn btn-danger">删除</a>
</td>
</tr>
<!-- 空状态 -->
<tr th:if="${#lists.isEmpty(users)}">
<td colspan="8" class="empty-state">
<p style="font-size: 18px; margin-bottom: 10px;">暂无用户数据</p>
<p>点击"添加用户"按钮创建第一个用户</p>
</td>
</tr>
</tbody>
</table>
</div>
</body>
</html>
product-list.html
<!DOCTYPE html>
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">商品列表</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
background: #f5f5f5;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 10px;
box-shadow: 0 5px 20px rgba(0,0,0,0.1);
padding: 30px;
}
h1 {
color: #333;
margin-bottom: 30px;
}
.back-link {
display: inline-block;
margin-bottom: 20px;
color: #667eea;
text-decoration: none;
}
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 20px;
}
.product-card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 20px;
transition: transform 0.3s, box-shadow 0.3s;
}
.product-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 20px rgba(0,0,0,0.1);
}
.product-card .category {
display: inline-block;
padding: 4px 10px;
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border-radius: 20px;
font-size: 12px;
margin-bottom: 10px;
}
.product-card h3 {
font-size: 20px;
margin-bottom: 10px;
color: #333;
}
.product-card .price {
font-size: 24px;
color: #e74c3c;
font-weight: bold;
margin: 10px 0;
}
.product-card .stock {
color: #666;
margin-bottom: 10px;
}
.product-card .description {
color: #888;
font-size: 14px;
line-height: 1.6;
}
.stats-bar {
display: flex;
gap: 30px;
margin-bottom: 30px;
padding: 20px;
background: #f8f9fa;
border-radius: 8px;
}
.stat {
text-align: center;
}
.stat-value {
font-size: 28px;
font-weight: bold;
color: #667eea;
}
.stat-label {
color: #666;
font-size: 14px;
}
.stock-low {
color: #e74c3c;
font-weight: bold;
}
.stock-normal {
color: #2ecc71;
}
.stock-high {
color: #3498db;
}
</style>
</head>
<body>
<div class="container">
<h1>📦 商品列表</h1>
<a th:href="@{/}" class="back-link">← 返回首页</a>
<!-- 统计信息 -->
<div class="stats-bar">
<div class="stat">
<div class="stat-value" th:text="${totalProducts}">0</div>
<div class="stat-label">商品总数</div>
</div>
<div class="stat">
<div class="stat-value" th:text="${#numbers.formatDecimal(totalValue, 1, 2)}">0.00</div>
<div class="stat-label">总价值(元)</div>
</div>
</div>
<!-- 商品网格 -->
<div class="product-grid">
<div class="product-card" th:each="product : ${products}"
th:classappend="${product.stock > 100 ? 'high-stock' : (product.stock > 50 ? 'normal-stock' : 'low-stock')}">
<span class="category" th:text="${product.category}">分类</span>
<h3 th:text="${product.name}">商品名称</h3>
<div class="price" th:text="${#numbers.formatDecimal(product.price, 1, 2)} + ' 元'">¥0.00</div>
<div class="stock" th:classappend="${product.stock < 100 ? 'stock-low' : 'stock-normal'}">
库存:<span th:text="${product.stock}">0</span> 件
</div>
<p class="description" th:text="${#strings.abbreviate(product.description, 50)}">商品描述</p>
<!-- 库存状态条件显示 -->
<div style="margin-top: 10px;">
<span th:if="${product.stock <= 0}" style="color: #e74c3c; font-weight: bold;">🚫 已售罄</span>
<span th:if="${product.stock > 0 and product.stock <= 10}" style="color: #e67e22;">⚠️ 库存紧张</span>
<span th:if="${product.stock > 10 and product.stock <= 50}" style="color: #27ae60;">✅ 库存充足</span>
<span th:if="${product.stock > 50}" style="color: #3498db;">📊 库存充裕</span>
</div>
</div>
</div>
<!-- 表达式运算示例 -->
<div style="margin-top: 30px; padding: 20px; background: #f8f9fa; border-radius: 8px;">
<h3 style="margin-bottom: 15px;">表达式运算示例:</h3>
<ul style="list-style: none;">
<li th:each="product : ${products}">
<span th:text="${product.name}">商品名</span> ×
<span th:text="${product.stock}">库存</span> =
<span th:text="${#numbers.formatDecimal(product.price * product.stock, 1, 2)}">总价</span> 元
</li>
</ul>
</div>
</div>
</body>
</html>
Main Application
package com.example.thymeleaf;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ThymeleafApplication {
public static void main(String[] args) {
SpringApplication.run(ThymeleafApplication.class, args);
}
}
application.yml
server:
port: 8080
spring:
thymeleaf:
prefix: classpath:/templates/
suffix: .html
cache: false
encoding: UTF-8
mode: HTML
运行测试
- 运行
ThymeleafApplication主类 - 访问 http://localhost:8080 查看首页
- 点击"用户管理"查看用户列表
- 点击"商品管理"查看商品列表
功能亮点
- 条件判断:使用
th:if,th:unless,th:switch,th:case - 循环遍历:使用
th:each遍历集合 - 属性操作:
th:href,th:text,th:classappend等 - 内联表达式:, ,
- 日期格式化:使用
#temporals.format() - 数字格式化:使用
#numbers.formatDecimal() - 字符串操作:使用
#strings.abbreviate() - URL链接:使用
th:href="@{/users/{id}(id=${user.id})}" - 样式动态绑定:基于条件的CSS类切换
- 统计聚合:在控制器中计算统计数据
这个案例涵盖了Thymeleaf在Spring Boot项目中的主要功能和使用场景,非常适合学习和参考,您可以根据需要扩展更多功能。