本文来自iteye @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说得比这文更详细,可以去读一下.