構建

本節介紹如何構建 Spring Shell 應用。

Starter 依賴

  1. Spring Shell Starter 依賴

名稱 描述

spring-shell-starter

基礎 Spring Shell 模組

spring-shell-starter-jansi

使用 JLine jansi 提供者

spring-shell-starter-jni

使用 JLine jni 提供者

spring-shell-starter-jna

使用 JLine jna 提供者

spring-shell-starter-ffm

使用 JLine ffm 提供者 (需要 JDK22+)

spring-shell-starter-test

Spring Shell 測試支援

終端提供者

與程式執行的底層終端進行互動歷來是一個相對複雜的過程,儘管表面看起來一切都只是文字互動。

還記得那些舊式手動打字機或點陣印表機嗎?字元在游標所在位置列印,如果需要在不同位置列印,游標就需要移動。簡而言之,這就是當前終端模擬器的工作方式。

為了更好地訪問和理解現有的終端模擬器環境,JLine 可以透過自己的共享庫使用原生程式碼。JLine 檢測哪些提供者存在,然後選擇使用其中一個。傳統上有三種提供者:`jansi`、`jni` 和 `jna`,它們都應提供相同的功能。

我們的 starter 依賴可以用於專門選擇其中的一些 JLine 提供者。

FFM

隨著 `JDK22` 的釋出,*Foreign Function and Memory API* 從預覽版中正式推出,它旨在取代 `JNI`,提供更好、更安全的原生 API。

從 `3.4.x` 版本開始,我們增加了對使用 `JLine` 的 `ffm` 終端提供者編譯 Spring Shell 應用的支援。這顯然意味著應用需要在 `JDK22+` 環境下執行。新的 JDK 中間版本每 6 個月釋出一次,長期支援 (LTS) 版本每 2 年釋出一次。在 Spring Shell 能與 Spring Framework 對齊的現有 LTS 版本釋出之前,我們將使用最新的 JDK 版本。顯然,如果您選擇使用 `ffm`,這意味著您可能需要在不方便的時候升級您的 JDK。我們還受限於 JLine 本身用於編譯其 `ffm` 部分的 JDK 版本。

FFM 本身在使用某些部分時會導致 JVM 列印警告。這些警告對於終端應用來說顯然很煩人,因為它可能會干擾並造成一些混亂。在未來的 JDK 版本中,這些警告也將新增到舊的 JNI 模組中,並且在某個時候,這些警告將變為硬錯誤。使用者將被要求手動啟用這些原生“不安全”部分。

在命令列中,為此設定的 JVM 選項是

--enable-native-access=ALL-UNNAMED

如果您有一個 Jar 檔案,您可以在其 `META-INF/MANIFEST.MF` 中包含此設定。

Enable-Native-Access: ALL-UNNAMED

可以在構建過程中新增,例如在使用 Gradle 時

tasks.named("bootJar") {
    manifest {
        attributes 'Enable-Native-Access': 'ALL-UNNAMED'
    }
}
至於在 JDK 中啟用原生部分,JLine 一直積極主動,已經對此進行了檢查,如果原生訪問未啟用,它將丟擲錯誤。

原生支援

將 *Spring Shell* 應用編譯為 *GraalVM* 二進位制檔案的支援主要來自 *Spring Framework* 和 *Spring Boot*,其中此功能稱為 *AOT*。AOT (Ahead of Time) 意味著應用上下文在編譯時就已準備好,以便進行 *GraalVM* 生成。

在 Spring Framework 和 Spring Boot 的 *AOT* 功能基礎上,*Spring Shell* 擁有自己的 *GraalVM* 配置,提供關於二進位制檔案中應包含哪些內容的提示。通常問題來自尚不包含 *GraalVM* 相關配置或配置不完整的第三方庫。

需要使用 *GraalVM Reachability Metadata Repository*,它為第三方庫提供了一些缺失的提示。您還需要安裝 *GraalVM* 並將 `JAVA_HOME` 指向它。

對於 *Gradle*,新增 graalvm 的原生外掛並配置元資料倉庫。

plugins {
	id 'org.graalvm.buildtools.native' version '0.9.16'
}

graalvmNative {
	metadataRepository {
        enabled = true
	}
}

使用 ./gradlew nativeCompile 執行 Gradle 構建時,您應該在 build/native/nativeCompile 目錄下獲得二進位制檔案。

對於 Maven,使用 spring-boot-starter-parent 作為父專案,您將獲得一個 native profile,可用於編譯。您需要配置元資料倉庫

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.graalvm.buildtools</groupId>
                <artifactId>native-maven-plugin</artifactId>
                <configuration>
                    <metadataRepository>
                        <enabled>true</enabled>
                    </metadataRepository>
                </configuration>
            </plugin>
        </plugins>
    </pluginManagement>
</build>
如果您依賴 spring-boot-starter-parent,它會管理 native-maven-plugin 的版本,並保持最新。

使用 ./mvnw native:compile -Pnative 執行 Maven 構建時,您應該在 target 目錄下獲得二進位制檔案。

如果一切順利,可以直接執行此二進位制檔案,而無需透過 JVM 執行 Boot 應用的 Jar 檔案。