Command Mode with Picocli
Picocli是一种用于创建丰富的命令行应用程序的开源工具。 Quarkus 提供对使用 Picocli 的支持。本指南包含 `picocli`扩展用法的示例。
如果你不熟悉 Quarkus 命令模式,请考虑首先阅读Command Mode reference guide。
- Extension
- Simple command line application
- Command line application with multiple Commands
- Customizing Picocli CommandLine instance
- Different entry command for each profile
- Configure CDI Beans with parsed arguments
- Providing own QuarkusMain
- Native mode support
- Development Mode
- Kubernetes support
- Configuration Reference
Extension
配置好 Quarkus 项目后,可以通过在项目基础目录中运行以下命令将 `picocli`扩展添加到项目中。
quarkus extension add {add-extension-extensions}
./mvnw quarkus:add-extension -Dextensions='{add-extension-extensions}'
./gradlew addExtension --extensions='{add-extension-extensions}'
这会将以下内容添加到构建文件中:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-picocli</artifactId>
</dependency>
implementation("io.quarkus:quarkus-picocli")
Simple command line application
仅一个 `Command`的简单 PicocliApplication 可按如下方式创建:
package com.acme.picocli;
import picocli.CommandLine;
import jakarta.enterprise.context.Dependent;
import jakarta.inject.Inject;
@CommandLine.Command (1)
public class HelloCommand implements Runnable {
@CommandLine.Option(names = {"-n", "--name"}, description = "Who will we greet?", defaultValue = "World")
String name;
private final GreetingService greetingService;
public HelloCommand(GreetingService greetingService) { (2)
this.greetingService = greetingService;
}
@Override
public void run() {
greetingService.sayHello(name);
}
}
@Dependent
class GreetingService {
void sayHello(String name) {
System.out.println("Hello " + name + "!");
}
}
1 | 如果只有一个用 `picocli.CommandLine.Command`注解的类,则将其用作 Picocli 命令行的入口点。 |
2 | 用 `picocli.CommandLine.Command`注解的所有类都将注册为 CDI bean。 |
具有 @CommandLine.Command`的 bean 不应使用代理范围(例如不要使用 `@ApplicationScope
),因为 Picocli 无法在此类 bean 中设置字段值。此扩展将使用 `@CommandLine.Command`范围将类与 `@Depended`注解一起注册。如果你需要使用代理范围,则注解 setter 而不是字段,例如:
@CommandLine.Command
@ApplicationScoped
public class EntryCommand {
private String name;
@CommandLine.Option(names = "-n")
public void setName(String name) {
this.name = name;
}
}
Command line application with multiple Commands
当多个类存在 picocli.CommandLine.Command
注释时,其中一个还需要使用 io.quarkus.picocli.runtime.annotations.TopCommand
做注释,可以使用 quarkus.picocli.top-command
属性进行覆盖。
package com.acme.picocli;
import io.quarkus.picocli.runtime.annotations.TopCommand;
import picocli.CommandLine;
@TopCommand
@CommandLine.Command(mixinStandardHelpOptions = true, subcommands = {HelloCommand.class, GoodByeCommand.class})
public class EntryCommand {
}
@CommandLine.Command(name = "hello", description = "Greet World!")
class HelloCommand implements Runnable {
@Override
public void run() {
System.out.println("Hello World!");
}
}
@CommandLine.Command(name = "goodbye", description = "Say goodbye to World!")
class GoodByeCommand implements Runnable {
@Override
public void run() {
System.out.println("Goodbye World!");
}
}
Customizing Picocli CommandLine instance
您可以通过生成自己的 bean 实例来自定义 picocli
扩展使用的 CommandLine 类:
package com.acme.picocli;
import io.quarkus.picocli.runtime.PicocliCommandLineFactory;
import io.quarkus.picocli.runtime.annotations.TopCommand;
import picocli.CommandLine;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
@TopCommand
@CommandLine.Command
public class EntryCommand implements Runnable {
@CommandLine.Spec
CommandLine.Model.CommandSpec spec;
@Override
public void run() {
System.out.println("My name is: " + spec.name());
}
}
@ApplicationScoped
class CustomConfiguration {
@Produces
CommandLine customCommandLine(PicocliCommandLineFactory factory) { (1)
return factory.create().setCommandName("CustomizedName");
}
}
1 | PicocliCommandLineFactory 会使用注入的 TopCommand 和 CommandLine.IFactory 创建一个 CommandLine 实例。 |
Different entry command for each profile
可以使用 @IfBuildProfile
为每个配置文件创建不同的入口命令:
@ApplicationScoped
public class Config {
@Produces
@TopCommand
@IfBuildProfile("dev")
public Object devCommand() {
return DevCommand.class; (1)
}
@Produces
@TopCommand
@IfBuildProfile("prod")
public Object prodCommand() {
return new ProdCommand("Configured by me!");
}
}
1 | 您可以在此处返回 java.lang.Class 的实例。在这种情况下 CommandLine 将尝试使用 CommandLine.IFactory 实例化此类。 |
Configure CDI Beans with parsed arguments
可以使用 Event<CommandLine.ParseResult>
或仅使用 CommandLine.ParseResult
根据 Picocli 解析的参数来配置 CDI bean。这个事件将在由这个扩展创建的 QuarkusApplication
类中生成。如果您提供自己的 @QuarkusMain
则不会触发此事件。CommandLine.ParseResult
是从默认的 CommandLine
bean 创建的。
@CommandLine.Command
public class EntryCommand implements Runnable {
@CommandLine.Option(names = "-c", description = "JDBC connection string")
String connectionString;
@Inject
DataSource dataSource;
@Override
public void run() {
try (Connection c = dataSource.getConnection()) {
// Do something
} catch (SQLException throwables) {
// Handle error
}
}
}
@ApplicationScoped
class DatasourceConfiguration {
@Produces
@ApplicationScoped (1)
DataSource dataSource(CommandLine.ParseResult parseResult) {
PGSimpleDataSource ds = new PGSimpleDataSource();
ds.setURL(parseResult.matchedOption("c").getValue().toString());
return ds;
}
}
1 | @ApplicationScoped 用于延迟初始化 |
Providing own QuarkusMain
您还可以提供自己的应用程序入口点,带有 QuarkusMain
注释(如 Command Mode reference guide 所述)。
package com.acme.picocli;
import io.quarkus.runtime.QuarkusApplication;
import io.quarkus.runtime.annotations.QuarkusMain;
import picocli.CommandLine;
import jakarta.inject.Inject;
@QuarkusMain
@CommandLine.Command(name = "demo", mixinStandardHelpOptions = true)
public class ExampleApp implements Runnable, QuarkusApplication {
@Inject
CommandLine.IFactory factory; (1)
@Override
public void run() {
// business logic
}
@Override
public int run(String... args) throws Exception {
return new CommandLine(this, factory).execute(args);
}
}
1 | CommandLine.IFactory bean 与 Quarkus 兼容,由 picocli 扩展创建。 |
Native mode support
此扩展使用 Quarkus 标准构建步骤机制来支持 GraalVM Native 映像。在特殊情况下,未来 picocli 版本中的不兼容更改会导致任何问题时,可以使用以下配置作为临时解决办法,以回退到来自 picocli 项目的注释处理器:
<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli-codegen</artifactId>
</dependency>
对于 Gradle,您需要在 build.gradle
文件的 dependencies
部分中添加以下内容:
annotationProcessor enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")
annotationProcessor 'info.picocli:picocli-codegen'
Development Mode
在开发模式下,即运行 mvn quarkus:dev
时,每次按下 Space bar
键时都会执行应用程序并重新启动。您还可以通过 quarkus.args
系统属性将参数传递给您的命令行应用程序,例如 mvn quarkus:dev -Dquarkus.args='--help'
和 mvn quarkus:dev -Dquarkus.args='-c -w --val 1'
。
Kubernetes support
拥有命令行应用程序后,您还可以通过添加 kubernetes
扩展来生成在 Kubernetes 中安装和使用此应用程序所需的资源。要在您的项目基本目录下安装 kubernetes
扩展,请运行以下命令。
quarkus extension add {add-extension-extensions}
./mvnw quarkus:add-extension -Dextensions='{add-extension-extensions}'
./gradlew addExtension --extensions='{add-extension-extensions}'
这会将以下内容添加到您的 pom.xml
:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kubernetes</artifactId>
</dependency>
然后,使用以下命令构建应用程序:
quarkus build
./mvnw install
./gradlew build
Kubernetes 扩展将检测到 Picocli 扩展的存在,并因此在 target/kubernetes/
目录中生成 Job 资源而不是 Deployment 资源。
如果您不想生成 Job 资源,可以使用 quarkus.kubernetes.deployment-kind
属性指定您想要生成的资源。例如,如果您想要生成 Deployment 资源,请使用 quarkus.kubernetes.deployment-kind=Deployment
。
此外,您可以通过 quarkus.kubernetes.arguments
属性提供 Kubernetes Job 将使用的参数。例如,在添加 quarkus.kubernetes.arguments=A,B
属性并构建项目后,将生成以下 Job 资源:
apiVersion: batch/v1
kind: Job
metadata:
labels:
app.kubernetes.io/name: app
app.kubernetes.io/version: 0.1-SNAPSHOT
name: app
spec:
completionMode: NonIndexed
suspend: false
template:
metadata:
labels:
app.kubernetes.io/name: app
app.kubernetes.io/version: 0.1-SNAPSHOT
spec:
containers:
- args:
- A
- B
env:
- name: KUBERNETES_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: docker.io/user/app:0.1-SNAPSHOT
imagePullPolicy: Always
name: app
ports:
- containerPort: 8080
name: http
protocol: TCP
restartPolicy: OnFailure
terminationGracePeriodSeconds: 10
最后,Kubernetes 任务将在每次安装在 Kubernetes 中时启动。更多关于如何在 document 中运行 Kubernetes 任务的信息,请点击此处。