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