Groovy 語言快速入門

簡單的土豆發表於2017-10-10

前言

由於專案需要用到 Groovy 語言,這兩天對其進行了粗略的學習,本文是對學習做的一個簡單總結,主要內容參考於官方文件(Groovy 的官方文件還是非常不錯的,強烈推薦閱讀),希望本文對準備學習使用或者對 Groovy 感興趣的同學有所幫助,如有不對之處還望指出哈,對這門語言的理解還是比較膚淺的。

簡介

Groovy 是 Apache 旗下的一門基於 JVM 平臺的動態/敏捷程式語言,在語言的設計上它吸納了 Python、Ruby 和 Smalltalk 語言的優秀特性,語法非常簡練和優美,開發效率也非常高(程式語言的開發效率和效能是相互矛盾的,越高階的程式語言效能越差,因為意味著更多底層的封裝,不過開發效率會更高,需結合使用場景做取捨)。並且,Groovy 可以與 Java 語言無縫對接,在寫 Groovy 的時候如果忘記了語法可以直接按Java的語法繼續寫,也可以在 Java 中呼叫 Groovy 指令碼,都可以很好的工作,這有效的降低了 Java 開發者學習 Groovy 的成本。Groovy 也並不會替代 Java,而是相輔相成、互補的關係,具體使用哪門語言這取決於要解決的問題和使用的場景。

快速開始

  1. 下載Groovy開發工具包(GDK)
    www.groovy-lang.org/download.ht…
  2. 建立Groovy專案
    使用IDEA的話需要安裝Groovy的支援外掛,安裝完成後在新建專案中便會出現Groovy專案選項,選擇Groovy專案並關聯Groovy libray即可,當然也可以直接建立.groovy檔案用命令列直接執行。
  3. Hello World
    在Java中要輸出“hello world”需要像下面這樣,建立一個類,然後建立一個main方法。
    public class Hello {
     public static void main(String[] args) {
         System.out.println("hello world");
     }
    }複製程式碼
    在Groovy中,這些都可以省略,下面這4種方式都可以輸出“hello world”。
    System.out.println("hello world");
    System.out.println "hello world";
    println("hello world")
    println 'hello world'複製程式碼
    當然,也可以像Java一樣執行在類的main方法中。
    class Hello {
     static void main(args) {
         println 'hello world'
     }
    }複製程式碼
    如果 Groovy 指令碼檔案裡只有執行程式碼,沒有類的定義,則 Groovy 編譯器會生成一個 Script 的子類,類名和指令碼檔案的檔名一樣,而指令碼中的程式碼會被包含在一個名為run的方法中,同時還會生成一個main方法,作為整個指令碼的入口。所以,作為 JVM 平臺語言,與 Java 本質上還是一樣的。

與Java的一些區別

預設匯入

Groovy 會預設匯入下面這些包、類,不需要使用import語句顯式匯入。

java.io.*
java.lang.*
java.math.BigDecimal
java.math.BigInteger
java.net.*
java.util.*
groovy.lang.*
groovy.util.*複製程式碼

多重方法

在 Groovy 中,呼叫的方法將在執行時被選擇。這種機制被稱為執行時分派或多重方法(multi-methods),是根據執行時實參(argument)的型別來選擇方法。Java 採用的是相反的策略:編譯時根據宣告的型別來選擇方法。

下面是一個例子,同樣的 Java 程式碼在 Java 和 Groovy 環境中執行結果是不同的.

int method(String arg) {
    return 1;
}
int method(Object arg) {
    return 2;
}
Object o = "Object";
int result = method(o);
// In Java
assertEquals(2, result);
// In Groovy
assertEquals(1, result);複製程式碼

產生差異的原因在於,Java 使用靜態資料型別,o 被宣告為 Object 物件,而 Groovy 會在執行時實際呼叫方法時進行選擇。因為呼叫的是 String 型別的物件,所以自然呼叫 String 版本的方法。

陣列初始化語法

在 Groovy 中,{...} 語句塊是留給閉包(Closure)使用的,所以不能像 Java 中一樣使用下面這種方式初始化陣列

int[] array = { 1, 2, 3}複製程式碼

而應該是下面這樣

int[] array = [1,2,3]複製程式碼

