现在我们已经成功实现了一个简单应用-->打印Hello World在我们的浏览器上面,但是这远远不够,我们的目的是编写一个网站后端!
但是一上来就说要编写一个网站后端听起来就不现实,在实现复杂的后台管理之前,我们先让网站能展示一些内容。比如,从后端获取一个团队成员列表,并在网页上显示出来。这需要用到模板引擎。
Spring Boot 常用的模板引擎:
- Thymeleaf (推荐): 功能强大,与 Spring Boot 集成良好,可以直接在 HTML 文件中嵌入逻辑,并且这些 HTML 文件在浏览器中也能以静态方式打开(便于设计师协作)。
- FreeMarker
- Velocity
- Mustache
本次实战以及教程将用到Thymeleaf。
使用 Thymeleaf 展示动态数据
要使用Thymeleaf,我们需要在依赖中添加它。
首先,我们要给IDEA装上Edit Starters插件,它可以方便我们快速增删依赖。
打开 文件--> 设置 --> 编辑器 --> 插件,然后在Marketplace中搜索Edit Starters,下载该插件。

下载完成之后,我们打开pom.xml,右键视图,点击生成。

选择Edit Starters。


在搜索框中搜索Thymeleaf,然后在Starters栏中选中Thymeleaf,将Thymeleaf添加到我们的Selected依赖列表中。

