桥接 CLI 参数解析与应用程序设置
Bridging CLI argument parsing with application setup
我基于 PicoCLI 的应用程序有多个命令和子命令,其中包含适用于所有命令的常规选项,以及适用于特定命令的一些选项。通用选项用于所有命令。
我的PicoCLI (sub-)commands与这个例子类似:
@Command(name = "country", description = "Resolve ISO country code (ISO-3166-1, Alpha-2 code)")
static class Subcommand1 implements Runnable {
@Parameters(arity = "1..*", paramLabel = "<country code>", description = "country code(s) to be resolved")
private String[] countryCodes;
@Override
public void run() {
for (String code : countryCodes) {
System.out.println(String.format("%s: %s", code.toUpperCase(), new Locale("", code).getDisplayCountry()));
}
}
}
但是每个(子)命令需要先运行一些通用设置代码,类似于:
@Override
public void run() {
try (Channel channel = _establishChannel(generalConfiguration)) {
// do sub-command work
}
}
其中 generalConfiguration
是用于所有(子)命令的一般参数和选项的示例。因此,这个通用设置代码块将在每个命令中重复:
try (Channel channel = _establishChannel(generalConfiguration)) {
// do sub-command work
}
但我希望它在一个地方表达,而不是。今天,我基本上复制了(子)参数和选项并调用了一个通用的助手:
void runCommand(String command, String c1Param, bool c1AllOption, String c2Filename, String c3Param /*...*/) {
try (Channel channel = _establishChannel(generalConfiguration)) {
switch(command) {
case "COMMAND_1":
doCommand1(c1Param,c1AllOption);
break;
case "COMMAND_2":
doCommand2(c2Filename);
break;
case "COMMAND_3":
doCommand3(c3Param);
break;
// ...
}
}
}
这很丑陋,而且很脆弱。有cleaner/better方法吗?
一个想法是使用自定义 Execution Strategy。
picocli 用户手册的Initialization Before Execution 部分有一个示例。
让我们尝试针对您的用例修改该示例。我得出这样的结论:
@Command(subcommands = {Sub1.class, Sub2.class, Sub3.class})
class MyApp implements Runnable {
Channel channel; // initialized in executionStrategy method
// A reference to this method can be used as a custom execution strategy
// that first calls the init() method,
// and then delegates to the default execution strategy.
private int executionStrategy(ParseResult parseResult) {
// custom initialization to be done before executing any command or subcommand
try (this.channel = _establishChannel(generalConfiguration)) {
// default execution strategy
return new CommandLine.RunLast().execute(parseResult);
}
}
public static void main(String[] args) {
MyApp app = new MyApp();
new CommandLine(app)
// wire in the custom execution strategy
.setExecutionStrategy(app::executionStrategy) // Java 8 method reference syntax
.execute(args);
}
// ...
}
此自定义执行策略可确保 top-level 命令的 channel
字段在执行任何命令之前初始化。
下一块是,子命令如何访问这个 channel
字段(因为这个字段在 top-level 命令中)?
这就是 @ParentCommand
注释的用途。
当子命令有一个@ParentCommand
注释的字段时,picocli会将父命令的用户对象注入该字段,以便子命令可以引用父命令中的状态。
例如:
@Command(name = "country", description = "Resolve ISO country code (ISO-3166-1, Alpha-2 code)")
static class Subcommand1 implements Runnable {
@ParentCommand
private MyApp parent; // picocli injects reference to parent command
@Parameters(arity = "1..*", paramLabel = "<country code>", description = "country code(s) to be resolved")
private String[] countryCodes;
@Override
public void run() {
Channel channel = parent.channel;
doSomethingWith(channel);
// ...
}
}
我基于 PicoCLI 的应用程序有多个命令和子命令,其中包含适用于所有命令的常规选项,以及适用于特定命令的一些选项。通用选项用于所有命令。
我的PicoCLI (sub-)commands与这个例子类似:
@Command(name = "country", description = "Resolve ISO country code (ISO-3166-1, Alpha-2 code)")
static class Subcommand1 implements Runnable {
@Parameters(arity = "1..*", paramLabel = "<country code>", description = "country code(s) to be resolved")
private String[] countryCodes;
@Override
public void run() {
for (String code : countryCodes) {
System.out.println(String.format("%s: %s", code.toUpperCase(), new Locale("", code).getDisplayCountry()));
}
}
}
但是每个(子)命令需要先运行一些通用设置代码,类似于:
@Override
public void run() {
try (Channel channel = _establishChannel(generalConfiguration)) {
// do sub-command work
}
}
其中 generalConfiguration
是用于所有(子)命令的一般参数和选项的示例。因此,这个通用设置代码块将在每个命令中重复:
try (Channel channel = _establishChannel(generalConfiguration)) {
// do sub-command work
}
但我希望它在一个地方表达,而不是。今天,我基本上复制了(子)参数和选项并调用了一个通用的助手:
void runCommand(String command, String c1Param, bool c1AllOption, String c2Filename, String c3Param /*...*/) {
try (Channel channel = _establishChannel(generalConfiguration)) {
switch(command) {
case "COMMAND_1":
doCommand1(c1Param,c1AllOption);
break;
case "COMMAND_2":
doCommand2(c2Filename);
break;
case "COMMAND_3":
doCommand3(c3Param);
break;
// ...
}
}
}
这很丑陋,而且很脆弱。有cleaner/better方法吗?
一个想法是使用自定义 Execution Strategy。
picocli 用户手册的Initialization Before Execution 部分有一个示例。 让我们尝试针对您的用例修改该示例。我得出这样的结论:
@Command(subcommands = {Sub1.class, Sub2.class, Sub3.class})
class MyApp implements Runnable {
Channel channel; // initialized in executionStrategy method
// A reference to this method can be used as a custom execution strategy
// that first calls the init() method,
// and then delegates to the default execution strategy.
private int executionStrategy(ParseResult parseResult) {
// custom initialization to be done before executing any command or subcommand
try (this.channel = _establishChannel(generalConfiguration)) {
// default execution strategy
return new CommandLine.RunLast().execute(parseResult);
}
}
public static void main(String[] args) {
MyApp app = new MyApp();
new CommandLine(app)
// wire in the custom execution strategy
.setExecutionStrategy(app::executionStrategy) // Java 8 method reference syntax
.execute(args);
}
// ...
}
此自定义执行策略可确保 top-level 命令的 channel
字段在执行任何命令之前初始化。
下一块是,子命令如何访问这个 channel
字段(因为这个字段在 top-level 命令中)?
这就是 @ParentCommand
注释的用途。
当子命令有一个@ParentCommand
注释的字段时,picocli会将父命令的用户对象注入该字段,以便子命令可以引用父命令中的状态。
例如:
@Command(name = "country", description = "Resolve ISO country code (ISO-3166-1, Alpha-2 code)")
static class Subcommand1 implements Runnable {
@ParentCommand
private MyApp parent; // picocli injects reference to parent command
@Parameters(arity = "1..*", paramLabel = "<country code>", description = "country code(s) to be resolved")
private String[] countryCodes;
@Override
public void run() {
Channel channel = parent.channel;
doSomethingWith(channel);
// ...
}
}