这一节很短,但不要低估它的重要性!如果你是按顺序看到这里的,这里就是你要跳的龙门,跳过去你就不是只在JVM上做Java开发的人了。JVM上有多种语言作为Java的补充,优秀的Java开发者要具备使用它们的能力,Groovy是个很好的起点!
首先,你会重温一下从Groovy中调用Java是多么简单。之后你会看到Java与Groovy交互的三种常用途径,使用GroovyShell
、GroovyClassLoader
和GroovyScriptEngine
。
我们先来重温一下Groovy里怎么调用Java。
8.5.1 从Groovy调用Java
还记得吗?我们说过从Groovy调用Java很简单,你只要把JAR放到CLASSPATH
中,然后用标准的import
语句就行了。这儿有个例子,引入流行的Joda日期时间类库中org.joda.time
包里的类1:
import org.joda.time.*;
1 在Java 8发布之前,实际上Joda一直都不是Java的标准日期时间类库。
可以跟在Java中一样使用这些类。下面的代码会输出当前月份的数值表示。
DateTime dt = new DateTime
int month = dt.getMonthOfYear
println month
哦,肯定要比这个更复杂点儿吧?
阿克巴上将:“陷阱!”2
2 星战迷对这句在网上广为流传的话应该不会感到陌生。
开个玩笑,这里没什么陷阱!真的就这么简单,那我们是不是应该看看更困难的情况?从Java调用Groovy并得到有意义的结果还是有点儿技术含量的。
8.5.2 从Java调用Groovy
从Java程序调用Groovy需要把Groovy及其相关的JAR放到这个程序的CLASSPATH
下,因为它们都是运行时依赖项。
提示 只需要把GROOVY_HOME/embeddable/groovy-all-1.8.6.jar文件放到
CLASSPATH
中。
下面是几种从Java调用Groovy代码的办法:
- 使用Bean Scripting Framework(BSF),即JSR 223;
- 使用
GroovyShell
; - 使用
GroovyClassLoader
; - 使用
GroovyScriptEngine
; - 使用嵌入式Groovy控制台。
我们在这一节重点讨论最常用的办法(GroovyShell
、GroovyClassLoader
和GroovyScriptEngine
)。先从最简单的GroovyShell
开始。
1. GroovyShell
在临时性快速调用Groovy并计算表达式或类似于脚本的代码时,可以用GroovyShell
。比如说,有些开发人员可能更喜欢用Groovy做数值处理,就可以调用GroovyShell
执行一些数学计算。代码清单8-10会返回用Groovy的数值相加得到的结果10.4。
代码清单8-10 在Java中用GroovyShell
执行Groovy代码
import groovy.lang.GroovyShell;
import groovy.lang.Binding;
import java.math.BigDecimal;
public class UseGroovyShell {
public static void main(String args) {
Binding binding = new Binding;
binding.setVariable(\"x\", 2.4);
binding.setVariable(\"y\", 8);
GroovyShell shell = new GroovyShell(binding); //设置shell上的binding
Object value = shell.evaluate(\"x + y\"); //计算并返回表达式
assert value.equals(new BigDecimal(10.4));
}
}
用GroovyShell
只能应付快速执行小段Groovy代码的情况,如果要与一个完整的Groovy类交互,该怎么办呢?这时可以用GroovyClassLoader
。
2. GroovyClassLoader
从开发人员的角度看,GroovyClassLoader
的表现很像Java的ClassLoader
。找到类和想要调用的方法,然后调用就行了。
下面的代码中有一个简单的CalculateMax
类,其中有个getMax
方法,会使用Groovy内置的max
函数。要在Java里通过GroovyClassLoader
运行这个方法,需要用下面的代码创建一个Groovy文件(CalculateMax.groovy):
class CalculateMax {
def Integer getMax(List values) {
values.max;
}
}
现在我们有了要执行的Groovy脚本,可以从Java调用它了。在代码清单8-11中,从Java调用CalculateMax getMax
函数,返回了传入参数中的最大值10。
代码清单8-11 在Java中用GroovyClassLoader
执行Groovy代码
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;
import org.codehaus.groovy.control.CompilationFailedException;
public class UseGroovyClassLoader {
public static void main(String args) {
GroovyClassLoader loader = new GroovyClassLoader; //准备GroovyClassLoader
try {
Class<?> groovyClass = loader.parseClass(
new File(\"CalculateMax.groovy\")); //得到Groovy类
GroovyObject groovyObject = (GroovyObject)
groovyClass.newInstance; //得到Groovy类的实例
ArrayList<Integer> numbers = new ArrayList<>; //准备参数
numbers.add(new Integer(1));
numbers.add(new Integer(10));
Object arguments = {numbers};
Object value =
groovyObject.invokeMethod(\"getMax\", arguments); //调用Groovy方法
assert value.equals(new Integer(10));
}
catch (CompilationFailedException | IOException | InstantiationException
| IllegalAccessException e) {
System.out.println(e.getMessage);
}
}
}
这种技术在调用几个Groovy实用类时可能会有用。但如果要用大量的Groovy代码,我们推荐使用完整的GroovyScriptEngine
。
3. GroovyScriptEngine
使用GroovyScriptEngine
要指明Groovy代码的URL或所在目录。Groovy脚本引擎会加载那些脚本,并在必要时进行编译,包括其中的依赖脚本。比如说你修改了脚本B,而脚本A依赖于B,则引擎会全重新编译它们。
假设有一个Groovy脚本(Hello.groovy)定义了一个简单的“Hello”语句,后面跟着一个名字(要从Java应用程序中传入的参数)。
def helloStatement = \"Hello ${name}\"
然后Java程序会通过GroovyScriptEngine
使用Hello.groovy,并输出一句问候,如代码清单8-12所示:
代码清单8-12 在Java中用GroovyScriptEngine
执行Groovy代码
import groovy.lang.Binding;
import groovy.util.GroovyScriptEngine;
import groovy.util.ResourceException;
import groovy.util.ScriptException;
import java.io.IOException;
public class UseGroovyScriptEngine {
public static void main(String args)
{
try {
String roots = new String {\"/src/main/groovy\"}; //设置根目录
GroovyScriptEngine gse =
new GroovyScriptEngine (roots); //初始化引擎
Binding binding = new Binding;
binding.setVariable(\"name\", \"Gweneth\");
Object output = gse.run(\"Hello.groovy\", binding); //运行脚本
assert output.equals(\"Hello Gweneth\");
}
catch (IOException | ResourceException | ScriptException e) {
System.out.println(e.getMessage);
}
}
}
GroovyScriptEngine
监控之下的任何Groovy脚本都可能被程序员一时兴起改掉。比如说,将Hello.groovy改成这样:
def helloStatement = \"Hello ${name}, it\'s Groovy baby, yeah!\"
这段Java代码下次再运行时,它就会用这个新的,更长的消息。这样Java应用程序就具备了以前根本不可能出现的动态灵活性。这在某些情况下简直是无价之宝,比如调试生产环境下的代码,在运行时修改系统属性,还有很多……
至此,对Groovy的介绍真要结束了。我们已经走了很远了!