這一節很短,但不要低估它的重要性!如果你是按順序看到這裡的,這裡就是你要跳的龍門,跳過去你就不是只在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的介紹真要結束了。我們已經走了很遠了!