然后点击OK,这样IDEA就自动帮我们配置好了Thymeleaf依赖。
创建数据模型(Model) - TeamMember.java
我们需要一个类来表示团队成员。
- 在你的主包 (例如 com.NZI.web) 下创建一个新的子包,比如 model。
- 在 model 包下,创建一个名为 TeamMember.java 的类:
// TeamMember.java
package com.NZI.web.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data // Lombok: 自动生成 getter, setter, toString, equals, hashCode
@NoArgsConstructor // Lombok: 自动生成无参构造函数
@AllArgsConstructor // Lombok: 自动生成全参构造函数
public class TeamMember {
private Long id;
private String name;
private String role;
private String bio; // 简介
private String imageUrl; // 成员头像图片URL (暂时先用URL)
}
@Data
: Lombok 注解,它会为所有字段自动生成 getter, setter,以及 equals(), hashCode(), toString() 方法。@NoArgsConstructor
: 生成一个无参数的构造函数。@AllArgsConstructor
: 生成一个包含所有字段的构造函数。
创建控制器 - TeamController.java
我们将创建一个新的控制器来处理与团队成员相关的请求。
- 在你的Controller包下,创建一个名为 TeamController.java 的类:
//TeamController.java
package com.NZI.web.Controller;
import com.NZI.web.model.TeamMember;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.ArrayList;
import java.util.List;
@Controller // 1. 使用 @Controller 而不是 @RestController
public class TeamController {
@GetMapping("/team") // 2. 映射到 /team 路径
public String showTeamPage(Model model) { // 3. 注入 Model 对象
// 4. 创建一些示例数据 (将来会从数据库获取)
List<TeamMember> teamMembers = new ArrayList<>();
teamMembers.add(new TeamMember(1L, "Alice Wonder", "Lead Game Designer", "Loves creating imaginative worlds.", "images/default-avatar.png"));
teamMembers.add(new TeamMember(2L, "Bob The Builder", "Chief Programmer", "Can fix anything, especially code.", "images/default-avatar.png"));
teamMembers.add(new TeamMember(3L, "Carol Artista", "Lead Artist", "Brings characters and environments to life.", "images/default-avatar.png"));
// 5. 将数据添加到 Model 中,使其在模板中可用
model.addAttribute("members", teamMembers); // "members" 是在模板中引用的名字
model.addAttribute("pageTitle", "我们的团队");
return "team"; // 6. 返回模板的名称 (team.html)
}
}
为什么使用 @Controller 而不是 @RestController?
@Controller
: 与 @RestController
不同,@Controller
通常用于返回一个视图(比如 HTML 页面),而不是直接返回数据。Spring Boot 会查找与返回的字符串同名的模板文件。
public String showTeamPage(Model model)
是什么?
- 方法返回 String,这个字符串是模板文件的名称(不带 .html 后缀)。
- Model model: Spring MVC 会自动注入一个 Model 对象。你可以用它来传递数据给视图(模板)。
这里我们硬编码了一些 TeamMember 对象。在后续步骤中,我们会从数据库加载这些数据。
model.addAttribute("members", teamMembers);
是什么?
- 将 teamMembers 列表添加到 Model 中。
- 在模板中,我们可以通过键名 "members" 来访问这个列表。
- 同样,我们添加了一个 "pageTitle"。
return "team";
是什么?
- 这告诉 Spring Boot 去查找名为 team.html 的模板文件。
- 根据默认配置,Spring Boot 会在
src/main/resources/templates/
目录下查找这个文件。
创建 Thymeleaf 模板 - team.html
- 在
src/main/resources/templates/
目录下创建一个名为 team.html 的文件。- 如果 templates 目录不存在,请手动创建它。
- 编辑 team.html 内容如下:
关于HTML模板部分需要掌握HTML基本语法,本教程将不再赘述。
team.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title th:text="${pageTitle}">团队页面</title> <!--使用从Controller传递过来的pageTitle-->
<style>
body { font-family: sans-serif; margin: 20px; background-color: #f4f4f4; }
.container { background-color: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }
h1 { color: #333; }
.team-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 20px; margin-top: 20px; }
.member-card { border: 1px solid #ddd; border-radius: 8px; padding: 15px; background-color: #fff; text-align: center; }
.member-card img { width: 100px; height: 100px; border-radius: 50%; object-fit: cover; margin-bottom: 10px; border: 2px solid #eee; }
.member-card h3 { margin: 10px 0 5px 0; color: #555; }
.member-card p { font-size: 0.9em; color: #777; }
.role { font-style: italic; color: #007bff; font-size: 0.95em; margin-bottom: 5px;}
</style>
</head>
<body>
<div class="container">
<h1 th:text="${pageTitle}">我的团队</h1>
<div class="team-grid" th:if="${not #lists.isEmpty(members)}">
<!--遍历从Controller传递过来的members列表-->
<div class="member-card" th:each="member : ${members}">
<!--图片放在src/main/resources/static/images/目录下-->
<img th:if="${member.imagePath != null}" th:src="@{${'/' + member.imagePath}}" th:alt="${member.name} + ' photo'"/>
<img th:unless="${member.imagePath != null}" th:src="@{/images/default-avatar.png}" alt="Default avatar"/> <!--备用图片-->
<h3 th:text="${member.name}">Member Name</h3>
<p class="role" th:text="${member.role}">Member Role</p>
<p th:text="${member.bio}">Member Bio</p>
</div>
</div>
<div th:if="${#lists.isEmpty(members)}">
<p>没有成员</p>
</div>
</div>
</body>
</html>
Thymeleaf 关键点解释:
xmlns:th="http://www.thymeleaf.org"
: 声明 Thymeleaf 命名空间,这样才能使用 th:* 属性。th:text="${pageTitle}"
: 将 <title> 和 <h1> 标签的内容替换为从 Model 中获取的 pageTitle 变量的值。th:if="${not #lists.isEmpty(members)}"
: 条件渲染。只有当 members 列表不为空时,才显示包含团队成员卡片的 div。#lists 是 Thymeleaf 提供的一个工具类。th:each="member : ${members}"
: 循环遍历 members 列表。对于列表中的每个元素,它会创建一个 member-card div。当前的元素可以在循环内部通过变量 member 来访问。th:text="${member.name}"
,th:text="${member.role}", th:text="${member.bio}"
: 显示 member 对象的属性。th:src="@{${'/' + member.imageUrl}}"
: 设置图片源。- @{...} 是 Thymeleaf 的 URL 表达式。
- 我们的图片会放在 src/main/resources/static/images/ 目录下。例如,如果 member.imageUrl 是 images/alice.jpg,那么最终 URL 会是 /images/alice.jpg。
- 创建图片目录和占位图片:
- 在 src/main/resources/static 目录下创建 images 目录。
- 你可以随意放一些图片进去,比如 alice.jpg, bob.jpg, carol.jpg,或者创建一个 default-avatar.png。
运行
刷新IDEA与浏览器,填入用户名与密码,我们能看到最终成果:

到这里,我们已经创建了一个简单的数据模型 TeamMember,创建了一个 @Controller ,它准备数据并将其传递给 Thymeleaf 模板,创建了一个 HTML 模板 (team.html),使用 Thymeleaf 的指令来动态显示从控制器传递过来的数据。
接下来的任务
硬编码是十分麻烦且低效率的事,而且网站怎么可能都是自己手动添加信息,这时候就得用到数据库和表单了,我们将团队成员信息存储到PostgreSQL数据库中,而不是硬编码。我们还会创建一个表单来添加新的团队成员。
更为重要的是,我们希望用户不会访问到我们的后端,然后哐哐改我们的数据,所以我们需要用到Spring Security这个依赖,保护添加、编辑、删除成员的页面,只允许管理员访问。
Comments NOTHING