POJO

Groovy 預設會隱式的建立getter、setter方法,並且會提供帶參的構造器,下面兩者是等價的。

// In Java
public class Person {
    private String name;
    Person(String name) {
        this.name = name
    }
    public String getName() {
        return name
    }
    public void setName(String name) {
        this.name = name
    }
}

// In Groovy
class Person {
    String name
}

def person = new Person(name: '張三')
assert '張三' == person.name
person.name = '李四'
//person.setName('李四')
assert '李四' == person.getName()複製程式碼

包訪問許可權

在 Java 中如果沒有顯式的指定訪問修飾符(public、protected、private)那麼預設是包訪問許可權,但在 Groovy 中預設是public的,所以需要使用@PackageScope註解。

class Person {
    @PackageScope String name
}複製程式碼

ARM 語句塊

ARM(Automatic Resource Management,自動資源管理)語句塊(或者叫TWR語法)從 Java 7 開始引入,用於降低IO操作程式碼的複雜度,但 Groovy 並不支援。相反,Groovy 提供多種基於閉包的方法,不但可以達到同樣的效果並且會更加簡潔優美。

//In Groovy
Path file = Paths.get("/User/lihengming/test.txt");
Charset charset = Charset.forName("UTF-8");
try (BufferedReader reader = Files.newBufferedReader(file, charset)) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }

} catch (IOException e) {
    e.printStackTrace();
}

//In Groovy
new File('/User/lihengming/test.txt').eachLine('UTF-8') {
   println it
}
//或者這樣,更接近於Java的方式
new File('/User/lihengming/test.txt').withReader('UTF-8') { reader ->
   reader.eachLine {
       println it
   }
}
//如果只是為了讀取並列印出文字的內容的話,下面是最簡潔的方式
print new File('/User/lihengming/test.txt').text複製程式碼

內部類

Groovy 同樣支援內部類並且實現跟 Java 是一樣的,但不應拿 Java 語言規範來考量它,應對差異情況保持冷靜與寬容(keep shaking the head about things that are different)。在Groovy中內部類看起來有點類似 groovy.lang.Closure 類的實現。

//靜態內部類
class A {
    static class B {}
}
new A.B()

//匿名內部類
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit

CountDownLatch called = new CountDownLatch(1)

Timer timer = new Timer()
timer.schedule(new TimerTask() {
    void run() {
        called.countDown()
    }
}, 0)

assert called.await(10, TimeUnit.SECONDS)複製程式碼

Lambda 表示式

Java 8 支援 Lambda 表示式和方法引用

Runnable run = () -> System.out.println("Run");
list.forEach(System.out::println);複製程式碼

Java 8 的 lambda 幾乎可以認為是匿名內部類。Groovy 並沒有採用這種語法,而採用閉包來實現。

Runnable run = { println 'run' }
list.each { println it } // or list.each(this.&println)複製程式碼

GString

由於雙引號所包括起來的字串字面量會被解釋為 GString 值(即 “Groovy 字串”的簡稱),所以如果當某個類中的 String 字面量含有美元字元($)時,那麼利用 Groovy 和 Java 編譯器進行編譯時,Groovy 很可能就會編譯失敗,或者產生與 Java 編譯所不同的結果。

通常,如果某個 API 宣告瞭形參的型別,Groovy 會自動轉換 GString 和 String。要小心那些形參為 Object 的 Java API,需要檢查它們的實際型別。

字串和字元字面量

在 Groovy 中,由單引號所建立的字面量屬於 String 型別物件,而雙引號建立的字面量則可能是 String 或 GString 物件,具體分類由字面量中是否有插值來決定。

assert 'c'.getClass()==String
assert "c".getClass()==String
assert "c${1}".getClass() in GString複製程式碼

基礎語法

  • Groovy 語句無需使用分號(;)結尾,當然加上也不會報錯,畢竟完全相容 Java 的語法。
  • Groovy 中==等價於 Java 中的equals方法。

註釋

