讀古今文學網 > Java程序員修煉之道 > 8.5 Groovy與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 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控制台。

我們在這一節重點討論最常用的辦法(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的介紹真要結束了。我們已經走了很遠了!