这一节很短,但不要低估它的重要性!如果你是按顺序看到这里的,这里就是你要跳的龙门,跳过去你就不是只在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 DateTimeint month = dt.getMonthOfYearprintln 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的介绍真要结束了。我们已经走了很远了!