註釋和 Java 一樣,支援單行(//)、多行(/* */)和文件註釋(/** */)

除此之外還支援 Shebang line(UNIX系統支援一種特殊的單行註釋叫作 Shebang line,用於指明指令碼的執行環境,便於在終端中可以直接執行)#號必須是檔案的第一個字元。

#!/usr/bin/env groovy
println "Hello from the shebang line"複製程式碼

變數

Groovy 中定義變數預設訪問修飾符是public,變數定義時遵循 Java 變數命名規範,變數名以字母、下劃線或美元符號$開始,可以包含字母、數字、下劃線和美元符號$,但關鍵字除外。除了這些規則之外,Groovy 定義變數時如果是一行定義一個型別,末尾的分號可以省略,但是如果多個變數佔一行,變數之間必須以分號分割。

Groovy 定義變數的方式和 Java 是類似的,區別在於 Groovy 提供了def關鍵字供使用,它可以省略變數型別的定義,根據變數的值進行型別推導。

def a = 123
def b = 'b'
def c = true 
boolean d = false
int e = 123複製程式碼

如果定義變數的字面量值為數字時,型別會根據數字的大小自動調整

def a = 1
assert a instanceof Integer

// Integer.MAX_VALUE
def b = 2147483647
assert b instanceof Integer

// Integer.MAX_VALUE + 1
def c = 2147483648
assert c instanceof Long

// Long.MAX_VALUE
def d = 9223372036854775807
assert d instanceof Long

// Long.MAX_VALUE + 1
def e = 9223372036854775808
assert e instanceof BigInteger複製程式碼

對於浮點型字面量為了精度 Groovy 預設使用的型別為 BigDecimal

def decimal = 123.456
println decimal.getClass() // class java.math.BigDecimal複製程式碼

Groovy為 數字型別提供一種更簡單的宣告型別的方式:型別字尾

- Integer 使用I或i
- Long 使用L或l
- BigInteger 使用G或g
- BigDecimal 使用G或g
- Double 使用D或d
- Float 使用F或f複製程式碼

使用例子

def a = 123I
assert a instanceof Integer
def b= 123L
assert b instanceof Long複製程式碼

字串

在Groovy種有兩種字串型別,普通字串(java.lang.String)和插值字串(groovy.lang.GString)。
普通字串使用單引號

println 'hello'複製程式碼

插值字串使用雙引號

def name = '張三'
println "hello $name"複製程式碼

除此之外,還支援三單引號的寫法,可以保留文字的換行及縮排格式

def strippedFirstNewline = '''line one
        line two
            line three
'''
println strippedFirstNewline
// 可以寫成下面這種形式,可讀性更好
def strippedFirstNewline2 = '''\
line one
    line two
line three
'''
println strippedFirstNewline2複製程式碼

字元

在 Groovy 中並沒有明確的字元字面量表示形式,需要顯示的指定,有三種方式

char c1 = 'A' // 宣告型別
assert c1 instanceof Character

def c2 = 'B' as char // 用as關鍵字
assert c2 instanceof Character

def c3 = (char) 'C' // 強制型別轉換
assert c3 instanceof Character複製程式碼

方法(函式)

Groovy 方法的預設訪問修飾符是public,方法的返回型別可以不需要宣告,但需新增def關鍵字。有返回值的方法return可以被省略,預設返回最後一行程式碼的執行結果,如果使用了return關鍵字則返回指定的返回值。

String method1() {
    return 'hello'
}
assert method1() == 'hello';

def method2() {
    return 'hello'
}
assert method2() == 'hello';

def method3() {
    'hello'
}
assert method3() == 'hello';複製程式碼

Groovy 方法的引數型別可以被省略,預設為Object型別。

def add(int a, int b) {
    return a + b
}
//與上面的等價
def add(a, b) {
    a + b
}複製程式碼

Groovy 方法的其他特性與Java一樣,比如支援過載、不定長引數(...)等。

閉包

Groovy 提供了閉包的支援,語法和 Lambda 表示式有些類似,簡單來說就是一段可執行的程式碼塊或函式指標。閉包在 Groovy 中是groovy.lang.Closure類的例項,這使得閉包可以賦值給變數,或者作為引數傳遞。Groovy 定義閉包的語法很簡單,就像下面這樣。

//閉包的引數為可選項
def closure = { [closureParameters -> ] statements }複製程式碼

閉包可以訪問外部變數,而方法(函式)則不能。

def str = 'hello'
def closure={
    println str
}
closure()//hello複製程式碼

閉包呼叫的方式有兩種,閉包.call(引數)或者閉包(引數),在呼叫的時候可以省略圓括號。

def closure = {
    param -> println param
}

closure('hello')
closure.call('hello')
closure 'hello'複製程式碼

閉包的引數是可選的,如果沒有引數的話可以省略->操作符。

def closure = {println 'hello'}
closure()複製程式碼

多個引數以逗號分隔,引數型別和方法一樣可以顯式宣告也可省略。

def closure = { String x, int y ->                                
    println "hey ${x} the value is ${y}"
}複製程式碼

如果只有一個引數的話,也可省略引數的定義,Groovy提供了一個隱式的引數it來替代它。

def closure = { it -> println it } 
//和上面是等價的
def closure = { println it }   
closure('hello')複製程式碼

閉包可以作為引數傳入,閉包作為方法的唯一引數或最後一個引數時可省略括號。

def eachLine(lines, closure) {
    for (String line : lines) {
        closure(line)
    }
}

eachLine('a'..'z',{ println it }) 
//可省略括號,與上面等價
eachLine('a'..'z') { println it }複製程式碼

Lists

Groovy 定義 List 的方式非常簡潔,使用中括號([]),以逗號(,)分隔元素即可。Groovy中的 List 其實就是java.util.List,實現類預設使用的是java.util.ArrayList

def numbers = [1, 2, 3]         

assert numbers instanceof List  
assert numbers.class == java.util.ArrayList  
assert numbers.size() == 3複製程式碼

Arrays

Groovy 定義陣列的語法和 List 非常類似,區別在於陣列的定義必須指定型別為陣列型別,可以直接定義型別或者使用def定義然後通過as關鍵字來指定其型別。

String[] arrStr = ['Ananas', 'Banana', 'Kiwi'] //直接宣告型別為陣列型別  String[]

assert arrStr instanceof String[]    
assert !(arrStr instanceof List)

def numArr = [1, 2, 3] as int[]     //痛過as關鍵字指定型別為陣列型別 int[] 

assert numArr instanceof int[]       
assert numArr.size() == 3複製程式碼

Maps

Groovy 定義 Map 的方式非常簡潔,通過中括號包含key、val的形式,key和value以冒號分隔([key:value])。Groovy中的Map其實就是java.util.Map,實現類預設使用的是java.util.LinkedHashMap

// key雖然沒有加引號,不過Groovy會預設將其轉換為字串
def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF']

assert colors['red'] == '#FF0000' // 使用中括號訪問
assert colors.green == '#00FF00' // 使用點表示式訪問

colors['pink'] = '#FF00FF' // 使用中括號新增元素,相當於Java Map 的 put(key,value)方法
colors.yellow = '#FFFF00'// 使用點表示式新增元素
assert colors.pink == '#FF00FF'
assert colors['yellow'] == '#FFFF00'
assert colors instanceof java.util.LinkedHashMap // 預設使用LinkedHashMap型別

// Groovy Map的key預設語法不支援變數,這裡的key時間上是字串'keyVal'而不是keyVal變數的值'name'
def keyVal = 'name'
def persons = [keyVal: 'Guillaume'] 
assert !persons.containsKey('name')
assert persons.containsKey('keyVal')

//要使用變數作為key,需要使用括號
def keyVal = 'name'
def persons = [(keyVal): 'Guillaume'] 
assert persons.containsKey('name')
assert !persons.containsKey('keyVal')複製程式碼

Range

在 Groovy 中可以使用..操作符來定義一個區間物件,簡化範圍操作的程式碼。

def range = 0..5
assert (0..5).collect() == [0, 1, 2, 3, 4, 5]
assert (0..<5).collect() == [0, 1, 2, 3, 4] // 相當於左閉右開區間
assert (0..5) instanceof List // Range實際上是List介面的實現
assert (0..5).size() == 6
assert ('a'..'d').collect() == ['a','b','c','d']//也可以是字元型別

//常見使用場景
for (x in 1..10) {
    println x
}

('a'..'z').each {
    println it
}

def age = 25;
switch (age) {
    case 0..17:
        println '未成年'
        break
    case 18..30:
        println '青年'
        break
    case 31..50:
        println '中年'
        break
    default:
        println '老年'
}複製程式碼

常見使用場景

Grails

Grails 是一個基於 Groovy 語言,構建於 Spring/Spring Boot、Hibernate 之上的高生產力、一站式 Web 開發框架。特別適合小型團隊進行敏捷開發,效率非常高。由於效能和學習成本的原因,普及率比較低,大分部公司還是更傾向於選擇 Spring Boot 作為開發框架。

Gradle

Gradle 是一個基於 Apache Ant 和 Apache Maven 概念的專案自動化構建工具。它使用一種基於 Groovy 的特定領域語言(DSL)來進行構建配置,拋棄了基於XML的各種繁瑣配置,主要以面向Java應用為主,支援從 Maven 倉庫下載依賴。現在越來越多的專案(Android專案居多)使用Gradle 來作為專案的構建工具,相信未來 Gradle 也會逐漸代替 Maven,就像 Maven 代替 Ant 那樣。

使用Maven構建專案

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
             http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.vs</groupId>
    <artifactId>com.vs.maven.gradle</artifactId>
    <version>1.0</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.6.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa
            </artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.25</version>
        </dependency>
    </dependencies>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>複製程式碼

使用Gradle構建專案

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.6.RELEASE")
    }
}
dependencies {
    compile("org.springframework.boot:spring-boot-starter-web") {
        exclude module: "spring-boot-starter-tomcat"
    }
    compile("org.springframework.boot:spring-boot-starter-security")
    compile("org.springframework.boot:spring-boot-starter-data-jpa")
    testCompile("mysql:mysql-connector-java:5.1.25")
}複製程式碼

