Java 第三方包-Apache commons-cli

1. wiki

当处理 java 传参问题时,可以简单的将 main 函数的 args[] 直接拿来处理,此时仅能利用传参顺序来处理,更加优雅的方式是使用 apache commons-cli 来获取长短参数来定义和获取参数

2. 引入

使用 maven 来引入此工具包

1
2
3
4
5
6
7
8
9
<properties>
<commons-cli.version>1.5.0</commons-cli.version>
</properties>

<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>${commons-cli.version}</version>
</dependency>

3. 使用

使用预先定义好参数的规则,对 main 函数的 args[] 解析

1. 创建参数选项

Option 是预定义的参数选项类

1
2
3
4
5
6
7
8
// 1. 创建
Option o = Option
.builder("n") // 短选项
.longOpt("name") // 长选项
.hasArg(true) // 是否含有参数,即该选项后有否参数
.argName("jobName") // 打印的帮助信息中,选项的名称
.desc("name of job") // 打印的帮助信息中,描述项
.build();

2. 注册参数选项

Options 本质是包含各个选项的数组

1
2
3
Option o = ...;
Options options = new Options();
options.addOption(o);

3. 格式化

利用预定义好的参数对命令行传参进行格式化,即对 main 函数的 args[] 中参数进行映射

1
2
3
4
5
6
7
Options options = ...;
CommandLineParser parser = new DefaultParser();
CommandLine line = parser.parse(options, args);

// 打印帮助信息
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("Job options", options);

4. 参数使用

1
2
3
4
5
CommandLine line = ...;
String name = null;
if (line.hasOption("n")) {
name = line.getOptionValue("name");
}

4. 实战

使用时一般利用枚举类、者配置文件或数据库表等静态方式,将参数规则定义好,之后将映射的实体类定义好,再对传参进行解析,利用反射,将传参值直接映射为相应实体类,方便后续使用

  1. 可以使用枚举类,将预定义好的参数格式封装起来,方便使用和修改

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public enum EOptParams {

    HELP("h", "help", "show optional parameters", false, ""),
    SQL("s", "sql", "sql file path", true, ""),
    NAME("n", "name", "name of job", true, "jobName"),
    ;

    private String simple;
    private String full;
    private String desc;
    private Boolean hasArg;
    private String argName;

    EOptParams(String simple, String full, String desc, Boolean hasArg, String argName) {
    this.simple = simple;
    this.full = full;
    this.desc = desc;
    this.argName = argName;
    this.hasArg = hasArg;
    }

    // 省略 getter、setter
    }
  2. 注册之

    1
    2
    3
    4
    5
    6
    Options options = new Options();
    Arrays.stream(EOptParams.values()).forEach(p -> {
    Option o = Option.builder(p.getSimple()).longOpt(p.getFull()).hasArg(p.getHasArg()).argName(p.getArgName())
    .desc(p.getDesc()).build();
    options.addOption(o);
    });
  3. 格式化以及帮助信息打印

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    CommandLineParser parser = new DefaultParser();
    CommandLine line = null;
    HelpFormatter formatter = new HelpFormatter();
    try {
    line = parser.parse(options, args);
    if (line.hasOption("h")) {
    formatter.printHelp("Job options", options);
    }
    } catch (ParseException e) {
    String message = "Input param is not true,use \"-h\" or \"--help\" to view available parameters.";
    throw RuntimeException(e, message);
    }
    return line;

调用时可以根据预定义好的参数进行传参

1
2
3
4
5
6
7
$ java -cp ${CLASS_PATH} ${MAIN_CLASS} -h

// 输出
usage: Job options
-h,--help show optional parameters
-n,--name <jobName> name of job submited to flink
-s,--sql sql file path
  1. 使用,利用反射,将传参与实体类绑定,此举主要是避免出现一些魔数或者静态值,可以定义映射规则,将之与实体类绑定,调用时就可以使用 get 方法来使用,只是隐藏了映射规则,其实还是约定大于规则的使用方法
    1
    2
    3
    4
    5
    6
    // 实体类
    public class OptionEntity {
    private String name;
    private String sql;
    // 省略 getter、setter
    }

尽量保证实体类字段与传参一致,这样可以简单的利用反射获取值,避免调用时,使用形如 line.getOptionValue("name") 等不优雅的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static OptionEntity getOptionEntity(String[] args) {
CommandLine line = ...;
OptionEntity options = new OptionEntity();

Class<?> clazz = options.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
String name = field.getName();
String value = line.getOptionValue(name);
if (StringUtils.isNotBlank(value)) {
field.setAccessible(true);
try {
field.set(options, value); // 约定前提 : 传参与实体类字段一致
} catch (Exception e) {
throw RuntimeException(e);
}
}
}
return options;
}

此时调用其来就简单且优雅

1
2
3
OptionEntity ops = getOptionEntity(args);
String sql = ops.getSql();
String name = ops.getName();