即使不使用 Spring MVC,Thymeleaf 也能在批次工作、命令列程式、郵件服務或報表管線中呈現 HTML。獨立應用程式會設定範本解析器和 TemplateEngine,提供內容環境,並將呈現後的輸出寫入目的地。
選擇整合層級 #
非 Spring 程式需要 Thymeleaf 核心構件:
<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 適合由操作人員管理的檔案:
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,而應公開符合領域模型形狀的值:
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);範本可以呈現包含不同資料列型別的獨立區段:
<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:
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 上閱讀或評論(發表評論需要登入)