• 使用 JCommander 解析命令行参数


    前言

    如果你想构建一个支持命令行参数的程序,那么 jcommander 非常适合你,jcommander 是一个只有几十 kb 的 Java 命令行参数解析工具,可以通过注解的方式快速实现命令行参数解析。

    这篇教程会通过介绍 jcommadner ,快速的创建一个命令行程序,最后支持的命令参数功能如下图。

    图片

    这个命令行工具仿照 git 操作命令,主要提供了如下功能命令:

    1. 1. git-app.jar -help 查看命令帮助信息。

    2. 2. git-app.jar -version 查看当前版本号。

    3. 3. git-app.jar clone http://xxxx 通过 URL 克隆一个仓库。

    4. 4. git-app.jar add file1 file2 暂存 file1 文件 file2 文件。

    5. 5. git-app.jar commit -m "注释" 提交并添加注释。

    jcommander 引入

    截止文章编写时间,最新版本如下:

    1. <!-- https://mvnrepository.com/artifact/com.beust/jcommander -->
    2. <dependency>
    3.     <groupId>com.beust</groupId>
    4.     <artifactId>jcommander</artifactId>
    5.     <version>1.82</version>
    6. </dependency>

    jcommander 参数绑定

    命令行解析中,参数解析与绑定是最实用的一个场景,jcommander 使用 Parameter 注解进行参数绑定。我们定义一个 GitCommandOptions.java 类来测试参数绑定。

    1. package com.wdbyte.jcommander.v1;
    2. import com.beust.jcommander.Parameter;
    3. /**
    4.  * @author https://www.wdbyte.com
    5.  */
    6. public class GitCommandOptions {
    7.     @Parameter(names = {"clone"},
    8.         description = "克隆远程仓库数据")
    9.     private String cloneUrl;
    10.     public String getCloneUrl() {
    11.         return cloneUrl;
    12.     }
    13. }

    使用 jcommander 结合 GitCommandOptions 来解析参数。

    1. package com.wdbyte.jcommander.v1;
    2. import com.beust.jcommander.JCommander;
    3. /**
    4.  * @author https://www.wdbyte.com
    5.  */
    6. public class GitApp {
    7.     public static void main(String[] args) {
    8.             // args = new String[]{"clone","http://www.wdbyte.com/test.git"};
    9.         GitCommandOptions gitCommandOptions = new GitCommandOptions();
    10.         JCommander commander = JCommander.newBuilder()
    11.             .addObject(gitCommandOptions)
    12.             .build();
    13.         commander.parse(args);
    14.         System.out.println("clone " + gitCommandOptions.getCloneUrl());
    15.     }
    16. }

    打包后可以执行命令参数:

    1. $ java -jar git-app.jar clone http://www.wdbyte.com/test.git
    2. clone http://www.wdbyte.com/test.git

    这里是一个字符串参数,需要在命令中输出参数值,对于 boolean 类型的参数,不需要传值,有命令即为 true 值。

    参数名称

    @Parameter 注解中的 names 属性可以定义参数的名称。且可以指定多个参数名称,让我再添加 version 参数和 help 参数,同时设置参数别名。这两个参数是 boolean 类型。

    1. @Parameter(names = {"help""-help""-h"},
    2.     description = "查看帮助信息",
    3.     help = true)
    4. private boolean help;
    5. @Parameter(names = {"version""-version""-v"},
    6.     description = "显示当前版本号")
    7. private boolean version = false;

    参数限制

    clone 参数可以接受一个要克隆的 URL 链接,但是正常情况下只需要一个 URL 链接。可以通过 arity = 1 进行限制。

    1. @Parameter(names = {"clone"},
    2.     description = "克隆远程仓库数据",
    3.     arity = 1)
    4. private String cloneUrl;

    帮助信息

    使用 usage() 参数可以打印命令帮助信息。

    1. GitCommandOptions gitCommandOptions = new GitCommandOptions();
    2. JCommander commander = JCommander.newBuilder()
    3.     .addObject(gitCommandOptions)
    4.     .build();
    5. commander.parse(args);
    6. // 打印帮助信息
    7. commander.usage();

    运行输出帮助信息:

    1. $ java -jar git-app.jar
    2. Usage<main class> [options]
    3.   Options:
    4.     clone
    5.       克隆远程仓库数据
    6.     help, -help, -h
    7.       查看帮助信息
    8.     version, -version, -v
    9.       显示当前版本号
    10.       Defaultfalse

    虽然正确的输出了帮助信息,但是其中有 main class 这段,是因为我们没有指定项目名称,我们指定项目名称为 git-app

    1. JCommander commander = JCommander.newBuilder()
    2.             .programName("git-app")
    3.             .addObject(gitCommandOptions)
    4.             .build();

    参数排序

    在帮助信息中,如果想要自定义参数顺序,可以通过 order = 来排序,数字越小越靠前。

    1. @Parameter(names = {"version""-version""-v"},
    2.     description = "显示当前版本号",
    3.     order = 2)
    4. private boolean version = false;

    参数绑定完整测试

    1. package com.wdbyte.jcommander.v2;
    2. import com.beust.jcommander.Parameter;
    3. /**
    4.  * @author https://www.wdbyte.com
    5.  */
    6. public class GitCommandOptions {
    7.     @Parameter(names = {"help""-help""-h"},
    8.         description = "查看帮助信息",
    9.         order = 1,
    10.         help = true)
    11.     private boolean help;
    12.     @Parameter(names = {"clone"},
    13.         description = "克隆远程仓库数据",
    14.         order = 3,
    15.         arity = 1)
    16.     private String cloneUrl;
    17.     @Parameter(names = {"version""-version""-v"},
    18.         description = "显示当前版本号",
    19.         order = 2)
    20.     private boolean version = false;
    21.     //...get method
    22. }

    GitApp.java

    1. package com.wdbyte.jcommander.v2;
    2. import com.beust.jcommander.JCommander;
    3. public class GitApp {
    4.     public static void main(String[] args) {
    5.         GitCommandOptions gitCommandOptions = new GitCommandOptions();
    6.         JCommander commander = JCommander.newBuilder()
    7.             .programName("git-app")
    8.             .addObject(gitCommandOptions)
    9.             .build();
    10.         commander.parse(args);
    11.         // 打印帮助信息
    12.         if (gitCommandOptions.isHelp()) {
    13.             commander.usage();
    14.             return;
    15.         }
    16.         if (gitCommandOptions.isVersion()) {
    17.             System.out.println("git version 2.24.3 (Apple Git-128)");
    18.             return;
    19.         }
    20.         if (gitCommandOptions.getCloneUrl() != null) {
    21.             System.out.println("clone " + gitCommandOptions.getCloneUrl());
    22.         }
    23.     }
    24. }

    运行测试:

    图片

    jcommander 参数验证

    在上面的例子中, 假设 clone 命令传入的参数必须是一个 URL,那么我们就要进行参数验证,jcommander 也提供了特有的参数验证方式。

    1. 1. 编写参数验证类,需要实现 IParameterValidator 接口。

      1. package com.wdbyte.jcommander.v3;
      2. import java.net.MalformedURLException;
      3. import java.net.URL;
      4. import com.beust.jcommander.IParameterValidator;
      5. import com.beust.jcommander.ParameterException;
      6. /**
      7.  * @author https://www.wdbyte.com
      8.  */
      9. public class UrlParameterValidator implements IParameterValidator {
      10.     @Override
      11.     public void validate(String key, String value) throws ParameterException {
      12.         try {
      13.             new URL(value);
      14.         } catch (MalformedURLException e) {
      15.             throw new ParameterException("参数 " + key + " 的值必须是 URL 格式");
      16.         }
      17.     }
      18. }
    2. 2. clone 参数指定验证类。

      1. @Parameter(names = {"clone"},
      2.     description = "克隆远程仓库数据",
      3.     validateWith = UrlParameterValidator.class,
      4.     order = 3,
      5.     arity = 1)
      6. private String cloneUrl;

    运行测试:

    1. $ java -jar git-app.jar clone https://www.wdbyte.com/test.git
    2. clone https://www.wdbyte.com/test.git
    3. $ java -jar git-app.jar clone test.git
    4. Exception in thread "main" com.beust.jcommander.ParameterException: 参数 clone 的值必须是 URL 格式
    5.     at com.wdbyte.jcommander.v3.UrlParameterValidator.validate(UrlParameterValidator.java:19)
    6.     at com.beust.jcommander.ParameterDescription.validateParameter(ParameterDescription.java:377)
    7.     at com.beust.jcommander.ParameterDescription.validateParameter(ParameterDescription.java:344)

    jcommander 子命令

    在使用 git 时,我们经常会使用下面两个命令。

    1. 1. git add file1 file2 暂存 file1 文件 file2 文件。

    2. 2. git commit -m "注释" 提交并添加注释。

    什么是子命令

    这是一种很常见的操作,git commit 除了可以跟 -m 子参数外,还可以跟各种参数,通过 git 帮助文档可以看到。

    1. git commit [-a | --interactive | --patch] [-s] [-v] [-u<mode>] [--amend]
    2.            [--dry-run] [(-c | -C | --fixup | --squash) <commit>]
    3.            [-F <file> | -m <msg>] [--reset-author] [--allow-empty]
    4.            [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
    5.            [--date=<date>] [--cleanup=<mode>] [--[no-]status]
    6.            [-i | -o] [-S[<keyid>]] [--] [<file>...]

    这种有子参数的情况,我们可以称 commit 为 git 的一个子命令,使用 jcommander 如何配置子命令呢?

    jcommander 子命令实现

    我们新增子命令对应的参数类 GitCommandCommit.java.

    1. package com.wdbyte.jcommander;
    2. import com.beust.jcommander.Parameter;
    3. import com.beust.jcommander.Parameters;
    4. /**
    5.  * git commit -m "desc"
    6.  * @author https://www.wdbyte.com
    7.  */
    8. @Parameters(commandDescription = "提交文件", commandNames = "commit")
    9. public class GitCommandCommit {
    10.   
    11.     public static final String COMMAND = "commit";
    12.   
    13.     @Parameter(names = {"-comment""-m"},
    14.         description = "请输入注释",
    15.         arity = 1,
    16.         required = true)
    17.     private String comment;
    18.     public String getComment() {
    19.         return comment;
    20.     }
    21. }

    代码中使用 @Parameters 注解指定了子命令为 commit,同时使用 @Paramete 注解指定子参数 -m,同时 -m 参数是必须的,使用属性 required = true 来指定。

    使用 GitCommandCommit:

    使用 addCommand 添加 Commit 命令参数类。

    1. GitCommandOptions gitCommandOptions = new GitCommandOptions();
    2. GitCommandCommit commandCommit = new GitCommandCommit();
    3. JCommander commander = JCommander.newBuilder()
    4.     .programName("git-app")
    5.     .addObject(gitCommandOptions)
    6.     .addCommand(commandCommit)
    7.     .build();
    8. commander.parse(args);
    9. String parsedCommand = commander.getParsedCommand();
    10. if ("commit".equals(parsedCommand)) {
    11.     System.out.println(commandCommit.getComment());
    12. }

    运行测试:

    1. $ java -jar git-app.jar commit -m '注释一下'
    2. 注释一下

    同上,我们可以添加 add 命令对应的参数类:GitCommandAdd.java. 这次我们定义一个 List 类型参数,但是不在属性上指定子参数名称。

    1. package com.wdbyte.jcommander.v5;
    2. import java.util.List;
    3. import com.beust.jcommander.Parameter;
    4. import com.beust.jcommander.Parameters;
    5. /**
    6.  * git add file1 file2
    7.  *
    8.  * @author https://www.wdbyte.com
    9.  */
    10. @Parameters(commandDescription = "暂存文件", commandNames = "add", separators = " ")
    11. public class GitCommandAdd {
    12.     public static final String COMMAND = "add";
    13.     @Parameter(description = "暂存文件列表")
    14.     private List<String> files;
    15.     public List<String> getFiles() {
    16.         return files;
    17.     }
    18. }

    同样添加到子命令:

    1. JCommander commander = JCommander.newBuilder()
    2.     .programName("git-app")
    3.     .addObject(gitCommandOptions)
    4.     .addCommand(commandCommit)
    5.     .addCommand(commandAdd)
    6.     .build();
    7. commander.parse(args);
    8. if ("add".equals(parsedCommand)) {
    9.     for (String file : commandAdd.getFiles()) {
    10.         System.out.println("暂存文件:" + file);
    11.     }
    12. }

    运行测试:

    1. $ java -jar git-app.jar add file1.txt file2.txt
    2. 暂存文件:file1.txt
    3. 暂存文件:file2.txt

    jcommander 参数转换

    在上面的 GitCommandAdd 代码中,add 命令传入的都是文件路径,现在是使用 List 来接收入参,通常情况想我们可能需要直接转换成方便操作的类型,如 File 或者 Path,这该如何方面的转换呢,jcommander 也提供了方便转换类。

    首先编写一个转换类 FilePathConverter 用于把入参转换成 Path 类,同时校验文件是否存在

    1. package com.wdbyte.jcommander;
    2. import java.nio.file.Files;
    3. import java.nio.file.Path;
    4. import java.nio.file.Paths;
    5. import com.beust.jcommander.IStringConverter;
    6. import com.beust.jcommander.ParameterException;
    7. /**
    8.  *
    9.  * @author https://www.wdbyte.com
    10.  */
    11. public class FilePathConverter implements IStringConverter<Path> {
    12.     @Override
    13.     public Path convert(String filePath) {
    14.         Path path = Paths.get(filePath);
    15.         if (Files.exists(path)) {
    16.             return path;
    17.         }
    18.         throw new ParameterException(String.format("文件不存在,path:%s", filePath));
    19.     }
    20. }

    通过注解指定转换类:

    1. @Parameter(description = "暂存文件列表", converter = FilePathConverter.class)
    2. private List<Path> files;

    打包测试:

    1. $ java -jar git-app.jar add file1 file2
    2. 文件不存在,path:file1
    3. $ ls -l
    4. total 12448
    5. drwxr-xr-x  2 darcy  staff    64B  6 15 21:10 archive-tmp
    6. drwxr-xr-x  3 darcy  staff    96B  6 15 21:10 classes
    7. drwxr-xr-x  3 darcy  staff    96B  6 15 21:10 generated-sources
    8. -rw-r--r--  1 darcy  staff   5.6M  6 16 20:44 git-app.jar
    9. $ git-app.jar git-app.jar
    10. 暂存文件:git-app.jar

    总体测试

    图片

  • 相关阅读:
    CSS 浮动布局
    nuxt使用echarts
    python 第五章
    dotnet 使用 Crossgen2 对 DLL 进行 ReadyToRun 提升启动性能
    如何优化CRM的线索
    今天早上在使用Layui的时候,排查出了自己的BUG
    防不胜防?网络钓鱼攻击常用手法盘点与防护建议
    quartz框架(七)-JobStore
    ARM_基础之RAS
    VueCLI脚手架
  • 原文地址:https://blog.csdn.net/H_Sino/article/details/136622786