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<String, Script> cache = new Hashtable<String, Script>(); 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<String, Object> row = new HashMap<String, Object>(); 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对表达式的内置支持能力与自定义脚本能力,可以实现功能强大的表达式解析能力。