使用 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
评论