Java如何调用Groovy

如果想在Java代码中嵌入Groovy代码,目前主要有三种方式。

GroovyShell

1
2
3
4
5
6
7
8
9
10
11
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
class Groovy {
public static void main(String[] args){
Binding bind = new Binding();
bind.setVariable("greeting", "Hello Groovy!");
GroovyShell shell = new GroovyShell(bind);
Object object = shell.evaluate("greeting");
System.out.println(object);
}
}

结果输出为:

Hello Groovy!

通过Binding类来设置参数,然后将其作为参数来构造一个GroovyShell实例,通过evaluate方法来执行一段GroovyScript。

evaluate方法不仅可以执行一段GroovyScript,也可以执行一个Groovy文件。

Foo.groovy

1
2
3
4
5
class Foo {
public static void main(args){
println 'Hello Groovy!'
}
}

Groovy.java

1
2
3
4
5
6
7
8
9
10
11
12
13
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
class Groovy {
public static void main(String[] args){
GroovyShell shell = new GroovyShell();
Object object = null;
try {
object = shell.evaluate(new File("./src/Foo.groovy"));
} catch (CompilationFailedException e) {
e.printStackTrace();
}
}
}

输出结果为:

Hello Groovy!

Groovy文件必须定义main方法,否则将报错。

Exception in thread “main”

GroovyScriptEngine

GroovyScriptEngine是比GroovyShell更完整的一个方案,我们指定脚本文件所在的目录,然后在运行时传入目录下脚本的文件名以及所需参数即可。

示例代码:

Foo.groovy

1
println "$arg"

GroovyScriptEngineTest.java

1
2
3
4
5
6
7
public class GroovyScriptEngineTest {
public static void main(String[] args) throws Exception{
String[] roots = new String[]{"src/"};
GroovyScriptEngine groovyScriptEngine = new GroovyScriptEngine(roots);
Object object = groovyScriptEngine.run("Foo3.groovy", "Hello Groovy!");
}
}

输出结果为:

Hello Groovy!

在这个示例中,我们没有通过Binding来进行传值,而是直接传入一个字符串,所以Foo.groovy脚本中必须使用arg参数,这是GroovyScriptEngine类中的run方法强制规定的。

源码如下:

1
2
3
4
5
6
public String run(String scriptName, String argument) throws ResourceException, ScriptException {
Binding binding = new Binding();
binding.setVariable("arg", argument);
Object result = run(scriptName, binding);
return result == null ? "" : result.toString();
}

我们可以看到,如果run方法传入第二个参数为String类型,那么代码

binding.setVariable("arg", argument);

中强制规定了Groovy脚本必须有一个arg参数。

当然,如果要传多个参数的话,还是要通过Binding,就好像Android里的ContentValues类或者Bundle类一样。

GroovyClassLoader

第一种方法

通过GroovyClassLoader加载Groovy类并且调用该类的方法

示例代码:

Foo.groovy

1
2
3
4
5
class Foo{
public sayHello(a){
println a
}
}

InvokeGroovy.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;
import java.io.File;
import java.io.IOException;

public class InvokeGroovy {
public static void main(String[] args){
GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
try {
Class groovyClass = groovyClassLoader.parseClass(new File("src/Foo.groovy"));
GroovyObject object = (GroovyObject) groovyClass.newInstance();
object.invokeMethod("sayHello", "Hello Groovy!");
} catch (IOException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}

输出结果为:

Hello Groovy!

通过GroovyObject的invokeMethod方法调用Groovy类中的方法,第一个参数为方法名,第二个参数为所调方法的参数。

第二种方法

定义一个Java接口,再定义一个实现该接口的Groovy类,通过GroovyClassLoader加载Groovy类,然后直接调用该接口方法即可。

代码示例:

IFoo.java

1
2
3
4
// 定义一个Java接口
public interface IFoo {
public void sayHello(Object foo);
}

Foo.groovy

1
2
3
4
5
6
7
// 实现IFoo接口的Groovy类
class Foo implements IFoo{
@Override
public sayHello(a){
println a
}
}

InvokeGroovy.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import groovy.lang.GroovyClassLoader;

import java.io.File;
import java.io.IOException;

public class InvokeGroovy {
public static void main(String[] args){
GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
try {
Class groovyClass = groovyClassLoader.parseClass(new File("src/Foo.groovy"));
IFoo foo = (IFoo)groovyClass.newInstance();
foo.sayHello("Hello Groovy!");
} catch (IOException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}

输出结果为:

Hello Groovy!