首页 » Java程序员修炼之道 » Java程序员修炼之道全文在线阅读

《Java程序员修炼之道》8.5 Groovy与Java的合作

关灯直达底部

这一节很短,但不要低估它的重要性!如果你是按顺序看到这里的,这里就是你要跳的龙门,跳过去你就不是只在JVM上做Java开发的人了。JVM上有多种语言作为Java的补充,优秀的Java开发者要具备使用它们的能力,Groovy是个很好的起点!

首先,你会重温一下从Groovy中调用Java是多么简单。之后你会看到Java与Groovy交互的三种常用途径,使用GroovyShellGroovyClassLoaderGroovyScriptEngine

我们先来重温一下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控制台。

我们在这一节重点讨论最常用的办法(GroovyShellGroovyClassLoaderGroovyScriptEngine)。先从最简单的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的介绍真要结束了。我们已经走了很远了!