@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@Target({ FIELD, METHOD })
public @interface Parameter {
/**
* An array of allowed command line parameters (e.g. "-d", "--outputdir", etc...).
* If this attribute is omitted, the field it's annotating will receive all the
* unparsed options. There can only be at most one such annotation.
*/
String[] names() default {};
/**
* A description of this option.
*/
String description() default "";
/**
* Whether this option is required.
*/
boolean required() default false;
/**
* The key used to find the string in the message bundle.
*/
String descriptionKey() default "";
/**
* How many parameter values this parameter will consume. For example,
* an arity of 2 will allow "-pair value1 value2".
*/
public static int DEFAULT_ARITY = -1;
int arity() default DEFAULT_ARITY;
/**
* If true, this parameter is a password and it will be prompted on the console
* (if available).
*/
boolean password() default false;
/**
* The string converter to use for this field. If the field is of type <tt>List</tt>
* and not <tt>listConverter</tt> attribute was specified, JCommander will split
* the input in individual values and convert each of them separately.
*/
Class<? extends IStringConverter<?>> converter() default NoConverter.class;
/**
* The list string converter to use for this field. If it's specified, the
* field has to be of type <tt>List</tt> and the converter needs to return
* a List that's compatible with that type.
*/
Class<? extends IStringConverter<?>> listConverter() default NoConverter.class;
/**
* If true, this parameter won't appear in the usage().
*/
boolean hidden() default false;
/**
* Validate the parameter found on the command line.
*/
Class<? extends IParameterValidator>[] validateWith() default NoValidator.class;
/**
* Validate the value for this parameter.
*/
Class<? extends IValueValidator>[] validateValueWith() default NoValueValidator.class;
/**
* @return true if this parameter has a variable arity. See @{IVariableArity}
*/
boolean variableArity() default false;
/**
* What splitter to use (applicable only on fields of type <tt>List</tt>). By default,
* a comma separated splitter will be used.
*/
Class<? extends IParameterSplitter> splitter() default CommaParameterSplitter.class;
/**
* If true, console will not echo typed input
* Used in conjunction with password = true
*/
boolean echoInput() default false;
/**
* If true, this parameter is for help. If such a parameter is specified,
* required parameters are no longer checked for their presence.
*/
boolean help() default false;
/**
* If true, this parameter can be overwritten through a file or another appearance of the parameter
* @return nc
*/
boolean forceNonOverwritable() default false;
/**
* If specified, this number will be used to order the description of this parameter when usage() is invoked.
* @return
*/
int order() default -1;
}
/**
* An interface that converts strings to any arbitrary type.
*
* If your class implements a constructor that takes a String, this
* constructor will be used to instantiate your converter and the
* parameter will receive the name of the option that's being parsed,
* which can be useful to issue a more useful error message if the
* conversion fails.
*
* You can also extend BaseConverter to make your life easier.
*
* @author cbeust
*/
public interface IStringConverter<T> {
/**
* @return an object of type <T> created from the parameter value.
*/
T convert(String value);
}
IParameterValidator支持参数值的校验。
/**
* The class used to validate parameters.
*
* @author Cedric Beust <cedric@beust.com>
*/
public interface IParameterValidator {
/**
* Validate the parameter.
*
* @param name The name of the parameter (e.g. "-host").
* @param value The value of the parameter that we need to validate
*
* @throws ParameterException Thrown if the value of the parameter is invalid.
*/
void validate(String name, String value) throws ParameterException;
}
有时候我们用Java开发了一个小工具,希望通过命令行(CLI)或者图形界面直接调用。命令行相较于图形界面,实现迅速,交互更接近于程序员人群,本文主要介绍Java在命令行交互上的应用,我们不妨先看看命令行的两种风格:
tar -zxvf foo.tar.gz
java -Djava.awt.headless=true -Djava.net.useSystemProxies=true Foo
JCommander介绍
根据官方文档,我简单总结了JCommander的几个特点:
注解驱动 它的核心功能命令行参数定义是基于注解的,这也是我选择用它的主要原因。我们可以轻松做到命令行参数与属性的映射,属性除了是String类型,还可以是Integer、boolean,甚至是File、集合类型。
功能丰富 它同时支持文章开头的两种命令行风格,并且提供了输出帮助文档的能力(
usage()
),还提供了国际化的支持。高度扩展 下文会详述。
在看具体应用示例前,我们先读懂核心注解
@Parameter
的源码(你大可以跳过下面这段长长的源码,直接看示例),以此来了解它向我们展示了哪些方面的能力:JCommander 应用示例
在一般应用场景,我们可能只需要设置
@Parameter
以下几个属性值:-old
下面是一个完整的示例,它用来比较两份文档,然后输出差异。源码在https://github.com/Sayi/swagger-diff上。
运行命令行查看帮助文档,输出结果如下:
这个示例像我们展示了JCommander注解的强大,我们仅仅使用注解就完成了所有参数的定义。注意,对于boolean为true的参数,我们只需要输入参数名,比如
--help
,而不是--help=true
。示例中使用了
usage()
方法即可完美的输出帮助文档。JCommander扩展:增加正则表达式校验
JCommander是高度扩展的,两个核心接口定义了扩展的能力。
IStringConverter
支持String类型的参数值可以转化为任意其他类型的属性。IParameterValidator
支持参数值的校验。在阅读上文示例中,可能会有些许疑问,比如
@Regex
是什么注解,JCommander并没有提供正则表达式校验参数值的功能。对于很多参数,我们都有校验的场景,比如值只能是几个可选值,或者是在一定范围内,IParameterValidator 和IParameterValidator2实现了参数校验了功能,接下来我们将基于接口
IParameterValidator2
扩展JCommander,同样,我们只需要使用注解即可。@Regex("(2\\.0|1\\.0)")
import static java.lang.annotation.ElementType.FIELD;
import java.lang.annotation.Retention; import java.lang.annotation.Target;
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @Target({ FIELD }) public @interface Regex {
String value() default "";
}
至此,正则校验已完成。
更多More: Apache Commons CLI
从源码中可以看到,JCommander默认提供了不少转化器。
Java在命令行交互的应用,还有很多工具。另一个使用比较广泛的是Apache Commons CLI: http://commons.apache.org/proper/commons-cli/index.html,它比JCommander支持更多的命令行风格,但是扩展能力不够。
PS: 我们一直在招人,Java,杭州,阿里系独角兽公司,E轮融资,欢迎投简历至adasai90Atgmail.com。