java 工具包学习之 Apache CommandLine

598 阅读1分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

1. wiki

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

2. 引入

使用 maven 来引入此工具包

<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. 创建
Option o = Option
    .builder("n")               // 短选项
    .longOpt("name")            // 长选项
    .hasArg(true)               // 是否含有参数,即该选项后有否参数
    .argName("jobName")         // 打印的帮助信息中,选项的名称
    .desc("name of job")        // 打印的帮助信息中,描述项
    .build();

2. 注册参数选项

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

Option o = ...;
Options options = new Options();
options.addOption(o);

3. 格式化

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

Options options = ...;
CommandLineParser parser = new DefaultParser();
CommandLine line = parser.parse(options, args);

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

4. 参数使用

CommandLine line = ...;
String name = null;
if (line.hasOption("n")) {
    name = line.getOptionValue("name");
}

4. 实战

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

  1. 可以使用枚举类,将预定义好的参数格式封装起来,方便使用和修改
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
}
  1. 注册之
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);
});
  1. 格式化以及帮助信息打印
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;

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

$ 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 方法来使用,只是隐藏了映射规则,其实还是约定大于规则的使用方法
// 实体类
public class OptionEntity {
    private String name;
    private String sql;
    // 省略 getter、setter
}

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

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;
    }

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

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