编辑此页面

使用 Quarkus 进行脚本编程

Quarkus 集成了 jbang,您可以使用它来编写 Java 脚本/应用程序,而无需 Maven 或 Gradle 即可运行。

在本指南中,我们将了解如何仅使用单个 Java 文件编写 REST 应用程序。

此技术被认为是预览版。

预览中,不保证向后兼容性和在生态系统中的存在。 具体改进可能需要更改配置或 API,并且成为稳定的计划正在进行中。 欢迎在我们的邮件列表或我们的GitHub 问题跟踪器上提供反馈。

有关可能的完整状态列表,请查看我们的常见问题解答条目

先决条件

要完成本指南,您需要

  • 大约 5 分钟

  • 一个 IDE

  • 已安装 JDK 17+ 并正确配置了 JAVA_HOME

  • 如果您想构建本机可执行文件(或者如果您使用本机容器构建,则为 Docker),可以选择安装 Mandrel 或 GraalVM 并进行适当的配置

  • JBang

解决方案

通常我们会链接到 Git 仓库进行克隆,但在这种情况下,除了以下内容之外没有其他文件

//usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS io.quarkus.platform:quarkus-bom:3.24.4@pom
//DEPS io.quarkus:quarkus-rest
//JAVAC_OPTIONS -parameters
//JAVA_OPTIONS -Djava.util.logging.manager=org.jboss.logmanager.LogManager

import io.quarkus.runtime.Quarkus;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/hello")
@ApplicationScoped
public class quarkusapp {

    @GET
    public String sayHello() {
        return "hello";
    }

    public static void main(String[] args) {
        Quarkus.run(args);
    }

    @Inject
    GreetingService service;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    @Path("/greeting/{name}")
    public String greeting(String name) {
        return service.greeting(name);
    }

    @ApplicationScoped
    static public class GreetingService {

        public String greeting(String name) {
            return "hello " + name;
        }
    }
}

架构

在本指南中,我们创建一个简单的应用程序,使用单个源文件提供一个 hello 端点,无需额外的构建文件(如 pom.xmlbuild.gradle)。 为了演示依赖注入,此端点使用一个 greeting bean。

Architecture

创建初始文件

首先,我们需要一个 Java 文件。 JBang 允许您使用以下命令创建初始版本

jbang init scripting/quarkusapp.java
cd scripting

此命令生成一个 .java 文件,您可以在 Linux 和 macOS 上直接运行它,即 ./quarkusapp.java - 在 Windows 上,您需要使用 jbang quarkusapp.java

此初始版本运行时将打印 Hello World

生成后,查看 quarkusapp.java 文件。

您会在顶部找到一行类似于

//usr/bin/env jbang "$0" "$@" ; exit $?

在 Linux 和 macOS 上,这一行允许您将其作为脚本运行。 在 Windows 上,此行将被忽略。

接下来的几行

//DEPS <dependency1> <dependency2>

说明如何将依赖项添加到此脚本。 这是 JBang 的一个功能。

继续并更新此行以包括 quarkus-bomquarkus-rest 依赖项,如下所示

//DEPS io.quarkus.platform:quarkus-bom:3.24.4@pom
//DEPS io.quarkus:quarkus-rest

现在,运行 jbang quarkusapp.java,您将看到 JBang 解决此依赖项并在 Quarkus JBang 集成的帮助下构建 jar。

$ jbang quarkusapp.java

[jbang] Resolving dependencies...
[jbang]     Resolving io.quarkus:quarkus-resteasy:3.24.4...Done
[jbang] Dependencies resolved
[jbang] Building jar...
[jbang] Post build with io.quarkus.launcher.JBangIntegration
Mar 22, 2023 9:47:51 A.M. org.jboss.threads.Version <clinit>
INFO: JBoss Threads version 3.5.0.Final
Mar 22, 2023 9:47:51 A.M. io.quarkus.deployment.QuarkusAugmentor run
INFO: Quarkus augmentation completed in 722ms
Hello World

目前,该应用程序没有做任何新的事情。

如何编辑此文件并获得内容辅助?

要在具有内容辅助的 IDE/编辑器中编辑 JBang 脚本,您可以运行 jbang edit quarkusapp.javajbang edit quarkusapp.java

有关更多信息,请参阅 JBang 文档

Jakarta REST 资源

现在让我们用一个使用 Quarkus 功能的类替换该类

