Groovy作为一种JVM-Based语言,目前普及程度正在提高。本文演示一下在Java类中,通过继承GDK的groovy.lang.Script类如何支持自定义表达式解析功能。
输入:
表示一行数据的某个map结构。在实际应用中,产生这种结构的最常见场景可能是通过JDBC访问数据库、通过调用WebService服务得到的某行结果集等。
目标设定:
假设我们希望对输入数据进行某个运算。此处示例中,我们模拟oracle中最常用的nvl函数。
处理过程:
首先,通过继承groovy.lang.Script,定义自己的表达式解析类:
public class MyBasicScript extends Script
在该类中实现具体的解析方法:
public static Object nvl(Object str,Object val){
return str==null ||"".equals(str)?val:str;
}
其次,基于上述自定义类,实例化一个CompilerConfiguration对象。
CompilerConfiguration cfg = new CompilerConfiguration();
cfg.setScriptBaseClass(MyBasicScript.class.getName());
以此CompilerConfiguration实例为参数,实例化一个GroovyShell对象
shell = new GroovyShell(cfg);
通过shell对象,解析并运行表达式。在运行前,可以通过bingding对象绑定脚本运行时的上下文数据:
Binding binding = new Binding(map);
Script script = shell.parse(expr);
script.setBinding(binding);
script.run();
附完整的代码示例(共两个类,分别是自定义脚本实现类、调用及测试类)
package jg.groovy;
import groovy.lang.Script;
import java.lang.reflect.Method;
public class MyBasicScript extends Script {
@Override
public Object run() {
//show usage
Method[] methods = MyBasicScript.class.getDeclaredMethods();
StringBuilder sb=new StringBuilder();
for (Method method : methods) {
sb.append(method);
}
return sb.substring(0, sb.length()-1);
}
public static Object nvl(Object str, Object val) {
return str == null || "".equals(str) ? val : str;
}
}
package jg.groovy;
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.codehaus.groovy.control.CompilerConfiguration;
public class ExprSupport {
private static final Object lock = new Object();
private static final GroovyShell shell;
private static Hashtable cache = new Hashtable();
static {
CompilerConfiguration cfg = new CompilerConfiguration();
cfg.setScriptBaseClass(MyBasicScript.class.getName());
shell = new GroovyShell(cfg);
}
public static Object parseExpr(String expr) {
Script s = getScriptFromCache(expr);
return s.run();
}
public static Object parseExpr(String expr, Map, ?> map) {
Binding binding = new Binding(map);
Script script = getScriptFromCache(expr);
script.setBinding(binding);
return script.run();
}
private static Script getScriptFromCache(String expr) {
if (cache.contains(expr)) {
return cache.get(expr);
}
synchronized (lock) {
if (cache.contains(expr)) {
return cache.get(expr);
}
Script script = shell.parse(expr);
cache.put(expr, script);
return script;
}
}
/**
* @param args
*/
public static void main(String[] args) {
// eg. get one row from db
Map row = new HashMap();
row.put("id", 42);
row.put("name", "");
//带绑定数据参数的调用方式
System.out.println(ExprSupport.parseExpr("nvl(id,0)", row));
System.out.println(ExprSupport.parseExpr("nvl(name,'anonymous')", row));
//不带绑定数据参数的调用方式,这个是groovy的内置能力
System.out.println(ExprSupport.parseExpr("1+2"));
}
}
输出:
42
anonymous
3
总结:结合groovy对表达式的内置支持能力与自定义脚本能力,可以实现功能强大的表达式解析能力。