帶有彈簧的無反射模板

https://spring.io/blog/2024/03/22/reflectionless-templates-with-spring

Java 開發中,最近出現了一些使用文字模板,但在構建時編譯成 Java 類的庫。他們因此在某種程度上可以宣稱是 “無反射”。此外還帶來了潛在的運行時性能優勢,承諾易於使用,並與 GraalVM 本地映像編譯集成,因此對那些剛開始使用 Spring Boot 3.x 堆棧的人來說非常有趣。我們深入探討了幾個庫(JStachio、Rocker、JTE 和 ManTL),並介紹了如何讓它們運行。

腳本的源代碼位於 GitHub 上,每個模板引擎都有自己的分支。這個示例是故意非常簡單的,並且沒有使用模板引擎的所有功能。重點是如何將它們與 Spring Boot 和 GraalVM 集成。

JStachio

我最喜歡它,因此我將從 JStachio 開始。它非常易於使用,佔用空間非常小,在運行時速度非常快。這些模板是以 Mustache 格式編寫的純文本文件,然後在構建時編譯為 Java 類,並在運行時渲染。

在這個示例中,有一個用於首頁的模板(index.mustache)。它只是打印問候語和訪問者計數:

它使用了一個簡單的“佈局”模板(layout.mustache)。

JStachio APT 處理器將為它找到的每個模板生成一個 Java 類,並使用@JStache 標註來標識源代碼中的模板文件。在這個例子中,我們有:

@JStache(path = “index”)
public class DemoModel {
public String name;
public long visits;

public DemoModel(String name, long visits) {
this.name = name;
this.visits = visits;
}
}

@JStache 標註的路徑屬性是模板文件的名稱,不帶擴展名(查看下面如何連接)。您還可以使用 Java 記錄來進行模型,這很方便,但是其他模板引擎都不支持,所以我們將其省略並使示例更具可比性。

構建配置

要將其編譯為 Java 類,您需要在 pom.xml 中的編譯器插件中添加一些配置:

JStachio 附帶了一些 Spring Boot 集成功能,因此您只需將其添加到類路徑:

控制器

您可以在控制器中使用該模板,例如:

這個控制器返回一個從 DemoModel 構造的 View 。它也可以直接返回 DemoModel,Spring Boot 會自動將其封裝成 JStachioModelView。

JStachio 配置

在 DemoApplication 類中還有全局配置:

和一個指向它的 package-info.java 文件(每個包含@JStache 模型的 Java 包都需要一個):

運行示例

使用 ./mvnw spring-boot:run(或從主方法中的 IDE 運行)運行應用程序,然後您應該在 http://localhost:8080/看到主頁。

服務器在重新編譯時生成的源碼位於 target/generated-sources/annotations 中,您可以在那裡看到 DemoModel 的生成 Java 類:

示例還包括一個測試主要,這樣您就可以使用./mvnw spring-boot:test-run 或 IDE 中的測試主要運行應用程序,而且在 IDE 中進行更改時應用程序將重新啟動。構建時編譯的一個缺點是要強制重新編譯才能看到模板中的更改。IDE 不會自動執行這個動作,因此您可能需要使用另一個工具觸發重新編譯。使用以下命令來強制模型類在模板更改時重新編譯:

inotifywait 命令是一個等待文件在寫入後關閉的工具。它在任何 Linux 發行版上或 Mac 上都很容易安裝和使用。

本地映像

本地映像可以使用./mvnw -P native spring-boot:build-image(或直接使用 native-image 插件)生成,映像在不到 0.1 秒內啟動:

Rocker

Rocker 可以像 JStachio 一樣使用。這些模板是用一種類似於 HTML 的自定義語言編寫的,並帶有額外的 Java 功能(有點像 JSP)。首頁如下(demo.rocker.html):

它導入了 DemoModel 對象 – 這個實現與 JStachio 示例相同。模板還直接參考它的佈局(調用 templates.layout 的靜態方法)。佈局文件是單獨的模板文件(layout.rocker.html):

構建配置

Rocker 需要一個 APT 處理器以及一些手動將生成的源碼添加到構建輸入中的配置,在 pom.xml 中都可以配置:

控制器

控制器實現非常常規 – 它構建一個模型並返回“demo”視圖的名稱:

我們使用“參數”作為特殊模型屬性的命名規則。這是將在稍後看到的 View 實現的一個 View 實現細節。

Rocker 配置

Rocker 沒有自己的 Spring Boot 集成,但實現起來並不難,而且只需要實現一次。示例包含了一個 View 實現,以及一個 ViewResolver 和一些 RockerAutoConfiguration 中的配置:

