据我所知,至少有两种方法可以用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
。
一般来说,按照上面的例子,是否有更简单的方法来实现第二种方法?
答案 0 :(得分:2)
实际上有两个以上的选项。所有这些都在文档中描述(参见http://docs.groovy-lang.org/2.3.8/html/documentation/#_integrating_groovy_in_a_java_application),但无论如何,我不建议使用JSR-223(javax.script),因为它是一个非常差的集成机制。
使用例如GroovyShell
或GroovyClassLoader
,您可以设置自己的基本脚本类,以便您轻松设置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有很多选择,从编译这样的脚本到编译扩展你自己的类或接口的类。看看文档并告诉我们。