据我了解,至少有2种方法可以用Java编译Groovy脚本。


使用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);

使用GroovyClassLoader#parse,做一些中间工作,然后调用GroovyObject#invokeMethod(String, Object[])


注意-以下代码来自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的正确方法。

总的来说,根据我上面的示例,有没有更简单的方法可以实现第二种方法?

最佳答案

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

例如,使用GroovyShellGroovyClassLoader,您可以设置自己的基本脚本类,这可以非常轻松地设置foo实例,但是您也可以从基本脚本类中调用任何方法。

顺便说一句,所有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的holder类,如下所示:

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有很多选择,从像这样的脚本到扩展您自己的类或接口的类的编译。看一下文档,让我们知道。

10-06 09:54
查看更多