commons-cli的使用及注解化

基本用法

假设我们要写一个命令行界面的天气查询程序,它的命令行参数列表如下:

代码如下:

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


你可能感兴趣的:(java命令行,commons-cli,java参数解析)