使用 Apache Commons CLI 工具包可以更加规范的处理命令行参数,同时支持多种参数风格:
POSIX:tar -zxvf foo.tar.gz
GNU:du --human-readable --max-depth=1
Java:java -Djava.awt.headless=true -Djava.net.useSystemProxies=true Foo
其他:gcc -O2 foo.c
ant -projecthelp
该工具处理命令行参数可分为三个阶段:定义阶段、解析阶段和服务阶段。
1. 定义阶段
定义待解析的命令行参数,设置命令行参数的限定属性,例如名称、是否必须、类型和描述信息等。
你可以通过创建 Option 类的实例来定义命令行参数,创建 Option 类的实例有多种途径:
Options options = new Options();
// 方式1:通过调用Options提供的方法
options.addOption("t", "thread",true, "并发数");
// 方式2:通过创建Option类的实例
options.addOption(new Option("c", "count", true, "调用次数"));
// 方式3:通过Option内部类Builder使用流式方式构建
options.addOption(Option.builder("s")
.longOpt("second")
.required()
.hasArg()
.desc("调用时长:单位秒")
.build());
复制代码
推荐使用流式方式构建 Option,直观流畅:
package com.ice.impl;
import com.ice.Parameter;
import com.ice.Starter;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
public class CommonCLIStarter extends Starter {
private static final String THREAD_NAME = "t";
private static final String COUNT_NAME = "c";
private static final String SECOND_NAME = "s";
private static final String PROPERTY_NAME = "p";
private static final String OUTPUT_NAME = "o";
public CommonCLIStarter(String[] args) {
super(args);
}
@Override
protected Parameter parse() {
// 1. 定义阶段
Options options = stage1();
// 2. 解析阶段
CommandLine commandLine = stage2(options);
// 3. 服务阶段
return stage3(commandLine);
}
private Options stage1() {
return new Options()
.addOption(Option.builder(THREAD_NAME)
.longOpt("thread")
.required()
.hasArg()
.desc("并发数")
.build())
.addOption(Option.builder(COUNT_NAME)
.longOpt("count")
.required()
.hasArg()
.desc("调用次数")
.build())
.addOption(Option.builder(SECOND_NAME)
.longOpt("second")
.required().hasArg()
.desc("调用时长:单位秒")
.build())
.addOption(Option.builder(PROPERTY_NAME)
.longOpt("property")
.hasArgs()
.valueSeparator()
.numberOfArgs(2)
.desc("自定义扩展属性")
.build())
.addOption(Option.builder(OUTPUT_NAME)
.longOpt("output")
.hasArg()
.desc("结果输出到文件中")
.build());
}
private CommandLine stage2(Options options) {
return null;
}
private Parameter stage3(CommandLine commandLine) {
return null;
}
}
复制代码
2. 解析阶段
命令行参数定义完毕,你需要使用默认的解析器 DefaultParser 类来解析实际的 args 数组,对于 DefaultParser 类主要的工作内容如下:
通过访问者模式处理 args 数组元素,验证该元素的合法性,校验参数的类型是标识符还是具体值等;
校验必要参数是否缺失;
参数默认值的设置。
继续编写解析阶段的代码:
private CommandLine stage2(Options options) {
CommandLineParser parser = new DefaultParser();
try {
return parser.parse(options, args);
} catch (Exception e) {
throw new IllegalArgumentException("Parsed parameters error", e);
}
}
复制代码
3. 服务阶段
通过解析阶段返回的 CommandLine 类的实例来访问命令行参数实际的值:
private Parameter stage3(CommandLine commandLine) {
PlanParameter parameter = new PlanParameter();
parameter.setThread(Integer.parseInt(commandLine.getOptionValue(THREAD_NAME)));
parameter.setCount(Integer.parseInt(commandLine.getOptionValue(COUNT_NAME)));
parameter.setSecond(Integer.parseInt(commandLine.getOptionValue(SECOND_NAME)));
parameter.setOutput(commandLine.getOptionValue(OUTPUT_NAME));
if (commandLine.hasOption(PROPERTY_NAME)) {
Properties properties = commandLine.getOptionProperties(PROPERTY_NAME);
if (properties != null && properties.size() > 0) {
Map<String, String> property = new HashMap<>();
parameter.setProperty(property);
for (Map.Entry<Object, Object> entity : properties.entrySet()) {
property.put(String.valueOf(entity.getKey()), String.valueOf(entity.getValue()));
}
}
}
return parameter;
}
复制代码
三个阶段的涉及代码编写完毕,让我们来编写单元测试用例,校验结果的正确性:
package com.ice;
import com.ice.impl.CommonCLIStarter;
import org.junit.Test;
public class CLIParameterTest extends ParameterTest {
@Test
@Override
public void startTest() {
Starter starter = new CommonCLIStarter(args);
Parameter parameter = starter.parse();
validate(parameter);
}
}
复制代码
单元测试通过:
该项目的代码已经上传 github:https://github.com/libingxin/api-test
评论