1. 简介
本文将深入探讨如何在 Spring MVC 中结合 Thymeleaf 模板引擎,灵活地传递和渲染数据。
我们会以构建一个邮件模板为例,逐步演示如何将 Spring 后端数据注入到前端页面中,并通过 Thymeleaf 进行展示。内容涵盖 Model 属性、请求参数、Session、ServletContext 以及 Spring Bean 等多种数据来源的使用方式和注意事项。
2. 项目初始化
首先,我们需要引入必要的依赖项,确保项目支持 Web 和 Thymeleaf。
✅ 添加 Thymeleaf 启动器:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
✅ 同时引入 Web 模块,用于构建 RESTful 接口:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
这两个依赖是开发 Spring Boot + Thymeleaf 应用的基础组合。
接下来,我们将 Thymeleaf 模板文件存放在 src/main/resources/templates/mvcdata/
目录下。每个示例对应一个独立的 HTML 模板:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<!-- 数据填充区域 -->
</html>
最后,创建核心控制器 EmailController
,用于处理页面跳转和数据准备:
@Controller
public class EmailController {
private ServletContext servletContext;
public EmailController(ServletContext servletContext) {
this.servletContext = servletContext;
}
}
⚠️ 注意:虽然并非所有场景都需要 ServletContext
,但为了演示特定功能,我们在此显式注入它。
3. 使用 Model 属性传递数据
Model 是控制器向视图传递数据最常用的方式之一。适用于需要在请求处理过程中动态组装数据并渲染页面的场景。
方式一:通过方法参数注入 Model
@GetMapping("/email/modelattributes")
public String emailModel(Model model) {
model.addAttribute("emailData", emailData);
return "mvcdata/email-model-attributes";
}
Spring MVC 会在请求到达时自动注入 Model
实例,开发者只需添加属性即可。
在 Thymeleaf 模板中通过 ${}
表达式访问:
<p th:text="${emailData.emailSubject}">邮件主题</p>
方式二:使用 @ModelAttribute
注解预加载
你也可以在控制器中声明一个带 @ModelAttribute
的方法,Spring 会自动将其结果放入 Model 中:
@ModelAttribute("emailModelAttribute")
EmailData emailModelAttribute() {
return emailData;
}
模板中可直接使用该属性名:
<p th:each="emailAddress : ${emailModelAttribute.getEmailAddresses()}">
<span th:text="${emailAddress}"></span>
</p>
✅ 优势:适合跨多个接口共享公共数据(如用户信息、站点配置)
❌ 缺点:过度使用可能导致 Model 膨胀,影响性能
更多关于
Model
、ModelMap
和ModelAndView
的区别,可参考相关进阶文档。
4. 通过请求参数(Request Parameters)传值
当数据来自 URL 查询参数时,可以使用 @RequestParam
注解接收,并在模板中通过 param
对象访问。
基本用法
@GetMapping("/email/requestparameters")
public String emailRequestParameters(
@RequestParam(value = "emailsubject") String emailSubject) {
return "mvcdata/email-request-parameters";
}
模板中通过 param
关键字获取参数值:
<p th:text="${param.emailsubject}"></p>
多值参数处理
支持同名参数多次出现(例如多选收件人):
@GetMapping("/email/requestparameters")
public String emailRequestParameters(
@RequestParam("emailsubject") String emailSubject,
@RequestParam("emailaddress") String emailAddress1,
@RequestParam("emailaddress") String emailAddress2) {
return "mvcdata/email-request-parameters";
}
Thymeleaf 提供两种方式读取:
✅ 方式一:使用 th:each
遍历所有值
<p th:each="emailaddress : ${param.emailaddress}">
<span th:text="${emailaddress}"></span>
</p>
✅ 方式二:通过索引访问数组元素
<p th:text="${param.emailaddress[0]}"></p>
<p th:text="${param.emailaddress[1]}"></p>
⚠️ 注意:param
返回的是字符串数组,即使只有一个值也需注意类型。
5. Session 属性共享数据
若需跨请求保持状态(如登录用户信息),可将数据存入 HttpSession
。
@GetMapping("/email/sessionattributes")
public String emailSessionAttributes(HttpSession session) {
session.setAttribute("emaildata", emailData);
return "mvcdata/email-session-attributes";
}
在 Thymeleaf 中通过 session
对象访问:
<p th:text="${session.emaildata.emailSubject}"></p>
⚠️ 重要提示:自 Thymeleaf 3.1 起,直接访问 session
已被标记为过时,出于安全考虑(防止敏感信息泄露)。
✅ 推荐做法:在控制器层将必要的 session 数据提取后放入 Model
,仅暴露所需字段。
例如:
@GetMapping("/email/session-safe")
public String safeAccess(HttpSession session, Model model) {
EmailData data = (EmailData) session.getAttribute("emaildata");
model.addAttribute("subject", data.getEmailSubject());
return "mvcdata/email-safe";
}
这样更安全、更可控。
6. ServletContext 属性(全局共享)
ServletContext
适合存放应用级别的全局数据(如系统配置、版本号),其生命周期与应用一致。
由于 Thymeleaf 不支持直接访问 ServletContext
中的对象属性,我们需要手动设置每个字段:
@GetMapping("/email/servletcontext")
public String emailServletContext() {
servletContext.setAttribute("emailsubject", emailData.getEmailSubject());
servletContext.setAttribute("emailcontent", emailData.getEmailBody());
servletContext.setAttribute("emailaddress", emailData.getEmailAddress1());
servletContext.setAttribute("emaillocale", emailData.getEmailLocale());
return "mvcdata/email-servlet-context";
}
在模板中通过工具方法获取:
<p th:text="${#servletContext.getAttribute('emailsubject')}"></p>
⚠️ 同样,自 Thymeleaf 3.1 开始,#servletContext
已被弃用。
✅ 替代方案:使用 @Bean
或 @ConfigurationProperties
将全局配置注入 Spring 容器,再通过 bean 方式访问(见下节)。
7. 通过 Spring Bean 注入数据
将数据对象注册为 Spring 容器中的 Bean,是实现全局可访问数据的优雅方式。
@Bean
public EmailData emailData() {
return new EmailData();
}
Thymeleaf 支持通过 @beanName
语法直接调用 Bean 的 getter 方法:
<p th:text="${@emailData.emailSubject}"></p>
✅ 优点:
- 全局可访问
- 支持 DI,便于测试
- 可结合
@ConfigurationProperties
实现配置绑定
❌ 注意事项:
- Bean 必须是
public
的 - 方法需有公开的 getter
- 不建议在模板中执行复杂逻辑,保持视图纯净
8. 总结
本文系统梳理了 Spring MVC 与 Thymeleaf 集成时的五种主要数据传递方式:
方式 | 适用场景 | 是否推荐 | 备注 |
---|---|---|---|
✅ Model 属性 | 请求级数据传递 | ✔️ 强烈推荐 | 最常用、最安全 |
✅ Request 参数 | URL 参数渲染 | ✔️ 推荐 | 注意 XSS 防护 |
⚠️ Session 属性 | 用户会话数据 | △ 限制使用 | Thymeleaf 3.1+ 已弃用直接访问 |
⚠️ ServletContext | 应用级全局数据 | △ 替代为 Bean | 已不推荐 |
✅ Bean 注入 | 全局配置/服务调用 | ✔️ 推荐 | 结合 Spring 容器最佳实践 |
📌 最佳实践建议:
- 优先使用
Model
+ 控制器显式传值 - 避免在模板中直接访问
session
或servletContext
- 全局常量或配置应通过
@Bean
或@ConfigurationProperties
暴露 - 所有输出注意防 XSS,Thymeleaf 默认已做 HTML 转义,无需额外处理
示例代码已托管至 GitHub:https://github.com/example/spring-thymeleaf-demo
踩坑提醒:升级 Thymeleaf 到 3.1+ 时务必检查模板中是否还在使用 session.
或 #servletContext
,否则运行时报错或安全警告。简单粗暴的修复方式就是改用 Model
显式传参。