如果查看 RockerView 實現,您會看到它是 Rocker 模板類的包裝器,並且含有一些反射代碼用於查找模板參數的名稱。這可能會對本地映像造成問題,因此這並不理想,但我們將看到如何在稍後解決它。Rocker 在內部還使用反射將模板參數綁定到模型,因此它並不完全是無反射的。

運行示例

如果使用./mvnw spring-boot:run 運行示例,您會在 http://localhost:8080/看到主頁。生成的源代碼將作為一個 Java 類提出,每個模板位於 target/generated-sources/rocker/中:

本地映像

本地映像需要一些附加配置以允許渲染期間的反射。我們對此進行了幾次嘗試,很快就明顯地看出 Rocker 內部使用了反射,要使其能夠與 GraalVM 一起使用需要付出很大的努力。重新來一天可能是值得的。

JTE

(JTE 示例直接從項目文檔中複製。本文檔中的其他示例只具有這種結構是因為它們反映了這一點。)

與 Rocker 一樣,JTE 具有一個類似於 HTML 的模板語言,並帶有額外的 Java 功能。項目文檔中的模板在 jte 目錄旁邊,所以我們採用了同樣的約定。首頁看起來像這樣(demo.jte):

在這個示例中沒有佈局模板,因為 JTE 不明確支持模板的構成。DemoModel 與我們用於其他示例的類似。

構建配置

在 pom.xml 中,您需要添加 JTE 編譯器插件:

以及一些源代碼和資源的複製:

運行示例

如果使用./mvnw spring-boot:run 運行示例,您將在 http://localhost:8080/看到主頁。生成的源代碼將作為每個模板一個 Java 類提出到 target/generated-sources/jte/中:

.bin 文件是文本模板的高效二進制表示,在運行時需要添加到類路徑。

本地映像

可以通過一些附加配置生成本地映像。我們需要確保.bin 文件是可用的,並且還要確保生成的 Java 類可以被反射:

因此 JTE 並不完全無反射,但它可以很容易地配置來與 GraalVM 本地映像一起使用。

ManTL

ManTL(Manifold 模板語言)是另一個具有類似於 Java 的語法的模板引擎。與其他示例不同,模板在構建時會編譯成 Java 類。首頁看起來像這樣(Demo.html.mtl):

其中 DemoModel 與其他示例中的相同。

構建配置

Manifold 與其他示例有些不同,它使用了 JDK 的編譯器插件,而不是 APT 處理器。在 pom.xml 中的配置有些更加複雜。這是 maven-compiler-plugin:

和運行時依賴:

控制器

這個示例中的控制器看起來更像是 JStachio 的控制器,而不是 Rocker/JTE 的控制器:

這個控制器返回一個由 Demo.render(new DemoModel(“mysterious visitor”, visitsRepository.get()))構造的 StringView。這也可以直接返回 DemoModel,Spring Boot 將自動將其封裝為 StringView。

運行示例

您可以使用./mvnw spring-boot:run 在命令行上構建和運行應用程序,並在 http://localhost:8080/檢查結果。生成的源代碼將作為每個模板一個類和輔助東西提出到 target/classes/templates/中:

ManTL 只能在 IntelliJ 中運行,在 Eclipse、NetBeans 或 VSCode 中根本無法運行。您可能可以在這些 IDE 中運行主方法,但是引用模板的代碼將有編譯錯誤,因為編譯器插件遺失。

本地映像

編譯器插件不受 GraalVM 支持,因此無法將 ManTL 與 GraalVM 本地映像一起使用。

總結

我們在這裡查看的所有模板引擎在文字模板在構建時編譯為 Java 類方面是無反射的。它們都很容易使用和與 Spring 集成,而且它們都具有或可以提供一定程度的 Spring Boot 自動配置。JStachio 是最輕量級且運行時最快的,並且它對 GraalVM 本地映像的支持最好。Rocker 在運行時也非常快,但它內部使用了反射,並且很難讓它與 GraalVM 能夠良好運作。JTE 的配置略微複雜,但在運行時也非常快,並且很容易讓它與 GraalVM 能夠良好運作。ManTL 的配置最為複雜,而且它完全不能與 GraalVM 一起運行。在 IDE IntelliJ 中作為一個插件,ManTL 能很好地運行。如果您想看更多示例,則每個模板引擎都有自己的文檔,請按照上面提供的鏈接。我自己對 JStachio 的工作產生了一些額外的示例,例如 Mustache PetClinic,還有一個 Todo MVC 實現,最初是由 Ollie Drotbohm 製作的,並且適應了各種不同的模板引擎。

達夫‧賽爾

倫敦,2024

via Spring

March 22, 2024 at 10:42PM

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *