Spring Boot 视图层与模板引擎
介绍 Spring Boot 视图层与模板引擎的核心概念及集成方法。涵盖 Thymeleaf、FreeMarker 和 Velocity 三种主流模板引擎的依赖配置、代码实现及模板编写示例。同时讲解了静态资源管理的常用目录与引用方式,并结合商品展示等实际应用场景演示了完整开发流程。通过本章学习,可掌握在 Spring Boot 项目中选择合适的视图层方案并处理渲染问题。

介绍 Spring Boot 视图层与模板引擎的核心概念及集成方法。涵盖 Thymeleaf、FreeMarker 和 Velocity 三种主流模板引擎的依赖配置、代码实现及模板编写示例。同时讲解了静态资源管理的常用目录与引用方式,并结合商品展示等实际应用场景演示了完整开发流程。通过本章学习,可掌握在 Spring Boot 项目中选择合适的视图层方案并处理渲染问题。

学习目标:掌握 Spring Boot 视图层与模板引擎的核心概念与使用方法,包括 Spring Boot 视图层的基本方法、Spring Boot 与 Thymeleaf/FreeMarker/Velocity 的集成、静态资源管理及实际应用场景。
重点:
Spring Boot 视图层是指使用 Spring Boot 进行 Web 应用开发的方法。
定义:视图层是指使用 Spring Boot 进行 Web 应用开发的方法。
作用:
结论:视图层是实现 Web 页面渲染、数据展示及用户交互的方法。
定义:视图层的常用组件是指 Spring Boot 提供的视图层组件。
组件:
结论:视图层的常用组件包括 Thymeleaf、FreeMarker、Velocity。
Spring Boot 与 Thymeleaf 的集成是最常用的视图层方法之一。
步骤:
示例: pom.xml 文件中的 Thymeleaf 依赖:
<dependencies>
<!-- Web 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Thymeleaf 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- Data JPA 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- H2 数据库依赖 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
application.properties 文件中的 Thymeleaf 配置:
# 服务器端口
server.port=8080
# 数据库连接信息
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
# JPA 配置
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
# H2 数据库控制台
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
# Thymeleaf 配置
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
实体类:
import javax.persistence.*;
@Entity
@Table(name = "product")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String productId;
private String productName;
private double price;
private int sales;
public Product() {}
public Product(String productId, String productName, double price, int sales) {
this.productId = productId;
this.productName = productName;
this.price = price;
this.sales = sales;
}
// Getter 和 Setter 方法
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getProductId() { return productId; }
public void setProductId(String productId) { this.productId = productId; }
public String getProductName() { return productName; }
public void setProductName(String productName) { this.productName = productName; }
public double getPrice() { return price; }
public void setPrice(double price) { this.price = price; }
public int getSales() { return sales; }
public void setSales(int sales) { this.sales = sales; }
@Override
public String toString() {
return "Product{" +
"id=" + id +
", productId='" + productId + '\'' +
", productName='" + productName + '\'' +
", price=" + price +
", sales=" + sales +
'}';
}
}
Repository 接口:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
List<Product> findBySalesGreaterThan(int sales);
}
控制器类:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Controller
@RequestMapping("/products")
public class ProductController {
@Autowired
private ProductRepository productRepository;
@GetMapping("/")
public String getAllProducts(Model model) {
List<Product> products = productRepository.findAll();
model.addAttribute("products", products);
return "products";
}
@PostMapping("/")
public String addProduct(@ModelAttribute Product product) {
productRepository.save(product);
return "redirect:/products/";
}
@GetMapping("/top-selling")
public String getTopSellingProducts(@RequestParam int topN, Model model) {
List<Product> products = productRepository.findBySalesGreaterThan(0);
products.sort((p1, p2) -> p2.getSales() - p1.getSales());
if (products.size() > topN) {
products = products.subList(0, topN);
}
model.addAttribute("products", products);
return "products";
}
}
Thymeleaf 模板文件(src/main/resources/templates/products.html):
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>产品列表</title>
<style>
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
.add-product { margin-bottom: 20px; }
</style>
</head>
<body>
<h1>产品列表</h1>
<div class="add-product">
<form th:action="@{/products/}" method="post" th:object="${product}">
<label>产品 ID:</label><input type="text" th:field="*{productId}" required><br>
<label>产品名称:</label><input type="text" th:field="*{productName}" required><br>
<label>价格:</label><input type="number" th:field="*{price}" step="0.01" required><br>
<label>销量:</label><input type="number" th:field="*{sales}" required><br>
<button type="submit">添加产品</button>
</form>
</div>
<div>
<form th:action="@{/products/top-selling}" method="get">
<label>销量 TOP:</label><input type="number" name="topN" value="3" min="1" required>
<button type="submit">查询</button>
</form>
</div>
<table>
<thead>
<tr><th>ID</th><th>产品 ID</th><th>产品名称</th><th>价格</th><th>销量</th></tr>
</thead>
<tbody>
<tr th:each="product : ${products}">
<td th:text="${product.id}"></td>
<td th:text="${product.productId}"></td>
<td th:text="${product.productName}"></td>
<td th:text="${#numbers.formatDecimal(product.price, 0, 'COMMA', 2, 'POINT')}"></td>
<td th:text="${product.sales}"></td>
</tr>
</tbody>
</table>
</body>
</html>
测试类:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.web.server.LocalServerPort;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ProductApplicationTests {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Test
void contextLoads() {}
@Test
void testHomePage() {
String response = restTemplate.getForObject("http://localhost:" + port + "/products/", String.class);
assertThat(response).contains("产品列表");
}
}
结论:集成 Thymeleaf 的步骤包括添加依赖、配置、创建实体类、Repository、控制器、模板文件及测试。
Spring Boot 与 FreeMarker 的集成是常用的视图层方法之一。
步骤:
示例: pom.xml 文件中的 FreeMarker 依赖:
<dependencies>
<!-- Web 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- FreeMarker 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- Data JPA 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- H2 数据库依赖 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
application.properties 文件中的 FreeMarker 配置:
# 服务器端口
server.port=8080
# 数据库连接信息
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
# JPA 配置
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
# H2 数据库控制台
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
# FreeMarker 配置
spring.freemarker.cache=false
spring.freemarker.prefix=classpath:/templates/
spring.freemarker.suffix=.ftl
实体类、Repository 接口、控制器类与集成 Thymeleaf 的示例相同。
FreeMarker 模板文件(src/main/resources/templates/products.ftl):
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>产品列表</title>
<style>
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
.add-product { margin-bottom: 20px; }
</style>
</head>
<body>
<h1>产品列表</h1>
<div>
<form action="/products/" method="post">
<label>产品 ID:</label><input type="text" name="productId" required><br>
<label>产品名称:</label><input type="text" name="productName" required><br>
<label>价格:</label><input type="number" name="price" step="0.01" required><br>
<label>销量:</label><input type="number" name="sales" required><br>
<button type="submit">添加产品</button>
</form>
</div>
<div>
<form action="/products/top-selling" method="get">
<label>销量 TOP:</label><input type="number" name="topN" value="3" min="1" required>
<button type="submit">查询</button>
</form>
</div>
<table>
<thead>
<tr><th>ID</th><th>产品 ID</th><th>产品名称</th><th>价格</th><th>销量</th></tr>
</thead>
<tbody>
<#list products as product>
<tr>
<td>${product.id}</td>
<td>${product.productId}</td>
<td>${product.productName}</td>
<td>${product.price?string(",###.00")}</td>
<td>${product.sales}</td>
</tr>
</#list>
</tbody>
</table>
</body>
</html>
测试类与 Thymeleaf 示例相同。
结论:集成 FreeMarker 的步骤包括添加依赖、配置、创建实体类、Repository、控制器、模板文件及测试。
Spring Boot 与 Velocity 的集成是常用的视图层方法之一。
步骤:
示例: pom.xml 文件中的 Velocity 依赖:
<dependencies>
<!-- Web 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Velocity 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-velocity</artifactId>
<version>1.5.22.RELEASE</version>
</dependency>
<!-- Data JPA 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- H2 数据库依赖 -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
application.properties 文件中的 Velocity 配置:
# 服务器端口
server.port=8080
# 数据库连接信息
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
# JPA 配置
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
# H2 数据库控制台
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
# Velocity 配置
spring.velocity.cache=false
spring.velocity.prefix=classpath:/templates/
spring.velocity.suffix=.vm
实体类、Repository 接口、控制器类与集成 Thymeleaf 的示例相同。
Velocity 模板文件(src/main/resources/templates/products.vm):
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>产品列表</title>
<style>
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
.add-product { margin-bottom: 20px; }
</style>
</head>
<body>
<h1>产品列表</h1>
<div>
<form action="/products/" method="post">
<label>产品 ID:</label><input type="text" name="productId" required><br>
<label>产品名称:</label><input type="text" name="productName" required><br>
<label>价格:</label><input type="number" name="price" step="0.01" required><br>
<label>销量:</label><input type="number" name="sales" required><br>
<button type="submit">添加产品</button>
</form>
</div>
<div>
<form action="/products/top-selling" method="get">
<label>销量 TOP:</label><input type="number" name="topN" value="3" min="1" required>
<button type="submit">查询</button>
</form>
</div>
<table>
<thead>
<tr><th>ID</th><th>产品 ID</th><th>产品名称</th><th>价格</th><th>销量</th></tr>
</thead>
<tbody>
#foreach ($product in $products)
<tr>
<td>$product.id</td>
<td>$product.productId</td>
<td>$product.productName</td>
<td>$product.price.format("###,###.00")</td>
<td>$product.sales</td>
</tr>
#end
</tbody>
</table>
</body>
</html>
测试类与 Thymeleaf 示例相同。
结论:集成 Velocity 的步骤包括添加依赖、配置、创建实体类、Repository、控制器、模板文件及测试。
Spring Boot 的静态资源管理是视图层的重要组件。
定义:静态资源管理是指使用 Spring Boot 管理静态资源的方法。
作用:
常用静态资源目录:
示例: 创建静态资源文件(src/main/resources/static/css/style.css):
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
h1 {
color: #333;
margin: 20px 0;
}
table {
border-collapse: collapse;
width: 100%;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
.add-product {
margin-bottom: 20px;
}
在 Thymeleaf 模板文件中引用静态资源:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>产品列表</title>
<link rel="stylesheet" th:href="@{/css/style.css}">
</head>
<body>
<h1>产品列表</h1>
<div class="add-product">
<form th:action="@{/products/}" method="post" th:object="${product}">
<label>产品 ID:</label><input type="text" th:field="*{productId}" required><br>
<label>产品名称:</label><input type="text" th:field="*{productName}" required><br>
<label>价格:</label><input type="number" th:field="*{price}" step="0.01" required><br>
<label>销量:</label><input type="number" th:field="*{sales}" required><br>
<button type="submit">添加产品</button>
</form>
</div>
<div>
<form th:action="@{/products/top-selling}" method="get">
<label>销量 TOP:</label><input type="number" name="topN" value="3" min="1" required>
<button type="submit">查询</button>
</form>
</div>
<table>
<thead>
<tr><th>ID</th><th>产品 ID</th><th>产品名称</th><th>价格</th><th>销量</th></tr>
</thead>
<tbody>
<tr th:each="product : ${products}">
<td th:text="${product.id}"></td>
<td th:text="${product.productId}"></td>
<td th:text="${product.productName}"></td>
<td th:text="${#numbers.formatDecimal(product.price, 0, 'COMMA', 2, 'POINT')}"></td>
<td th:text="${product.sales}"></td>
</tr>
</tbody>
</table>
</body>
</html>
结论:静态资源管理是指使用 Spring Boot 管理静态资源的方法,常用静态资源目录包括 src/main/resources/static、src/main/resources/public、src/main/resources/resources、src/main/resources/templates。
在实际开发中,Spring Boot 视图层与模板引擎的应用场景非常广泛,如:
示例:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import javax.persistence.*;
import java.util.List;
// 产品类
@Entity
@Table(name = "product")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String productId;
private String productName;
private double price;
private int sales;
public Product() {}
public Product(String productId, String productName, double price, int sales) {
this.productId = productId;
this.productName = productName;
this.price = price;
this.sales = sales;
}
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getProductId() { return productId; }
public void setProductId(String productId) { this.productId = productId; }
public String getProductName() { return productName; }
public void setProductName(String productName) { this.productName = productName; }
public double getPrice() { return price; }
public void setPrice(double price) { this.price = price; }
public int getSales() { return sales; }
public void setSales(int sales) { this.sales = sales; }
@Override
public String toString() {
return "Product{" +
"id=" + id +
", productId='" + productId + '\'' +
", productName='" + productName + '\'' +
", price=" + price +
", sales=" + sales +
'}';
}
}
// 产品 Repository
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
List<Product> findBySalesGreaterThan(int sales);
}
// 产品控制器
@Controller
@RequestMapping("/products")
public class ProductController {
@Autowired
private ProductRepository productRepository;
@GetMapping("/")
public String getAllProducts(Model model) {
List<Product> products = productRepository.findAll();
model.addAttribute("products", products);
model.addAttribute("product", new Product());
return "products";
}
@PostMapping("/")
public String addProduct(@ModelAttribute Product product) {
productRepository.save(product);
return "redirect:/products/";
}
@GetMapping("/top-selling")
public String getTopSellingProducts(@RequestParam int topN, Model model) {
List<Product> products = productRepository.findBySalesGreaterThan(0);
products.sort((p1, p2) -> p2.getSales() - p1.getSales());
if (products.size() > topN) {
products = products.subList(0, topN);
}
model.addAttribute("products", products);
model.addAttribute("product", new Product());
return "products";
}
}
// 应用启动类
@SpringBootApplication
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
@Autowired
private ProductRepository productRepository;
public void run(String... args) {
// 初始化数据
productRepository.save(new Product("P001", "手机", 1000.0, 100));
productRepository.save(new Product("P002", "电脑", 5000.0, 50));
productRepository.save(new Product("P003", "电视", 3000.0, 80));
productRepository.save(new Product("P004", "手表", 500.0, 200));
productRepository.save(new Product("P005", "耳机", 300.0, 150));
}
}
// 测试类
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class ProductApplicationTests {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate restTemplate;
@Test
void contextLoads() {}
@Test
void testHomePage() {
String response = restTemplate.getForObject("http://localhost:" + port + "/products/", String.class);
assertThat(response).contains("产品列表");
}
}
输出结果:
结论:在实际开发中,Spring Boot 视图层与模板引擎的应用场景非常广泛,需要根据实际问题选择合适的模板引擎。
本章我们学习了 Spring Boot 视图层与模板引擎,包括 Spring Boot 视图层的基本方法、Spring Boot 与 Thymeleaf 的集成、Spring Boot 与 FreeMarker 的集成、Spring Boot 与 Velocity 的集成、Spring Boot 的静态资源管理、Spring Boot 的实际应用场景,学会了在实际开发中处理视图层问题。其中,Spring Boot 视图层的基本方法、Spring Boot 与 Thymeleaf 的集成、Spring Boot 与 FreeMarker 的集成、Spring Boot 与 Velocity 的集成、Spring Boot 的静态资源管理、Spring Boot 的实际应用场景是本章的重点内容。从下一章开始,我们将学习 Spring Boot 的其他组件、微服务等内容。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online