编译的Groovy脚本

时间:2014-12-10 19:10:26

标签: java groovy

据我所知,至少有两种方法可以用Java编译Groovy脚本。

  1. 使用javax.script.ScriptEngine转换为CompiledScript

    ScriptEngine engine = new ScriptEngineManager().getEngineByName("Groovy");
    
    Bindings bindings = new SimpleBindings();
    bindings.put("foo", 1234);
    
    Compilable compEngine = (Compilable)engine;
    CompiledScript cs = compEngine.compile("if (foo == 1234) true else false");
    cs.eval(bindings); 
    
  2. 使用GroovyClassLoader#parse,做一些中间工作,然后致电GroovyObject#invokeMethod(String, Object[])

  3. 注意 - 以下代码来自Groovy in Action。我发现它是学习Groovy的好书。

    GroovyClassLoader gcl = new GroovyClassLoader();
    // Note, assume that the Groovy script gets compiled to a class that 
    // includes a method, "do". And "do" accepts an `Integer` argument, "foo."
    Class foo             = gcl.parseClass("if (foo == 1234) true else false");
    GroovyObject hello    = (GroovyObject) foo.newInstance();
    Object[] args         = { Integer.valueOf(1234) };
    assert                  true == (foo.invokeMethod("do", args)); 
    

    据我了解这两种方法之间的差异,第一种方法涉及使用Bindings对填充key-value地图 - 将变量名称填充为值。然后,我们通过CompiledScript执行CompiledScript#eval(Bindings),其中Bindings参数发生变异。

    但是,假设我想将Foo类对象传递给方法Foo#do。而不是评估if (foo == 1234) ...,我需要在源代码中写:

    if(fooObj.getFoo() == 1234) ...

    然后,结果,我发现我需要后处理 DSL,以包含正确的方式来foo

    一般来说,按照上面的例子,是否有更简单的方法来实现第二种方法?

1 个答案:

答案 0 :(得分:2)

实际上有两个以上的选项。所有这些都在文档中描述(参见http://docs.groovy-lang.org/2.3.8/html/documentation/#_integrating_groovy_in_a_java_application),但无论如何,我不建议使用JSR-223(javax.script),因为它是一个非常差的集成机制。

使用例如GroovyShellGroovyClassLoader,您可以设置自己的基本脚本类,以便您轻松设置foo实例,但也可以调用任何方法来自你的基础脚本类。

BTW,所有Groovy脚本将实现的方法不是do而是run。所以想象你有以下基类:

public abstract class MyDSL extends groovy.lang.Script {
    Object fooObj
    public void setFooObj(Object foo) { fooObj = foo; }
    public Object getFooObj() { return fooObj; }
}

Foo的持有者类如下:

public class FooHolder {
    def getFoo() { return 1234; }
}

然后你可以用这种方式创建一个脚本:

CompilerConfiguration config = new CompilerConfiguration();
config.setScriptBaseClass("test.MyDSL");
GroovyClassLoader gcl = new GroovyClassLoader(this.getClass().getClassLoader(),config);
Class<? extends MyDSL> scriptClass = gcl.parseClass("return (fooObj.getFoo()==1234)");
MyDSL v1 = scriptClass.newInstance();
v1.setFooObj(new FooHolder());
Object result = v1.run();

请注意,这实际上是一种方法,但不一定是满足您需求的最佳方式。也许你可以描述一下你想要实现的东西,但是Groovy有很多选择,从编译这样的脚本到编译扩展你自己的类或接口的类。看看文档并告诉我们。