subtask2 = scope.fork(task2);
scope.join();
} catch (Exception e) {
e.printStackTrace();
}
}
我們現在可以像使用任何 Java 檔案一樣開發腳本。
一旦準備好發布,我們可以將其匯出為不同的格式,如下所示:
- 一個jar檔:jbang導出可移植的helloworld.java。如果您的腳本使用依賴項,則更建議使用以下命令。
- A fatjar:包含所有相依性:jbang export fatjar helloworld.java。此方法仍需要在目標機器上安裝JDK/JRE。如果您不想這樣做,更建議使用接下來的命令。
- 包含 JDK 的 jlink 二進位檔案:jbang export jlink helloworld.java。要執行的二進位檔案是 Unix 上的 helloworld-jlink/bin/helloworld 或 Windows 上的 helloworld-jlink/bin/helloworld.bat。
- A 原生 imgae:jbang 匯出原生 helloworld.java。這需要安裝 GraalVM。
腳本也可以匯出為 mavenrepo: jbang export mavenrepo helloworld.java
JDK管理
如前一章所示,JBang 可以在您的電腦上安裝 JDK。
您可以使用 jbang jdk list 列出已安裝的 JDK,使用 jbang jdk list --available --show-details 列出可安裝的 JDK,使用 jbang jdk install [version] 安裝新的 JDK。 Jbang 也支援使用 SDKMAN 來管理支援系統上的 JDK。
此外,可以在腳本中指定 JDK 版本。
這是透過將以下行新增至腳本屬性來完成的: //JAVA [version](如果我們想要精確版本)或 //JAVA [version](如果我們至少想要一個特定版本)。
在這種情況下,JBang 將自動安裝所需的 JDK 版本並僅將其用於該腳本,而不更改系統的預設 JDK。
例如,以下腳本使用 Java 25 和一些預覽功能。
> jbang init helloworld.java
沒有「Main」類別的腳本
由於腳本往往是輕量級的,因此最好在沒有類別和 main 方法的情況下編寫它們。
幸運的是,Java 有一個稱為隱式宣告類別和實例主方法的功能(在 Java 23 中仍處於預覽狀態)。
此功能允許在沒有類別和靜態 main 方法的情況下編寫 java 程式和 JBang 腳本。
下面的腳本將被編譯並執行,沒有任何問題。
> jbang helloworld.java
Hello world
這是透過向腳本添加以下屬性來實現的。
///usr/bin/env jbang "<pre class="brush:php;toolbar:false">> chmod +x helloworld.java
> ./helloworld.java
Hello world
" "$@" ; exit $?
import static java.lang.System.*;
public class helloworld {
public static void main(String... args) {
out.println("Hello world");
}
}
第一行 //JAVA 23 告訴 JBang 使用 Java 23 或更高版本。
第二行和第三行 //COMPILE_OPTIONS --enable-preview -source 23 和 //RUNTIME_OPTIONS --enable-preview 分別啟用編譯和執行時的預覽功能。
一旦功能變得穩定,我們可以刪除這 3 行,腳本仍然可以工作。整齊!
依賴關係
JBang 支援以 Gradle 樣式依賴項的形式為腳本新增依賴項,方法是為每個依賴項新增 //DEPS atrefact-id:atrefact-name:version 行。
例如,要使用 jfiglet 函式庫,我們可以將以下行加入腳本: //DEPS com.github.lalyos:jfiglet:0.0.8.
> jbang init helloworld.java
目錄
JBang 中的目錄允許有效地組織和共享腳本和範本。
此功能對於想要共享常見任務或工作流程的腳本集合的團隊或社群特別有用。
對於想要分發入門程式碼或在不提供原始程式碼的情況下顯示練習結果的教師來說,它也很有用。
目錄是一個名為 jbang-catalog.json 的 JSON 文件,其中包含兩組項目:別名和模板。
別名允許使用簡單的命令從目錄運行腳本,而模板則提供新腳本的起點。
目錄可以是遠端的或本地的,並且可以根據需要添加和使用任意數量的本地或遠端儲存庫。
有趣的是,JBang 在設定過程中創建了一個本地目錄,其中包含一些開箱即用的別名和模板。
JBang 依照下列順序在這些目錄中尋找本機目錄(來源 JBang 文件):
- 目前目錄,./jbang-catalog.json
- 在./.jbang/jbang-catalog.json中
- 在父目錄中,../jbang-catalog.json
- 在父級的 .jbang 目錄中,../.jbang/jbang-catalog.json
- 並向上遞歸重複步驟3和4到檔案系統的根
- 最後一步它將查看 $HOME/.jbang/jbang-catalog.json
JBang 將在許多開源儲存庫(如 GitHub、GitLab、Bitbucket 等)中尋找遠端目錄。
我將在本文中使用 GitHub 作為範例。
若要建立遠端目錄,您需要將 jbang-catalog.json 新增至儲存庫的根資料夾。
然後透過 account/repository_name 引用該目錄。
如果您的儲存庫名為 jbang-catalog,那麼您可以透過帳戶引用它。
因此,例如,如果我的GitHub 帳戶名為yostane,並且我有一個名為curs-java 的儲存庫,其中包含一個名為jbang-catalog.json 的檔案的目錄,我可以透過yostane/cours-java 引用該目錄。此外,如果我在名為 jbang-catalog 的儲存庫中有一個 jbang-catalog.json,那麼我可以透過 yostane/jbang-catalog 或簡單地 yostane 引用它。
> jbang helloworld.java
Hello world
以下章節將展示如何使用目錄中的別名和模板。
別名
JBang 中的別名允許從目錄執行腳本。
完整語法是 jbang alias@account/repository [args] 和 jbang alias [args] 分別表示遠端和本地別名。
可以使用以下格式在目錄檔案的別名部分定義別名:
> jbang init helloworld.java
這是我在 DevoxxMA 2024 會議期間使用的目錄。
> jbang helloworld.java
Hello world
您可以使用以下命令執行這些別名:
- jbang palcli@yostane/cours-java 女士
- jbang palqrest@yostane/cours-java
- jbang hellojfx@yostane/cours-java
官方JBang GitHub帳號提供了包含許多別名和範本的目錄。
讓我們運行其中的一些:
-
jbang httpd@jbangdev 運行本機網路伺服器。
-
jbang gavsearch@jbangdev [arg] 在 search.maven.org 上搜尋 [arg]。
範本
模板,這是預先定義的腳本,可以用作新腳本的起點。
它們使用以下格式在目錄檔案的範本部分中定義:
///usr/bin/env jbang "<pre class="brush:php;toolbar:false">> chmod +x helloworld.java
> ./helloworld.java
Hello world
" "$@" ; exit $?
import static java.lang.System.*;
public class helloworld {
public static void main(String... args) {
out.println("Hello world");
}
}
使用範本時,JBang 會在 file-refs 屬性中建立所有檔案的副本。
當 file-ref 包含 {basename} 時,JBang 會將其替換為正在建立的腳本的名稱。
當檔案引用使用 .qute 副檔名時,JBang 使用 Qute 模板引擎
以下是一些現成可用的範本的範例:
- 使用 picocli 的 CLI 腳本:jbang init -t cli hellocli.java
- Quarkus 單檔 REST API:jbang init -t qrest helloqrest.java
我們也可以使用社群共享的網路範本。
例如,此指令建立一個 JUnit 單元測試檔: jbang init -t junit@jbangdev file_to_test.java.
從指令中我們可以找到 jbangdev/jbang-catalog 儲存庫中定義範本的 jbang-catalog.json。
/// usr/bin/env jbang "<pre class="brush:php;toolbar:false">///usr/bin/env jbang "<pre class="brush:php;toolbar:false">//JAVA 23+
//COMPILE_OPTIONS --enable-preview -source 23
//RUNTIME_OPTIONS --enable-preview
" "$@" ; exit $?
//JAVA 23+
//COMPILE_OPTIONS --enable-preview -source 23
//RUNTIME_OPTIONS --enable-preview
void main(String... args) {
System.out.println("Hello World");
}
" "$@" ; exit $?
//JAVA 25
//COMPILE_OPTIONS --enable-preview -source 25
//RUNTIME_OPTIONS --enable-preview
import java.util.concurrent.Callable;
import java.util.concurrent.StructuredTaskScope;
import static java.lang.System.*;
void main(String... args) {
out.println("Hello Java 25");
Callable task1 = () -> {
out.println("Task 1" + Thread.currentThread());
return "Task 1";
};
Callable task2 = () -> {
out.println("Task 2" + Thread.currentThread());
return 2;
};
try (
var scope = new StructuredTaskScope