Picocli:class 属性 填充和应用程序执行的职责分开
Picocli: Separate reponsibilities of class property population and application execution
在使用 cli 之前,我会有一个启动器 class,它调用我的 ApplicationPropertiesProvider class(它读取我的属性文件),然后启动业务逻辑。所以有一个分离,ApplicationPropertiesProvider 只有一份工作。
现在使用 picocli,guide/documentation 声明我必须使用 CommandLine.run(objectToPopulate, args) 或 CommandLine.call(objectToPopulate, args)。因此,使用 cli 参数 (ApplicationPropertiesProvider) 填充的 class 必须实现 Runnable 或 Callable。现在我可以将 Starter class 的启动代码粘贴到 运行() 或 call() 方法中,然后放弃 Starter class。
但我不喜欢那样,我想将只包含属性的 class 和我的 Starter class.
区分开来
我想到并在下面的示例中显示的一种肮脏的解决方法是将参数从 main 方法传递到我的 Starter class' 构造函数,用 CommandLine.run() 填充 ApplicationPropertiesProvider但只在那里实现一个空的 运行() 或 call() 方法,这样它会立即 return 到我的 Starter class 然后我开始业务逻辑。
这就是我要求的结果(分离),但那样看起来真的很愚蠢。
还有另一个刚刚出现的问题:如果我有多个 class 包含业务代码和它们自己的属性的标准情况(而不是单个 属性 提供 class): 是否可以通过一个 cli 调用填充多个不同的 classes,即调用 "test.jar command --a --b" 其中参数 "a" 直接进入 class [=23] 的实例=] 和 "b" 转到 "Y"?
的实例
public class Starter {
public static void main(String[] args) {
new Starter(args);
}
public Starter(String[] args) {
app = ApplicationPropertiesProvider.getInstance();
CommandLine.run(app, args);
//then kick off the business logic of the application
}
}
@Command(...)
public class ApplicationPropertiesProvider implements Runnable {
//annotated properties
@Option(...)
private String x;
@Override
public void run() { }
run
和 call
方法是 方便的 方法,允许应用程序减少其样板代码。您 不需要 使用它们。相反,您可以使用 parse
或 parseArgs
方法。这看起来像这样:
1 @Command(mixinStandardHelpOptions = true)
2 public class ApplicationPropertiesProvider { // not Runnable
3 //annotated properties
4 @Option(...)
5 private String x;
6 // ...
7 }
8
9 public class Starter {
10 public static void main(String[] args) {
11 ApplicationPropertiesProvider app = ApplicationPropertiesProvider.getInstance();
12 try {
13 ParseResult result = new CommandLine(app).parseArgs(args);
14 if (result.isUsageHelpRequested()) {
15 cmd.usage(System.out);
16 } else if (result.isVersionHelpRequested()) {
17 cmd.printVersionHelp(System.out);
18 } else {
19 new Starter(app); // run the business logic
20 }
21 } catch (ParameterException ex) {
22 System.err.println(ex.getMessage());
23 ex.getCommandLine().usage(out, ansi);
24 }
25 }
26
27 public Starter(ApplicationPropertiesProvider app) {
28 // kick off the business logic of the application
29 }
30 }
这很好,只是第 11-24 行是样板代码。您可以省略它,让 picocli 通过让带注释的对象实现 Runnable 或 Callable 来为您完成这项工作。
我理解您关于关注点分离的观点,并且对于业务逻辑和具有属性的 class 有不同的 classes。我有一个建议,但首先让我回答你的第二个问题:
Is it possible to populate multiple different classes with one cli call?
Picocli 支持允许您执行此操作的“Mixins”。例如:
class A {
@Option(names = "-a") int aValue;
}
class B {
@Option(names = "-b") int bValue;
}
class C {
@Mixin A a;
@Mixin B b;
@Option(names = "-c") int cValue;
}
// populate C's properties as well as the nested mixins
C c = CommandLine.populate(new C(), "-a=11", "-b=22", "-c=33");
assert c.a.aValue == 11;
assert c.b.bValue == 22;
assert c.cValue == 33;
现在,让我们把所有这些放在一起:
class A {
@Option(names = "-a") int aValue;
@Option(names = "-b") int bValue;
@Option(names = "-c") int cValue;
}
class B {
@Option(names = "-x") int xValue;
@Option(names = "-y") int yValue;
@Option(names = "-z") int zValue;
}
class ApplicationPropertiesProvider {
@Mixin A a;
@Mixin B b;
}
class Starter implements Callable<Void> {
@Mixin ApplicationPropertiesProvider properties = ApplicationPropertiesProvider.getInstance();
public Void call() throws Exception {
// business logic here
}
public static void main(String... args) {
CommandLine.call(new Starter(), args);
}
}
这为您提供了关注点分离:属性位于 ApplicationPropertiesProvider
,业务逻辑位于 Starter
class。
它还允许您将逻辑上属于一起的属性分组到单独的 class 中,而不是在 ApplicationPropertiesProvider
.
中有一个单一的垃圾场
Starter
class实现Callable
;这允许您省略上面的样板逻辑并在 main
.
中的一行代码中启动您的应用程序
在使用 cli 之前,我会有一个启动器 class,它调用我的 ApplicationPropertiesProvider class(它读取我的属性文件),然后启动业务逻辑。所以有一个分离,ApplicationPropertiesProvider 只有一份工作。
现在使用 picocli,guide/documentation 声明我必须使用 CommandLine.run(objectToPopulate, args) 或 CommandLine.call(objectToPopulate, args)。因此,使用 cli 参数 (ApplicationPropertiesProvider) 填充的 class 必须实现 Runnable 或 Callable。现在我可以将 Starter class 的启动代码粘贴到 运行() 或 call() 方法中,然后放弃 Starter class。 但我不喜欢那样,我想将只包含属性的 class 和我的 Starter class.
区分开来我想到并在下面的示例中显示的一种肮脏的解决方法是将参数从 main 方法传递到我的 Starter class' 构造函数,用 CommandLine.run() 填充 ApplicationPropertiesProvider但只在那里实现一个空的 运行() 或 call() 方法,这样它会立即 return 到我的 Starter class 然后我开始业务逻辑。 这就是我要求的结果(分离),但那样看起来真的很愚蠢。
还有另一个刚刚出现的问题:如果我有多个 class 包含业务代码和它们自己的属性的标准情况(而不是单个 属性 提供 class): 是否可以通过一个 cli 调用填充多个不同的 classes,即调用 "test.jar command --a --b" 其中参数 "a" 直接进入 class [=23] 的实例=] 和 "b" 转到 "Y"?
的实例public class Starter {
public static void main(String[] args) {
new Starter(args);
}
public Starter(String[] args) {
app = ApplicationPropertiesProvider.getInstance();
CommandLine.run(app, args);
//then kick off the business logic of the application
}
}
@Command(...)
public class ApplicationPropertiesProvider implements Runnable {
//annotated properties
@Option(...)
private String x;
@Override
public void run() { }
run
和 call
方法是 方便的 方法,允许应用程序减少其样板代码。您 不需要 使用它们。相反,您可以使用 parse
或 parseArgs
方法。这看起来像这样:
1 @Command(mixinStandardHelpOptions = true)
2 public class ApplicationPropertiesProvider { // not Runnable
3 //annotated properties
4 @Option(...)
5 private String x;
6 // ...
7 }
8
9 public class Starter {
10 public static void main(String[] args) {
11 ApplicationPropertiesProvider app = ApplicationPropertiesProvider.getInstance();
12 try {
13 ParseResult result = new CommandLine(app).parseArgs(args);
14 if (result.isUsageHelpRequested()) {
15 cmd.usage(System.out);
16 } else if (result.isVersionHelpRequested()) {
17 cmd.printVersionHelp(System.out);
18 } else {
19 new Starter(app); // run the business logic
20 }
21 } catch (ParameterException ex) {
22 System.err.println(ex.getMessage());
23 ex.getCommandLine().usage(out, ansi);
24 }
25 }
26
27 public Starter(ApplicationPropertiesProvider app) {
28 // kick off the business logic of the application
29 }
30 }
这很好,只是第 11-24 行是样板代码。您可以省略它,让 picocli 通过让带注释的对象实现 Runnable 或 Callable 来为您完成这项工作。
我理解您关于关注点分离的观点,并且对于业务逻辑和具有属性的 class 有不同的 classes。我有一个建议,但首先让我回答你的第二个问题:
Is it possible to populate multiple different classes with one cli call?
Picocli 支持允许您执行此操作的“Mixins”。例如:
class A {
@Option(names = "-a") int aValue;
}
class B {
@Option(names = "-b") int bValue;
}
class C {
@Mixin A a;
@Mixin B b;
@Option(names = "-c") int cValue;
}
// populate C's properties as well as the nested mixins
C c = CommandLine.populate(new C(), "-a=11", "-b=22", "-c=33");
assert c.a.aValue == 11;
assert c.b.bValue == 22;
assert c.cValue == 33;
现在,让我们把所有这些放在一起:
class A {
@Option(names = "-a") int aValue;
@Option(names = "-b") int bValue;
@Option(names = "-c") int cValue;
}
class B {
@Option(names = "-x") int xValue;
@Option(names = "-y") int yValue;
@Option(names = "-z") int zValue;
}
class ApplicationPropertiesProvider {
@Mixin A a;
@Mixin B b;
}
class Starter implements Callable<Void> {
@Mixin ApplicationPropertiesProvider properties = ApplicationPropertiesProvider.getInstance();
public Void call() throws Exception {
// business logic here
}
public static void main(String... args) {
CommandLine.call(new Starter(), args);
}
}
这为您提供了关注点分离:属性位于 ApplicationPropertiesProvider
,业务逻辑位于 Starter
class。
它还允许您将逻辑上属于一起的属性分组到单独的 class 中,而不是在 ApplicationPropertiesProvider
.
Starter
class实现Callable
;这允许您省略上面的样板逻辑并在 main
.