Spring 支援

從 Spring 2.0 版本開始對動態指令碼語言進行了支援(Spring 官方文件該部分地址),其中便包括 Groovy ,Spring 提供了<lang:groovy/>標籤來定義 Groovy Bean 。Groovy Bean 可以通過script-source屬性載入指令碼檔案,指令碼原始檔可以來自本地或者網路,並且可以通過refresh-check-delay屬性監聽指令碼內程式碼變更重新裝載 Bean 實現動態 Bean 。

// from the file '/java/Calculator.java'
public interface Calculator {
    int add(int x, int y);
}

// from the file '/resources/CalculatorGroovyImpl.groovy'
class CalculatorGroovyImpl implements Calculator {
    int add(int x, int y) {
        x + y
    }
}複製程式碼
<-- from the file 'beans.xml' 省略 xmlns -->
<?xml version="1.0" encoding="UTF-8"?>
<beans>
    <lang:groovy id="calculator" script-source="classpath:CalculatorGroovyImpl.groovy" refresh-check-delay="1000"/>
</beans>複製程式碼
public class Tester {
    public static void main(String[] args) throws InterruptedException {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Calculator calculator = (Calculator)context.getBean("calculator");
        //嘗試修改CalculatorGroovyImpl.groovy,將 x + y,修改為x * y。
        while (true){
            System.out.println(calculator.add(1, 1));
            TimeUnit.SECONDS.sleep(1);
        }
    }
}複製程式碼

