关于java:Picocli快速构建Java命令行程序

置信每个Java程序员都曾应用过 Scanner ,因编写出一个命令行程序而兴奋不已。

命令行程序也颇为实用,然而,应用Java来编写一个功能强大的命令行程序却并不容易,次要有以下几方面的痛点:

  • 没有成熟的框架来封装参数接管参数提醒以及参数校验
  • 很难解决参数的互斥以及特定命令的互相依赖关系
  • 无奈进行命令主动补全
  • 因为JVM解释执行字节码,并且JIT无奈在短时执行中发挥作用,Java命令行程序启动迟缓
  • 集成SpringBoot及其它组件后,启动更加迟缓

上述这些问题都能够应用Picocli来解决

本文次要向大家介绍Picocli,以及剖析它是如何解决上述的问题,并介绍应用其构建一个控制台程序的根本流程,具体的使用指南请至官网文档。

官网:https://picocli.info

根本介绍

Picocli 致力于以最简洁的形式来创立一个基于JVM的功能强大的命令行程序。

Picocli aims to be the easiest way to create rich command line applications that can run on and off the JVM. 

下图是利用Picocli构建命令行程序,输入 checksum -h 后打印出的帮忙文档:

疾速开始

定义一个命令有两种形式:

  1. 应用成员属性来接管命令行参数,实现 Callable 接口并覆写 call() 办法定义业务流程
  2. 应用类办法的参数来接管命令行参数,办法外部就是业务流程,集体举荐这种。

以下是两种定义方法:

实现Callable接口

// 定义checksum命令,以及命令的提示信息
@Command(name = "checksum", mixinStandardHelpOptions = true, version = "checksum 4.0",
         description = "Prints the checksum (MD5 by default) of a file to STDOUT.")
class CheckSum implements Callable<Integer> {

    @Parameters(index = "0", description = "The file whose checksum to calculate.")
    private File file;

    @Option(names = {"-a", "--algorithm"}, description = "MD5, SHA-1, SHA-256, ...")
    private String algorithm = "MD5";

    @Override
    public Integer call() throws Exception { // your business logic goes here...
        byte[] fileContents = Files.readAllBytes(file.toPath());
        byte[] digest = MessageDigest.getInstance(algorithm).digest(fileContents);
        System.out.printf("%0" + (digest.length*2) + "x%n", new BigInteger(1, digest));
        return 0;
    }

    // this example implements Callable, so parsing, error handling and handling user
    // requests for usage help or version help can be done with one line of code.
    public static void main(String... args) {
        int exitCode = new CommandLine(new CheckSum()).execute(args);
        System.exit(exitCode);
    }
}

类办法

# 上述代码可简化为:
@Command(name = "checksum", mixinStandardHelpOptions = true, version = "checksum 4.0",
        description = "Prints the checksum (MD5 by default) of a file to STDOUT.")
public int checkSum(@Parameters(index = "0", description = "The file whose checksum to calculate.") File file,
                    @Option(names = {"-a", "--algorithm"}, description = "MD5, SHA-1, SHA-256, ...") String algorithm) throws Exception {
    byte[] fileContents = Files.readAllBytes(file.toPath());
    byte[] digest = MessageDigest.getInstance(algorithm).digest(fileContents);
    System.out.printf("%0" + (digest.length * 2) + "x%n", new BigInteger(1, digest));
    return 0;
}

如何解决痛点

置信你曾经对Picocli有了初步理解,上面就开始答复Picocli是如何解决文章结尾提出的痛点的。

@Option与@Parameter

不同点

  • @Option 是选项参数,分为参数名参数内容,应用参数名来辨别不同参数
  • @Parameter地位参数,应用地位来辨别不同参数
  • @Option 能够叠加
  • @Option 润饰的参数若为布尔类型,则能够不加省略参数内容

相同点

  • 都能够定义默认值
  • 都能够为可选参数,默认 @Option(required = false) ,@Parameter 默认是必填,但能够通过 arity 属性来控制参数个数, arity = "0..1" 示意参数为0 – 1个。
  • 都能够应用 interactive = true 开启交互模式输出,例如明码等敏感信息

arity的默认值以及应用办法:

详见:https://picocli.info/#_arity

丰盛的参数类型

如果咱们应用Scanner读取参数,咱们须要手动进行参数解析和类型转换,而Picocli提供了开箱即用的参数类型解析器,反对绝大部分的Java罕用类,能够提前对类型进行查看,并且简化了类型解析过程。

详见:https://picocli.info/#_built_…

参数校验

Picocli反对多种参数校验形式:

  • 内置的required和arity查看
  • 手动在业务中查看
  • JSR-380 BeanValidation注解

BeanValidation注解在Spring MVC的参数校验中经常出现,同样Picocli也对其反对,如常见注解:@NotNull, @NotEmpty, @Min, @Max等,弱小而实用。

详见:https://picocli.info/#_valida…

@ArgGroup(参数组)

@ArgGroup 用来定义一组参数,通过 @ArgGroup(exclusive = ture/false, multiplicity = "...") 与 @Option(required=ture/false) 和 @Parameter(@arity = "...") 联合,能够实现以下性能:

  • 一组互斥命令
  • 一组参数用户必须至多输出一个
  • 一组具备依赖关系的命令,即若未输出某一参数,就不能输出某些参数。
  • 复合参数, @ArgGroup 可嵌套

详见:https://picocli.info/#_argument_groups

SpringBoot集成

Picocli能够轻松地与SpringBoot联合.

<dependency>
    <groupId>info.picocli</groupId>
    <artifactId>picocli-spring-boot-starter</artifactId>
    <version>${picocli.version}</version>
</dependency>
@SpringBootApplication
public class MySpringMailer implements CommandLineRunner, ExitCodeGenerator {

    private IFactory factory;        
    private MailCommand mailCommand; 
    private int exitCode;

    // 结构器注入
    MySpringMailer(IFactory factory, MailCommand mailCommand) {
        this.factory = factory;
        this.mailCommand = mailCommand;
    }

    @Override
    public void run(String... args) {
        // let picocli parse command line args and run the business logic
        exitCode = new CommandLine(mailCommand, factory).execute(args);
    }

    @Override
    public int getExitCode() {
        return exitCode;
    }

    public static void main(String[] args) {
        // let Spring instantiate and inject dependencies
        System.exit(SpringApplication.exit(SpringApplication.run(MySpringMailer.class, args)));
    }
}

详见:https://picocli.info/#_spring_boot_example

主动补全

Picocli能够主动生成命令补全脚本,从而实现按下<TAB>键的时候,能够进行补全提醒主动补全

详见:https://picocli.info/#_tab_autocomplete

执行命令

应用Picocli构建的命令行程序的应用形式大抵分为两种:

  1. 应用 alias 命令来简化执行 java -jar
  2. 打包为可执行文件,退出环境变量

alias

alias mycommand='java -cp "/path/to/picocli-4.6.1.jar:/path/to/myapp.jar" org.myorg.MainClass'

GraalVM Native

GraalVM Native Image 使得开发者能够AOT(ahead-of-time compile 提前)编译Java代码为可执行文件,也就是一个native image。

家喻户晓,以往启动一个SpringBoot程序,个别都会破费几十秒,这对命令行程序的使用者十分不敌对,因为每一次命令都须要很长的延时能力失去响应。

而通过GraalVM Native Image编译后的可执行文件启动速度十分快,快到足够满足命令行程序所需的响应时延。

同时,咱们可能依靠Spring Native我的项目提供的依赖和Maven插件,来简化打包流程。

详见:

Picocli文档:https://picocli.info/#_graalvm_native_image

Spring Native文档:https://docs.spring.io/spring-native/docs/current/reference/htmlsingle/

示例我的项目:https://docs.spring.io/spring-native/docs/current/reference/htmlsingle/

结语

因为篇幅和工夫无限,本文只分享了Picocli的个性与应用形式,倡议读者查看官网文档以取得最新、最全的使用指南。
之后若有工夫或者大家有趣味,我会持续更新具体的应用形式。


感谢您浏览本文,您的关注与点赞是对我最大的反对!

关注我的公众号“语冰Yubing”可接管最新推送,外面也有我分享的一些优质资源。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理