import io.quarkus.runtime.Quarkus;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;

@Path("/hello")
@ApplicationScoped
public class quarkusapp {

    @GET
    public String sayHello() {
        return "hello";
    }

    public static void main(String[] args) {
        Quarkus.run(args);
    }
}

这是一个非常简单的类,带有一个 main 方法,用于启动带有 REST 端点的 Quarkus,在 "/hello" 上返回“hello”。

为什么有 main 方法?

目前需要一个 main 方法才能使 JBang 集成工作 - 我们可能会在将来取消此要求。

运行应用程序

现在,当您运行该应用程序时,您将看到 Quarkus 启动。

使用:jbang quarkusapp.java

$ jbang quarkusapp.java

[jbang] Building jar...
[jbang] Post build with io.quarkus.launcher.JBangIntegration
Mar 22, 2023 9:48:39 A.M. org.jboss.threads.Version <clinit>
INFO: JBoss Threads version 3.5.0.Final
Mar 22, 2023 9:48:39 A.M. io.quarkus.deployment.QuarkusAugmentor run
INFO: Quarkus augmentation completed in 521ms
__  ____  __  _____   ___  __ ____  ______
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2023-03-22 09:48:39,891 INFO  [io.quarkus] (main) quarkus 999-SNAPSHOT on JVM (powered by Quarkus 3.24.4) started in 0.283s. Listening on: http://0.0.0.0:8080
2023-03-22 09:48:39,904 INFO  [io.quarkus] (main) Profile prod activated.
2023-03-22 09:48:39,904 INFO  [io.quarkus] (main) Installed features: [cdi, rest, smallrye-context-propagation, vertx]

启动后,您可以请求提供的端点

$ curl -w "\n" https://:8080/hello
hello

之后,点击 CTRL+C 停止应用程序。

使用 curl -w "\n" 自动添加换行符

在此示例中,我们使用 curl -w "\n" 以避免您的终端打印“%”或将结果和下一个命令提示符放在同一行上。

为什么 quarkus-rest 没有被解析?

在第二次运行时,您不应看到一行说它正在解析 quarkus-rest,因为 JBang 会缓存运行之间的依赖项解析。 如果要清除缓存以强制解析,请使用 jbang cache clear

使用注入

Quarkus 中的依赖注入基于 ArC,ArC 是一种基于 CDI 的依赖注入解决方案,专为 Quarkus 的架构量身定制。 您可以在上下文和依赖注入指南中了解有关它的更多信息。

ArC 作为 quarkus-rest 的依赖项提供,因此您已经可以方便地使用它。

让我们修改应用程序并添加一个配套 bean。

通常您会添加一个单独的类,但由于我们的目标是将所有内容放在一个文件中,因此您将添加一个嵌套类。

quarkusapp 类主体添加以下内容。

@ApplicationScoped
static public class GreetingService {

    public String greeting(String name) {
        return "hello " + name;
    }

}
使用嵌套静态公共类

我们使用嵌套静态公共类而不是顶级类有两个原因

  1. JBang 目前不支持多个源文件。

  2. 所有依赖于内省的 Java 框架在使用顶级类时都面临挑战,因为它们不像公共类那样可见;并且在 Java 中,一个文件中只能有一个顶级公共类。

编辑 quarksapp 类以注入 GreetingService 并使用它创建一个新端点,您应该得到类似以下内容

//usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS io.quarkus.platform:quarkus-bom:3.24.4@pom
//DEPS io.quarkus:quarkus-rest

import io.quarkus.runtime.Quarkus;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/hello")
@ApplicationScoped
public class quarkusapp {

    @GET
    public String sayHello() {
        return "hello";
    }

    public static void main(String[] args) {
        Quarkus.run(args);
    }

    @Inject
    GreetingService service;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    @Path("/greeting/{name}")
    public String greeting(String name) {
        return service.greeting(name);
    }

    @ApplicationScoped
    static public class GreetingService {

        public String greeting(String name) {
            return "hello " + name;
        }
    }
}

现在,当您运行 jbang quarkusapp.java 时,您可以检查新端点返回的内容

$ curl -w "\n" https://:8080/hello/greeting/quarkus
hello null

这真是出乎意料,为什么它返回 hello null 而不是 hello quarkus

原因是 Quarkus REST(以前的 RESTEasy Reactive)依赖于设置 -parameters 编译器标志,以便能够将 {name} 映射到 name 参数。

