不使用 Spring MVC 的 Thymeleaf

最后更新: 2026-05-10 10:03:32 +0800 +0800
原文以英文撰写。您正在阅读AI翻译版本。

即使不使用 Spring MVC,Thymeleaf 也可以在批处理作业、命令行程序、邮件服务或报表流水线中渲染 HTML。独立应用需要配置模板解析器和 TemplateEngine,提供上下文,并将渲染后的输出写入目标位置。

选择集成级别 #

非 Spring 程序需要 Thymeleaf 核心构件:

xml
<dependency>
  <groupId>org.thymeleaf</groupId>
  <artifactId>thymeleaf</artifactId>
  <version>3.1.3.RELEASE</version>
</dependency>

Spring 应用可以使用 thymeleaf-spring6 和 SpringTemplateEngine,以支持 Spring EL、MessageSource、类型转换和感知 Spring 的链接行为。Spring Boot 的依赖管理应选择匹配的 Thymeleaf 版本。当需要这些集成功能时,spring-boot-starter-thymeleaf 很方便,但运行核心引擎并不需要它,也不要求使用 MVC。

配置解析器和引擎 #

选择与模板所有权相匹配的解析器。ClassLoaderTemplateResolver 适用于打包在 JAR 中的不可变模板;FileTemplateResolver 适用于由运维人员管理的文件:

java
var resolver = new ClassLoaderTemplateResolver();
resolver.setPrefix("reports/");
resolver.setSuffix(".html");
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setCharacterEncoding(StandardCharsets.UTF_8.name());
resolver.setCacheable(true);

var engine = new TemplateEngine();
engine.setTemplateResolver(resolver);

引擎应配置一次,然后共享使用。Thymeleaf 文档将完成配置的 TemplateEngine 说明为线程安全;但在处理开始后更改配置不受支持。对于稳定的生产模板,应启用缓存。只有在开发或运行期间必须重新读取文件时,才应禁用缓存,或配置有效期受限的缓存。

解析器上的字符编码控制模板字节的解码方式。输出 Writer 使用的编码则独立控制生成文件的字节,因此应显式设置两者。

渲染异构报表数据 #

不要在 Java 中构造 HTML,而应暴露具有领域模型形状的值:

java
record WarningRow(String orderId, String message) {}
record DailyReport(List<OrderSummary> orders, List<WarningRow> warnings) {}

var context = new Context(Locale.ENGLISH);
context.setVariable("report", report);
context.setVariable("generatedAt", generatedAt);
engine.process("daily-report", context, writer);

模板可以渲染包含不同行类型的独立部分:

html
<section>
  <h2>Warnings</h2>
  <table>
    <tr th:each="warning : ${report.warnings}">
      <td th:text="${warning.orderId}">order</td>
      <td th:text="${warning.message}">message</td>
    </tr>
  </table>
</section>

th:text 会根据输出模式对文本进行转义。th:utext 会输出未转义的标记,因此不得接收不受信任的内容。HTML 模板模式会解析并写出能够识别 HTML 的标记;只有在目标格式需要不同规则时,才选择 XML、text、JavaScript 或 CSS 模式。

片段和可测试性 #

使用 th:fragment 声明可复用标记,并通过 th:insert 或 th:replace 组合这些标记。片段参数可以使共享表格和页眉不依赖全局变量名。可以对多个解析器排序,并通过可解析模式限制其范围,从而防止范围过宽的文件系统解析器意外遮蔽已打包的模板。

将渲染保持为接收报表模型和 Writer 的纯服务。测试可以使用 StringWriter,通过 HTML 解析器解析结果,并断言结构和转义行为。解析器测试还应验证模板缺失和非 ASCII 内容的情况。

管理输出资源 #

使用显式字符集写入,并关闭 writer:

java
Path temporary = Files.createTempFile(outputDirectory, "daily-", ".html");
try (Writer writer = Files.newBufferedWriter(
        temporary, StandardCharsets.UTF_8,
        StandardOpenOption.TRUNCATE_EXISTING)) {
    engine.process("daily-report", context, writer);
}
Files.move(temporary, destination,
    StandardCopyOption.ATOMIC_MOVE,
    StandardCopyOption.REPLACE_EXISTING);

需要原子移动时,应在目标文件系统中创建临时文件。渲染或上传失败后清理临时路径,并且在处理成功之前不要发布目标文件。对于对象存储,应渲染到有界缓冲区,或通过明确规定重试和部分失败行为的上传 API 进行流式上传。

主要参考资料 #

评论

在 GitHub 上阅读或评论(发表评论需要登录)