一起养成写作习惯!这是我参与「掘金日新计划 · 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. 实战
使用时一般利用枚举类、者配置文件或数据库表等静态方式,将参数规则定义好,之后将映射的实体类定义好,再对传参进行解析,利用反射,将传参值直接映射为相应实体类,方便后续使用
- 可以使用枚举类,将预定义好的参数格式封装起来,方便使用和修改
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
}
- 注册之
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);
});
- 格式化以及帮助信息打印
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
- 使用,利用反射,将传参与实体类绑定,此举主要是避免出现一些魔数或者静态值,可以定义映射规则,将之与实体类绑定,调用时就可以使用 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();