从Commons CLI迁移到Picocli

为何迁移?

从Commons CLI迁移到Picocli值得吗?从一个命令行解析器移动到另一个命令行解析器有什么好处?这不只是重新装修我们应用程序的起居室吗?

最终用户体验

最终用户有哪些好处?

命令行完成。基于Picocli的应用程序可以在bash和zsh shell以及基于JLine的交互式shell应用程序中完成命令行。

美观,高度可读的用法帮助信息。Commons CLI生成的使用帮助有点简约。开箱即用,Picocli生成帮助,使用ANSI样式和颜色进行对比,以强调重要信息,如命令,选项和参数。使用注释可以轻松自定义帮助消息布局。此外,还有一个帮助API,以防您想要不同的东西。有关示例屏幕截图,请参阅Picocli 自述文件。

通过@ -files或“参数文件 ” 支持非常大的命令行。有时,用户需要指定比操作系统或shell支持的命令行更长的命令行。当Picocli遇到以字符开头的参数时,它会将该文件的内容扩展到参数列表中。这允许应用程序处理任意长度的命令行。@

开发经验

作为开发人员,您有什么好处?

通常,Picocli应用程序的代码少于 Commons CLI等效代码。Picocli注释允许应用程序以声明方式定义选项和位置参数,其中所有信息都在一个位置。此外,Picocli提供了许多便利,如类型转换和自动帮助,可以处理一些机制,因此应用程序可以更专注于业务逻辑。本文的其余部分将更详细地说明这一点。

文档:Picocli有一个广泛的用户手册和详细的javadoc。

故障排除。Picocli具有内置的跟踪功能,便于故障排除。最终用户可以使用系统属性picocli.trace来控制跟踪级别。支持的水平OFFWARNINFO,和DEBUG。默认跟踪级别为WARN

未来的扩张

最后,除了立即支付之外,从Commons CLI迁移到Picocli还有未来的好处吗?

Picocli有许多高级功能。您的应用程序可能尚未使用这些功能,但如果您希望将来扩展您的应用程序,Picocli支持嵌套子命令(以及任何深度的子命令),mixin可以重用,可以轻松地与依赖注入容器集成,以及有一个不断增长的工具框架,用于从Picocli CommandSpec模型生成源代码,文档和配置文件。

最后,Picocli得到了积极的维护,而Commons CLI似乎已接近休眠状态,16年内发布了6个版本。

使用CheckStyle进行迁移的示例

命令行应用程序需要做三件事:

  1. 定义支持的选项

  2. 解析命令行参数

  3. 处理结果

让我们比较一下如何在Commons CLI和Picocli中完成,使用CheckStyle的com.puppycrawl.tools.checkstyle.Main命令行实用程序作为示例。

迁移之前和之后的完整源代码都在GitHub上。

定义选项和位置参数

使用Commons CLI定义选项

Commons CLI有多种方法来定义选项:在此对象上Options.addOption构建new Options(…)和调用方法,不推荐使用的OptionBuilder类和推荐的Option.Builder类。

Checkstyle Main类使用该Options.addOption方法。首先为选项名称定义一些常量:

/ **选项's'的名称。* /
private  static  final  String  OPTION_S_NAME  =  “s” ;
 
/ **选项't'的名称。* /
private  static  final  String  OPTION_T_NAME  =  “t” ;
 
/ **选项'--tree'的名称。* /
private  static  final  String  OPTION_TREE_NAME  =  “tree” ;
 
...... //甚至更多。Checkstyle Main共有26个选项。

Main.buildOptions方法使用这些常量来构造和返回Options定义支持的选项的Commons CLI 对象:

private  static  Options  buildOptions(){
    final  Options  options  =  new  Options();
    选项。addOption(OPTION_C_NAME,true,“设置要使用的检查配置文件。”);
    选项。addOption(OPTION_O_NAME,true,“设置输出文件。默认为stdout”);
    ...
    选项。addOption(OPTION_V_NAME,false,“打印产品版本并退出”);
    选项。addOption(OPTION_T_NAME,OPTION_TREE_NAME,false,
            “打印文件的抽象语法树(AST)” ;
    ...
    回报 选项 ;
}

使用Picocli定义选项

在Picocli中,您可以使用构建器以编程方式定义支持的选项,类似于Commons CLI方法,也可以使用注释进行声明性定义。

Picocli的编程API可能对动态应用程序非常有用,因为事先并不知道所有选项。如果你有兴趣的编程方法,看一看的CommandSpecOptionSpecPositionalParamSpec类。有关更多详细信息,另请参阅  Programmatic API。

在本文中,我们将使用Picocli注释。对于CheckStyle示例,这将类似于以下内容:

@Option(names  =  “ -  c”,description  =  “设置要使用的检查配置文件。”)
private  File  configurationFile ;
 
@Option(names  =  “ - o ”,description  =  “设置输出文件。默认为stdout”)
private  file  outputFile ;
 
@Option(names  =  “ -  v”,versionHelp  =  true,description  =  “打印产品版本并退出”)
private  boolean  versionHelpRequested ;
 
@Option(names  = { “ -  t”,“ -  tree” },description  =  “打印文件的抽象语法树(AST)”)
private  boolean  printAST ;

对照

陈述

使用Commons CLI,您可以通过调用具有String值的方法来构建规范。像这样的API的一个缺点是好的样式迫使客户端代码定义常量以避免“魔术值”,就像Checkstyle Main类一样尽职尽责。

使用Picocli,所有信息都在一个地方。注释只接受字符串文字,因此定义和用法会自动放在一起,而不需要声明常量。这样可以实现更清晰,更少的代码。

强类型

Commons CLI使用布尔标志来表示该选项是否接受参数。

Picocli允许您直接使用类型。根据类型,Picocli“知道”选项需要多少个参数:boolean字段没有参数CollectionMap并且数组字段可以为零到任意数量的参数,而任何其他类型意味着选项只需要一个参数。这可以自定义(请参阅参考资料arity),但大部分时间默认值都足够好。

Picocli鼓励您使用enum有限值有效值的选项或位置参数类型。Picocli不仅会为您验证输入,还可以在使用帮助消息中显示所有值@Option(description = "Valid values: ${COMPLETION-CANDIDATES}")。枚举还允许命令行完成以建议选项值的完成候选。

少代码

Picocli 将选项参数String值转换为字段类型。它不仅可以保存应用程序,还可以对用户输入进行一些最小的验证。如果转换失败,ParameterException则抛出一个用户友好的错误消息。

让我们看一个例子,看看它有多有用。该Checkstyle的Main类定义-x--exclude-regexp选项,允许使用指定数量的正则表达式的目录中排除。

使用Commons CLI,您需要将命令行上匹配的String值转换java.util.regex.Pattern为应用程序中的对象:

/ **
 *从解析结果中获取排除列表。
 * @param commandLine对象,表示解析命令行的结果
 * @return排除模式列表。
 * /
private  static  List < Pattern >  getExclusions(CommandLine  commandLine){
    final  List < Pattern >  result  =  new  ArrayList <>();
 
    如果(命令行。hasOption(OPTION_X_NAME)){
        对于(字符串 值:命令行。getOptionValues(OPTION_X_NAME)){
            结果。添加(模式。编译(值));
        }
    }
    返回 结果 ;
}

通过契约,在Picocli中,您只需在List<Pattern>(或Pattern[]数组)字段上声明选项。由于Picocli有一个内置转换器java.util.regex.Pattern,所需要的只是声明选项。转换代码完全消失了。如果-x在命令行上指定了一个或多个选项,Picocli将实例化并填充列表。

/ **允许用户指定要排除的路径的正则表达式的选项。* /
@Option(names  = { “ -  x”,“ -  exclude-regexp” },
        description  =  “要从CheckStyle中排除的目录的正则表达式”)
private  List < Pattern >  excludeRegex ;

选项名称

Commons CLI支持“短”和“长”选项,例如-t--tree。这并不总是你想要的。

Picocli允许选项具有任意数量的名称,带有任何前缀。例如,这在Picocli中完全没问题:

@Option(names  = { “ - cy ”,“ -  classpath”,“ -  class-path” })

位置参数

在Commons CLI中,您无法预先定义位置参数。相反,它的CommandLineparse结果类有一个方法getArgs,它将位置参数作为字符串数组返回。Checkstyle Main类使用它来创建File要处理的对象列表。

在Picocli中,位置参数是一等公民,就像命名选项一样。它们不仅可以是强类型的,而且不同位置的参数也可以具有不同的类型,并且每个参数都将在使用帮助消息中列出单独的条目和描述。

例如,Checkstyle Main类需要一个要处理的文件列表,因此我们声明一个字段并使用它进行注释@Parameters。该arity = "1..*"属性表示必须至少指定一个文件,否则Picocli将显示有关缺少参数的错误消息。

帮助选项

在Commons CLI中,创建一个具有必需选项的应用程序也很困难,该选项也有一个--help选项。Commons CLI对帮助选项没有特殊处理,并且会在用户指定时抱怨缺少必需的选项<command> --help

Picocli内置支持常见(和自定义)帮助选项。

解析命令行参数

Commons CLI有一个CommandLineParser接口,该接口parse返回一个CommandLine表示解析结果的方法。然后,应用程序调用CommandLine.hasOption(String)以查看是否设置了标志,或者CommandLine.getOptionValue(String)获取选项值。

Picocli在解析命令行参数时填充带注释的字段。Picocli的parse…方法还返回一个ParseResult可以查询指定选项和它们具有的值的方法,但是大多数应用程序实际上不需要使用ParseResult该类,因为它们可以简单地检查在解析期间注入到注释字段中的值。

处理结果

解析器完成后,应用程序需要运行其业务逻辑,但首先要检查一些事项:

  • 是否要求提供版本信息或使用帮助?如果是,请打印出所请求的信息并退出。

  • 用户输入无效吗?打印出包含详细信息的错误消息,打印使用帮助消息并退出。

  • 最后,运行业务逻辑并处理业务逻辑抛出的错误。

使用Commons CLI,它看起来像这样:

int  exitStatus ;
尝试 {
    CommandLine  commandLine  =  new  DefaultParser()。parse(buildOptions(),args);
 
    如果(命令行。hasOption(OPTION_VERSION)){ // --version
        系统。出。println(“Checkstyle版本:”  +  version());
        exitStatus  =  0 ;
    } 否则 如果(命令行。hasOption(OPTION_HELP)){ // --help
        printUsage(系统。出来);
        exitStatus  =  0 ;
    } else {
        exitStatus  =  runBusinessLogic(); // 商业逻辑
    }
} catch(ParseException  pex){ //输入无效
    exitStatus  =  EXIT_WITH_CLI_VIOLATION ;
    系统。犯错。的println(PEX。的getMessage());
    printUsage(系统。犯错);
} catch(CheckstyleException  ex){ //业务逻辑异常
    exitStatus  =  EXIT_WITH_CHECKSTYLE_EXCEPTION_CODE ;
    恩。printStackTrace();
}
系统。exit(exitStatus);

Picocli提供了一些方便的方法来处理上述大部分内容。通过发出命令,实现  Runnable 或  Callable。该应用程序可以专注于业务逻辑。最简单的是,它看起来像这样:

public  class  Main  实现 Callable < Integer > {
    public  static  void  main(String [] args){
        CommandLine。call(new  Main(),args);
    }
 
    public  Integer  call()抛出 CheckstyleException {
        //这里的业务逻辑
    }
}

Checkstyle  Main 类需要控制退出代码,并且对错误处理有一些严格的内部要求,因此我们最终没有使用便捷方法,并且保持解析结果处理与Commons CLI非常相似。在Picocli待办事项列表中改进了这一领域。

用法帮助消息

Picocli在支持的平台上的使用帮助消息中使用ANSI颜色和样式。这不仅看起来很好,还减少了用户的认知负担:对比使得命令,选项和参数等重要信息从周围文本中脱颖而出。

应用程序还可以在描述或使用帮助消息的其他部分中使用ANSI颜色和样式,并使用简单的标记@|bg(red) text with red background|@。请参阅用户手册的相关部分。

对于CheckStyle,我们将它保持在最低限度,CheckStyle的结果输出如下所示:

checkstyle用法

总结:最后一个提示

请注意,Commons CLI默认解析器将识别单个hyphen(-)和double hyphen(--)长选项,即使使用帮助消息仅显示带有双连字符的长选项。您需要决定是否继续支持此功能。

在Picocli中,@Option(names = "-xxx", hidden = true)如果要模仿与Commons CLI完全相同的行为,可以使用单个连字符声明长选项:Picocli 中的隐藏选项未显示在使用帮助消息中。

结论

从Commons CLI迁移到Picocli可以为最终用户提供更好的用户体验,并且可以为开发人员提供更高的可维护性和未来扩展潜力的显着优势。迁移是一个手动过程,但相对简单。

猜你喜欢

转载自blog.csdn.net/Tybyqi/article/details/85787550
今日推荐