從 Spring 4.0 版本開始 Spring 支援使用 Groovy DSL 來定義 Bean 的配置,詳見 Spring 官方文件該部分

beans {
    //beanName(type)  
    dataSource(BasicDataSource) {
        //注入屬性
        driverClassName = "org.hsqldb.jdbcDriver"
        url = "jdbc:hsqldb:mem:grailsDB"
        username = "sa"
        password = ""
        settings = [mynew:"setting"]
    }
    sessionFactory(SessionFactory) {
       //注入屬性,引用其他Bean
        dataSource = dataSource
    }
    myService(MyService) {
       //使用閉包定義巢狀的Bean
        nestedBean = { AnotherBean bean ->
            dataSource = dataSource
        }
    }
}複製程式碼
ApplicationContext context = new GenericGroovyApplicationContext("beans.groovy");
MyService myService = context.getBean(MyService.class);複製程式碼

與Java整合

Groovy非常容易整合在Java環境中,利用其動態性來做規則引擎、流程引擎、動態指令碼環境,非常適合那些不不需要經常釋出但又經常變更的場景下使用。在Java中整合(呼叫)Groovy 程式碼有下面幾種方式。

Eval

groovy.util.Eval 類是最簡單的用來在執行時動態執行 Groovy 程式碼的類,提供了幾個靜態工廠方法供使用,內部其實就是對GroovyShell的封裝。

