基本用法
假设我们要写一个命令行界面的天气查询程序,它的命令行参数列表如下:
代码如下:
public class App {
private static final Options OPTIONS = new Options();
//配置Options
static {
//设置每个Option
Option city = OptionBuilder.withArgName("city").isRequired().hasArg()
.withDescription("要查询的城市名称").withLongOpt("city").create('c');
Option day = OptionBuilder.withArgName("day").hasArg()
.withDescription("要查询的日期: 0表示当天,1表示第二天, 2表示第三天,依次类推")
.withLongOpt("day").create('d');
Option help = OptionBuilder.withDescription("显示帮助信息")
.withLongOpt("help").create('h');
OPTIONS.addOption(city);
OPTIONS.addOption(day);
OPTIONS.addOption(help);
}
public static void main(String[] args) throws Exception {
try { //解析参数
CommandLine cli = new BasicParser().parse(OPTIONS, args);
if (cli.hasOption('h')) {
new HelpFormatter().printHelp("java -jar weather.jar [OPTIONS]", OPTIONS, false);
return;
}
String city = cli.getOptionValue('c');
String day = cli.getOptionValue('d');
System.out.println(Wheather.get(city,
day == null ? 0 : Integer.parseInt(day)));
} catch (Exception e) {
new HelpFormatter().printHelp("java -jar weather.jar [OPTIONS]", OPTIONS, false);
}
}
}
可以看到代码的可读性不太好,而且如果有多个命令行参数时需要编写多个Options, 这会使App类变得臃肿。
使用注解
我们可以使用注解来对其进行简化
1.定义CLIOptions注解,用于标注要解析的参数类
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface CLIOptions {
}
2.定义CLIOptoin注解,用于标注和解释命令行参数的属性
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface CLIOption {
public boolean hasArg() default false;
public boolean hasArgs() default false;
public boolean isRequired() default false;
public String opt() default "";
public String longOpt() default "";
public String desc() default "";
public String argName() default "";
}
3.定义注解的处理器,用于生成Options实例
public class CLIOptionsProcessor {
public static Options process(Class<?> cliClz) {
if (cliClz.getAnnotation(CLIOptions.class) == null)
throw new RuntimeException(String.format(
"Target class: %s is not annotated with CLIOptions",
cliClz.getCanonicalName()));
Options options = new Options();
CLIOption cli = null;
for (Field field : cliClz.getDeclaredFields()) {
cli = field.getAnnotation(CLIOption.class);
if (cli != null)
options.addOption(parseOption(field, cli));
}
return options;
}
private static Option parseOption(Field field, CLIOption cli) {
if (cli.hasArg())
OptionBuilder.hasArg();
if (cli.hasArgs())
OptionBuilder.hasArgs();
if (cli.isRequired())
OptionBuilder.isRequired();
if (!cli.desc().isEmpty())
OptionBuilder.withDescription(cli.desc());
if (!cli.argName().isEmpty())
OptionBuilder.withArgName(cli.argName());
if (cli.longOpt().isEmpty())
OptionBuilder.withLongOpt(field.getName());
else
OptionBuilder.withLongOpt(cli.longOpt());
return OptionBuilder.create(cli.opt());
}
}
OK, 我们使用新添加的注解来对这个程序进行简化,代码如下:
@CLIOptions
public class App {
@CLIOption(argName = "city", isRequired = true, hasArg = true, desc = "要查询的城市名称", longOpt = "city", opt = "c")
private String city;
@CLIOption(argName = "day", hasArg = true, desc = "要查询的日期: 0表示当天,1表示第二天, 2表示第三天,依次类推", longOpt = "day", opt = "d")
private int day;
@CLIOption(desc = "显示帮助信息", longOpt = "help", opt = "h")
private boolean help;
public static void main(String[] args) throws Exception {
App app = new App();
Options options = CLIOptionsProcessor.process(App.class);
try {
CommandLine cli = new BasicParser().parse(options, args);
app.help = cli.hasOption('h');
if (app.help) {
app.showHelp(options);
return;
}
app.city = cli.getOptionValue('c');
String tmp = cli.getOptionValue('d');
app.day = tmp == null ? 0 : Integer.parseInt(tmp);
} catch (Exception e) {
app.showHelp(options);
}
System.out.println(Wheather.get(app.city, app.day));
}
public void showHelp(Options options) {
new HelpFormatter().printHelp("java -jar weather.jar [OPTIONS]",
options, false);
}
}