我们通过将以下注释指令添加到文件中来解决此问题

//JAVAC_OPTIONS -parameters

现在,当您使用 jbang quarkusapp.java 运行时,端点应该返回您期望的结果

$ curl -w "\n" https://:8080/hello/greeting/quarkus
hello quarkus

调试

要调试应用程序,您可以使用 jbang --debug quarkusapp.java,并且可以使用您的 IDE 连接到端口 4004; 如果您想使用更传统的 Quarkus 调试端口,可以使用 jbang --debug=5005 quarkusapp.java

注意:JBang 调试始终会暂停,因此您需要连接调试器才能运行该应用程序。

日志记录

要在 JBang 的 Quarkus 脚本中使用日志记录,您可以像往常一样配置记录器,例如

public static final Logger LOG = Logger.getLogger(quarkusapp.class);

要使其工作,您需要添加一个 Java 选项,以确保正确初始化日志记录,例如

//JAVA_OPTIONS -Djava.util.logging.manager=org.jboss.logmanager.LogManager

完成此设置后,运行 jbang quarkusapp.java 将按预期进行日志记录和呈现。

配置应用程序

要配置应用程序,您可以像往常一样使用 application.properties 文件,但您需要将它添加到脚本

//FILES application.properties

// ...
@ConfigProperty(name = "prefix", defaultValue = "WG -")
String prefix;

这将使 application.properties 文件可用于脚本,并像往常一样处理配置。

您还可以使用 application.yaml 文件。 为此,您需要将它添加application.yaml 文件到脚本中,并包括 quarkus-config-yaml 依赖项

//DEPS io.quarkus:quarkus-config-yaml
//FILES application.yaml
application.propertiesapplication.yaml 文件的路径是相对于脚本文件的。

作为本机应用程序运行

如果您已安装 native-image 二进制文件并设置了 GRAALVM_HOME,或者在 Linux 上安装了容器运行时(例如,podman 或 docker),则可以使用 jbang --native quarkusapp.java 构建和运行本机可执行文件