//執行Groovy程式碼
Eval.me("println 'hello world'");
//繫結自定義引數
Object result = Eval.me("age", 22, "if(age < 18){'未成年'}else{'成年'}");
assertEquals(result, "成年");
//繫結一個名為 x 的引數的簡單計算
assertEquals(Eval.x(4, "2*x"), 8);
//帶有兩個名為 x 與 y 的繫結引數的簡單計算
assertEquals(Eval.xy(4, 5, "x*y"), 20);
//帶有三個繫結引數(x、y 和 z)的簡單計算
assertEquals(Eval.xyz(4, 5, 6, "x*y+z"), 26);複製程式碼
GroovyShell

groovy.lang.GroovyShell除了可以執行 Groovy 程式碼外,提供了更豐富的功能,比如可以繫結更多的變數,從檔案系統、網路載入程式碼等。

GroovyShell shell = new GroovyShell();
//可以繫結更多變數
shell.setVariable("age",22);
//直接求值
shell.evaluate("if(age < 18){'未成年'}else{'成年'}");
//解析為指令碼,延遲執行或者快取起來
Script script = shell.parse("if(age < 18){'未成年'}else{'成年'}");
assertEquals(script.run(), "成年");
//可以從更多位置載入/執行指令碼
//shell.evaluate(new File("script.groovy"));
//shell.evaluate(new URI("http://wwww.a.com/script.groovy"));複製程式碼
GroovyClassLoader

groovy.lang.GroovyClassLoader是一個定製的類載入器,可以在執行時載入 Groovy 程式碼,生成 Class 物件。

 GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
 String scriptText = "class Hello { void hello() { println 'hello' } }";
 //將Groovy指令碼解析為Class物件
 Class clazz = groovyClassLoader.parseClass(scriptText);
 //Class clazz = groovyClassLoader.parseClass(new File("script.groovy"));
 assertEquals(clazz.getName(),"Hello");
 clazz.getMethod("hello").invoke(clazz.newInstance());複製程式碼
GroovyScriptEngine

groovy.util.GroovyScriptEngine能夠處理任何 Groovy 程式碼的動態編譯與載入,可以從統一的位置載入指令碼,並且能夠監聽指令碼的變化,當指令碼發生變化時會重新載入。

//script/groovy/hello.groovy
println "hello $name"複製程式碼
GroovyScriptEngine scriptEngine = new GroovyScriptEngine("script/groovy");
Binding binding = new Binding();
binding.setVariable("name", "groovy");
while (true){
    scriptEngine.run("hello.groovy", binding);
    TimeUnit.SECONDS.sleep(1);
}複製程式碼
//輸出
hello groovy
hello groovy
....
//將hello.groovy內程式碼修改為println "hi $name", GroovyScriptEngine會重新進行載入
hi groovy
hi groovy複製程式碼
JSR 223 javax.script API

JSR-223 是 Java 中呼叫指令碼語言的標準 API。從 Java 6 開始引入進來,主要目的是用來提供一種統一的框架,以便在 Java 中呼叫多種指令碼語言。JSR-223 支援大部分流行的指令碼語言,比如JavaScript、Scala、JRuby、Jython和Groovy等。

ScriptEngine engine = new ScriptEngineManager().getEngineByName("groovy");
Bindings bindings = new SimpleBindings();
bindings.put("age", 22);
Object value = engine.eval("if(age < 18){'未成年'}else{'成年'}",bindings);
assertEquals(value,"成年");

//script/groovy/hello.groovy
//println "hello world"
engine.eval(new FileReader("script/groovy/hello.groovy"));
//hello world複製程式碼

由於 Groovy 自身已經提供了更豐富的整合機制,如果在 Java 應用中只是使用 Groovy 一種指令碼語言的話,使用 Groovy 提供的整合機制可能會更合適一點。

與Java整合大致就以上幾種方式,在使用前推薦閱讀:Groovy與Java整合常見的坑

參考

相關文章