1

在使用 cli 之前,我将有一个启动类,它调用我的 ApplicationPropertiesProvider 类(它读取我的属性文件),然后启动业务逻辑。所以有一个分离,ApplicationPropertiesProvider 只做一份工作。

现在有了 picocli,指南/文档说明我必须使用 CommandLine.run(objectToPopulate, args) 或 CommandLine.call(objectToPopulate, args)。因此,使用 cli 参数 (ApplicationPropertiesProvider) 填充的类必须实现 Runnable 或 Callable。现在我可以将 Starter 类的启动代码粘贴到 run() 或 call() 方法中,然后放弃 Starter 类。但我不喜欢这样,我想将只持有属性的类和我的 Starter 类分开。

我想到并在下面的示例中显示的一种肮脏的解决方法是将参数从 main 方法传递给我的 Starter 类的构造函数,使用 CommandLine.run() 填充 ApplicationPropertiesProvider 但只实现一个空的 run() 或 call( ) 方法,因此它将立即返回到我的 Starter 类,然后我将在其中启动业务逻辑。这将是我要求(分离)的结果,但那样看起来真的很愚蠢。

还有一个刚刚出现的问题:如果我有多个包含业务代码的类以及它们自己的属性(而不是提供单个属性的类)的标准情况:是否可以使用一个 cli 调用填充多个不同的类,即调用“test.jar command --a --b”,其中参数“a”直接指向“X”类的实例,“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() { }
4

1 回答 1

5

runcall方法是允许应用程序减少其样板代码的便捷方法。不需要使用它们。相反,您可以使用parseorparseArgs方法。这看起来像这样:

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。

我理解您关于关注点分离的观点,并且对于业务逻辑和具有属性的类有不同的类。我有一个建议,但首先让我回答你的第二个问题:

是否可以通过一个 cli 调用填充多个不同的类?

Picocli 支持允许您执行此操作的“混合”。例如:

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类中。它还允许您将逻辑上属于一起的属性分组到单独的类中,而不是在ApplicationPropertiesProvider.

该类Starter实现Callable;这允许您省略上面的样板逻辑,并在main.

于 2019-01-10T05:18:21.947 回答