JCommander:Java外部参数解析利器
13/Mar 2015
最近需要把项目交给别人进行运维,为了不让接手之人涉及太多繁琐细节,我把一些定义在final类中的不可变量抽取出来,把项目变成可外部配置的。用配置文件可以达到这个目的,但由于配置之间有相互依赖关系,比如:
public static boolean local = false;
public static String host = (local) ? "127.0.0.1" : "172.16.3.142";
public static int port = (local) ? 6379 : 6380;
原本只需要改变local, 用配置文件的话与local值有依赖关系的地方都要面临修改。
后来打算用命令行参数实现可外部动态配置,如果自己动手实现完善的命令行参数解析,可不是一项little job。 比较了几款开源的工具,还是选择了JCommander。主要原因是被它官网的slogan打动了:
Because life is too short to parse command line parameters.
项目是maven构建的,使用JCommander的方式十分简单:
<dependency>
<groupId>com.beust</groupId>
<artifactId>jcommander</artifactId>
</dependency>
使用JCommander的方式也很简单,给需要外部传参的变量加Parameter标注:
@Parameter(names = { "-topologyName"}, description = "Topology name.")
private static String TOP_NAME = "sz-train";
一般类型参数后面都要跟值,JCommander会根据对应变量做类型检查和转换,不合法时会抛出异常错误。
boolean类型有点特殊,后面不需要跟一个值,输入-local
之后,local值即为true:
@Parameter(names = { "-local"}, description = "Local model, default cluster Model.")
public static boolean LOCAL_MODE = false;
如果一个boolean变量的默认值为true,而想通过参数设置为false,可以指定元数:
@Parameter(names = { "-log", "-verbose"}, description = " Wheather to write system log.", arity = 1)
public static boolean LOG = true;
默认情况下,参数是可选的,如果要求必须指定参数,可以设置required:
@Parameter(names = "-operator", required = true)
private String operator;
有时仅仅依靠JCommander的类型检查还不够,还需要自定义检查器提前发现不合法的输入:
@Parameter(names = { "-redisPort"}, description = "Redis port.", validateWith = PortValidator.class)
public static int REDIS_PORT=(LOCAL_MODE) ? 6379:6380;
public static class PortValidator implements IParameterValidator {
public void validate(String name, String value)
throws ParameterException {
Pattern pattern = Pattern.compile("[1-9]\\d*");
Matcher matcher = pattern.matcher(value);
if (matcher.matches()) {
int n = Integer.parseInt(value);
if (n < 65536) {
return;
}
}
throw new ParameterException("Parameter " + name
+ " should be a number(0~65535) (found " + value +")");
}
}
如果是一个写日志的目录,可以提前发现该目录是否可写,这是配置文件无法做到的:
@Parameter(names = { "-logDir"}, description = "Dir to write log file.",
validateWith = DirValidator.class)
public static String LOG_DIR = "/logs/your_project/";
public static class DirValidator implements IParameterValidator {
public void validate(String name, String value)
throws ParameterException {
File file = new File(value);
if (!file.isDirectory() || !file.canWrite()) {
throw new ParameterException("Parameter " + name
+ " should be a writable folder(found " + value +")");
}
}
}
对于IP地址,不仅可以通过正则表达式进行匹配,还可以进行简单的网络连通性探测等。相比基于文本的配置文件,JCommander显示出了强大的优势。
但更多的项目可能还是更适合用配置文件的方式进行外部配置,如果配置文件如果可以吸收JCommander的特点,那就perfect了。我理想中的配置文件应该有如下特性:
- 配置文件本身是programmable
- 可以进行上下文联系
- 自动类型检查和转换
- 可以自定义语义检查器
- 所使用的弱语言可以方便嵌入