据我了解,至少有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),因为它的集成机制非常差。
例如,使用GroovyShell
或GroovyClassLoader
,您可以设置自己的基本脚本类,这可以非常轻松地设置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有很多选择,从像这样的脚本到扩展您自己的类或接口的类的编译。看一下文档,让我们知道。