本文来自图灵社区 @fairjm 转截请注明出处
偶尔会有一些业务需求是需要在动态改变线上代码的行为.一般的做法是使用个配置文件,存在数据库或者redis等存储中,程序动态获取之后解析并根据配置进行相应的操作.
在配置不复杂的情况下这样的做法能够满足需求.但如果配置很复杂,或者配置的规则很多,那么解析配置并运行就变成了一件很麻烦的事情.可能会引入一些解析器,或者Criteria这样的类.
如果这样,那是不是嵌入一段代码动态加载运行会不会简单点?
好在java本身就提供了这样的机制,也就是JSR-223.
因为是开发使用的,所以最好还是用和java一样的语法,方便现有开发,那选取的脚本语言为groovy比较合适,毕竟能兼容java语法.
搞起.
首先引入依赖:
org.codehaus.groovy
groovy-jsr223
2.4.7
这边用判断用户是否能够访问作为例子.
其中要考虑的用户类如下(省略getter setter和构造器):
public class User {
private String userName;
private Long userId;
private Boolean isVip;
private Integer gender;
private Integer age;
现在的规则是如果是非生产环境返回true,会员返回true,用户以test开头(这只是例子 千万别学)返回true 如果性别是男性并且大于18岁返回true(咦)
对应的groovy脚本如下:
if (!prod) {
return true;
}
if (Boolean.TRUE.equals(user.getIsVip())) {
return true;
}
if (user.getUserName()?.startsWith("test")) {
return true;
}
if (user.getGender()?.equals(1) && user.getAge() != null && user.getAge() >= 18) {
return true;
}
return false;
?表达式可以减少空判断,因为groovy兼容java,所以手工写 != null(比如age)也是可以的.
接来下执行:
// 获取脚本引擎
final ScriptEngineManager factory = new ScriptEngineManager();
final ScriptEngine engine = factory.getEngineByName("groovy");
// 读取脚本
String str;
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(
GroovyScriptTest.class.getResourceAsStream("/groovy-script")))) {
str = reader.lines().collect(Collectors.joining("\r\n"));
}
final User u = new User("fairjm", 1L, false, 1, 18);
// groovy的引擎是支持可编译的所以直接这么写
final CompiledScript script = ((Compilable) engine).compile(str);
final Bindings bindings = new SimpleBindings();
bindings.put("prod", true);
bindings.put("user", u);
final Boolean result = (Boolean) script.eval(bindings);
System.out.println(result);
返回true.
以上,如果从写配置文件的角度想,上面那么几个条件以及条件的优先级和组合会比较麻烦(感兴趣的可以试试看).
参考了
http://www.ticmy.com/?p=267
原文对于JSR223说得比这文更详细,可以去读一下.