$ jbang --native quarkusapp.java
[jbang] Building jar...
[jbang] Post build with io.quarkus.launcher.JBangIntegration
Mar 22, 2023 9:58:47 A.M. org.jboss.threads.Version <clinit>
INFO: JBoss Threads version 3.5.0.Final
Mar 22, 2023 9:58:47 A.M. io.quarkus.deployment.pkg.steps.JarResultBuildStep buildNativeImageThinJar
INFO: Building native image source jar: /tmp/quarkus-jbang8082065952748314720/quarkus-application-native-image-source-jar/quarkus-application-runner.jar
Mar 22, 2023 9:58:47 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildStep build
INFO: Building native image from /tmp/quarkus-jbang8082065952748314720/quarkus-application-native-image-source-jar/quarkus-application-runner.jar
Mar 22, 2023 9:58:47 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildStep getNativeImageBuildRunner
WARN: Cannot find the `native-image` in the GRAALVM_HOME, JAVA_HOME and System PATH. Attempting to fall back to container build.
Mar 22, 2023 9:58:47 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildContainerRunner <init>
INFO: Using docker to run the native image builder
Mar 22, 2023 9:58:47 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildContainerRunner setup
INFO: Checking image status quay.io/quarkus/ubi-quarkus-mandrel-builder-image:22.3-java17
Mar 22, 2023 9:58:51 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildStep checkGraalVMVersion
INFO: Running Quarkus native-image plugin on native-image 22.3.1.0-Final Mandrel Distribution (Java Version 17.0.6+10)
Mar 22, 2023 9:58:51 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildRunner build
INFO: docker run --env LANG=C --rm --user 1000:1000 -v /tmp/quarkus-jbang8082065952748314720/quarkus-application-native-image-source-jar:/project:z --name build-native-XaZUc quay.io/quarkus/ubi-quarkus-mandrel-builder-image:22.3-java17 -J-Dsun.nio.ch.maxUpdateArraySize=100 -J-Djava.util.logging.manager=org.jboss.logmanager.LogManager -J-Dlogging.initial-configurator.min-level=500 -J-Dvertx.logger-delegate-factory-class-name=io.quarkus.vertx.core.runtime.VertxLogDelegateFactory -J-Dvertx.disableDnsResolver=true -J-Dio.netty.noUnsafe=true -J-Dio.netty.leakDetection.level=DISABLED -J-Dio.netty.allocator.maxOrder=3 -J-Duser.language=en -J-Duser.country=IE -J-Dfile.encoding=UTF-8 --features=io.quarkus.runner.Feature,io.quarkus.runtime.graal.DisableLoggingFeature -J--add-exports=java.security.jgss/sun.security.krb5=ALL-UNNAMED -J--add-opens=java.base/java.text=ALL-UNNAMED -J--add-opens=java.base/java.io=ALL-UNNAMED -J--add-opens=java.base/java.lang.invoke=ALL-UNNAMED -J--add-opens=java.base/java.util=ALL-UNNAMED -H:+CollectImageBuildStatistics -H:ImageBuildStatisticsFile=quarkus-application-runner-timing-stats.json -H:BuildOutputJSONFile=quarkus-application-runner-build-output-stats.json -H:+AllowFoldMethods -J-Djava.awt.headless=true --no-fallback --link-at-build-time -H:+ReportExceptionStackTraces -H:-AddAllCharsets --enable-url-protocols=http -H:NativeLinkerOption=-no-pie -H:-UseServiceLoaderFeature -H:+StackTrace -J--add-exports=org.graalvm.sdk/org.graalvm.nativeimage.impl=ALL-UNNAMED --exclude-config io\.netty\.netty-codec /META-INF/native-image/io\.netty/netty-codec/generated/handlers/reflect-config\.json --exclude-config io\.netty\.netty-handler /META-INF/native-image/io\.netty/netty-handler/generated/handlers/reflect-config\.json quarkus-application-runner -jar quarkus-application-runner.jar
Mar 22, 2023 9:37:56 A.M. io.quarkus.deployment.pkg.steps.NativeImageBuildRunner runCommand
INFO: docker run --env LANG=C --rm --user 1000:1000 -v /tmp/quarkus-jbang9315448339582904220/quarkus-application-native-image-source-jar:/project:z --entrypoint /bin/bash quay.io/quarkus/ubi-quarkus-mandrel-builder-image:22.3-java17 -c objcopy --strip-debug quarkus-application-runner
Mar 22, 2023 9:37:57 A.M. io.quarkus.deployment.QuarkusAugmentor run
INFO: Quarkus augmentation completed in 31729ms
__  ____  __  _____   ___  __ ____  ______
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2023-03-22 09:37:57,471 INFO  [io.quarkus] (main) quarkus 999-SNAPSHOT native (powered by 3.24.4) started in 0.009s. Listening on: http://0.0.0.0:8080
2023-03-22 09:37:57,472 INFO  [io.quarkus] (main) Profile prod activated.
2023-03-22 09:37:57,472 INFO  [io.quarkus] (main) Installed features: [cdi, rest, smallrye-context-propagation, vertx]

此本机构建在首次运行时会花费一些时间,但是任何后续运行(在不更改 quarkusapp.java 的情况下)都会接近即时,这要归功于 JBang 缓存

$ jbang --native quarkusapp.java
__  ____  __  _____   ___  __ ____  ______
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2023-03-22 09:38:45,450 INFO  [io.quarkus] (main) quarkus 999-SNAPSHOT native (powered by 3.24.4) started in 0.009s. Listening on: http://0.0.0.0:8080
2023-03-22 09:38:45,450 INFO  [io.quarkus] (main) Profile prod activated.
2023-03-22 09:38:45,450 INFO  [io.quarkus] (main) Installed features: [cdi, rest, smallrye-context-propagation, vertx]

使用 Qute

您可以通过添加 quarkus-qute 依赖项在 JBang 脚本中使用 Qute 模板引擎。 您还需要将 templates 目录包含在脚本中

//DEPS io.quarkus:quarkus-qute
//FILES templates/=templates/*

// ...

 @Inject
 Template template; // Locate and load the `templates/template.html` file

如果您的 templates 目录包含子目录,请使用 templates/=templates/**/ 代替。

结论

如果您想开始使用 Quarkus 或快速编写一些内容,Quarkus Scripting with jbang 可让您做到这一点。 没有 Maven,没有 Gradle - 只有一个 Java 文件。 在本指南中,我们概述了使用带有 JBang 的 Quarkus 的最基本知识; 如果您想了解有关 JBang 可以做什么的更多信息,请访问 https://jbang.dev