Groovy 語言快速入門

前言

由于項(xiàng)目需要用到 Groovy 語言,這兩天對(duì)其進(jìn)行了粗略的學(xué)習(xí)温治,本文是對(duì)學(xué)習(xí)做的一個(gè)簡單總結(jié)剧劝,主要內(nèi)容參考于官方文檔(Groovy 的官方文檔還是非常不錯(cuò)的,強(qiáng)烈推薦閱讀)舰攒,希望本文對(duì)準(zhǔn)備學(xué)習(xí)使用或者對(duì) Groovy 感興趣的同學(xué)有所幫助败富,如有不對(duì)之處還望指出哈,對(duì)這門語言的理解還是比較膚淺的摩窃。

簡介

Groovy 是 Apache 旗下的一門基于 JVM 平臺(tái)的動(dòng)態(tài)/敏捷編程語言兽叮,在語言的設(shè)計(jì)上它吸納了 Python、Ruby 和 Smalltalk 語言的優(yōu)秀特性猾愿,語法非常簡練和優(yōu)美鹦聪,開發(fā)效率也非常高(編程語言的開發(fā)效率和性能是相互矛盾的,越高級(jí)的編程語言性能越差匪蟀,因?yàn)橐馕吨嗟讓拥姆庋b椎麦,不過開發(fā)效率會(huì)更高,需結(jié)合使用場景做取舍)材彪。并且观挎,Groovy 可以與 Java 語言無縫對(duì)接,在寫 Groovy 的時(shí)候如果忘記了語法可以直接按Java的語法繼續(xù)寫段化,也可以在 Java 中調(diào)用 Groovy 腳本嘁捷,都可以很好的工作,這有效的降低了 Java 開發(fā)者學(xué)習(xí) Groovy 的成本显熏。Groovy 也并不會(huì)替代 Java雄嚣,而是相輔相成、互補(bǔ)的關(guān)系喘蟆,具體使用哪門語言這取決于要解決的問題和使用的場景缓升。

快速開始

1.下載Groovy開發(fā)工具包(GDK)
http://www.groovy-lang.org/download.html
2.創(chuàng)建Groovy項(xiàng)目
使用IDEA的話需要安裝Groovy的支持插件,安裝完成后在新建項(xiàng)目中便會(huì)出現(xiàn)Groovy項(xiàng)目選項(xiàng)蕴轨,選擇Groovy項(xiàng)目并關(guān)聯(lián)Groovy libray即可港谊,當(dāng)然也可以直接創(chuàng)建.groovy文件用命令行直接運(yùn)行。
3.Hello World
在Java中要輸出“hello world”需要像下面這樣橙弱,創(chuàng)建一個(gè)類歧寺,然后創(chuàng)建一個(gè)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'

當(dāng)然,也可以像Java一樣運(yùn)行在類的main方法中顷链。

class Hello {
    static void main(args) {
        println 'hello world'
    }
}

如果 Groovy 腳本文件里只有執(zhí)行代碼目代,沒有類的定義,則 Groovy 編譯器會(huì)生成一個(gè) Script 的子類蕴潦,類名和腳本文件的文件名一樣像啼,而腳本中的代碼會(huì)被包含在一個(gè)名為run的方法中,同時(shí)還會(huì)生成一個(gè)main方法潭苞,作為整個(gè)腳本的入口忽冻。所以,作為 JVM 平臺(tái)語言此疹,與 Java 本質(zhì)上還是一樣的僧诚。

與Java的一些區(qū)別

默認(rèn)導(dǎo)入

Groovy 會(huì)默認(rèn)導(dǎo)入下面這些包、類蝗碎,不需要使用import語句顯式導(dǎo)入湖笨。

java.io.*
java.lang.*
java.math.BigDecimal
java.math.BigInteger
java.net.*
java.util.*
groovy.lang.*
groovy.util.*

多重方法

在 Groovy 中,調(diào)用的方法將在運(yùn)行時(shí)被選擇蹦骑。這種機(jī)制被稱為運(yùn)行時(shí)分派或多重方法(multi-methods)慈省,是根據(jù)運(yùn)行時(shí)實(shí)參(argument)的類型來選擇方法。Java 采用的是相反的策略:編譯時(shí)根據(jù)聲明的類型來選擇方法眠菇。

下面是一個(gè)例子边败,同樣的 Java 代碼在 Java 和 Groovy 環(huán)境中運(yùn)行結(jié)果是不同的.

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);

產(chǎn)生差異的原因在于,Java 使用靜態(tài)數(shù)據(jù)類型捎废,o 被聲明為 Object 對(duì)象笑窜,而 Groovy 會(huì)在運(yùn)行時(shí)實(shí)際調(diào)用方法時(shí)進(jìn)行選擇。因?yàn)檎{(diào)用的是 String 類型的對(duì)象登疗,所以自然調(diào)用 String 版本的方法排截。

數(shù)組初始化語法

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

int[] array = { 1, 2, 3}

而應(yīng)該是下面這樣

int[] array = [1,2,3]

POJO

Groovy 默認(rèn)會(huì)隱式的創(chuàng)建getter断傲、setter方法,并且會(huì)提供帶參的構(gòu)造器智政,下面兩者是等價(jià)的艳悔。

// 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()

包訪問權(quán)限

在 Java 中如果沒有顯式的指定訪問修飾符(public、protected女仰、private)那么默認(rèn)是包訪問權(quán)限猜年,但在 Groovy 中默認(rèn)是public的,所以需要使用@PackageScope注解疾忍。

class Person {
    @PackageScope String name
}

ARM 語句塊

ARM(Automatic Resource Management乔外,自動(dòng)資源管理)語句塊(或者叫TWR語法)從 Java 7 開始引入,用于降低IO操作代碼的復(fù)雜度一罩,但 Groovy 并不支持杨幼。相反,Groovy 提供多種基于閉包的方法聂渊,不但可以達(dá)到同樣的效果并且會(huì)更加簡潔優(yōu)美差购。

//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
   }
}
//如果只是為了讀取并打印出文本的內(nèi)容的話汉嗽,下面是最簡潔的方式
print new File('/User/lihengming/test.txt').text

內(nèi)部類

Groovy 同樣支持內(nèi)部類并且實(shí)現(xiàn)跟 Java 是一樣的欲逃,但不應(yīng)拿 Java 語言規(guī)范來考量它,應(yīng)對(duì)差異情況保持冷靜與寬容(keep shaking the head about things that are different)饼暑。在Groovy中內(nèi)部類看起來有點(diǎn)類似 groovy.lang.Closure 類的實(shí)現(xiàn)稳析。

//靜態(tài)內(nèi)部類
class A {
    static class B {}
}
new A.B()

//匿名內(nèi)部類
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 表達(dá)式

Java 8 支持 Lambda 表達(dá)式和方法引用

Runnable run = () -> System.out.println("Run");
list.forEach(System.out::println);

Java 8 的 lambda 幾乎可以認(rèn)為是匿名內(nèi)部類。Groovy 并沒有采用這種語法弓叛,而采用閉包來實(shí)現(xiàn)彰居。

Runnable run = { println 'run' }
list.each { println it } // or list.each(this.&println)

GString

由于雙引號(hào)所包括起來的字符串字面量會(huì)被解釋為 GString 值(即 “Groovy 字符串”的簡稱),所以如果當(dāng)某個(gè)類中的 String 字面量含有美元字符($)時(shí)撰筷,那么利用 Groovy 和 Java 編譯器進(jìn)行編譯時(shí)陈惰,Groovy 很可能就會(huì)編譯失敗,或者產(chǎn)生與 Java 編譯所不同的結(jié)果毕籽。

通常抬闯,如果某個(gè) API 聲明了形參的類型,Groovy 會(huì)自動(dòng)轉(zhuǎn)換 GString 和 String影钉。要小心那些形參為 Object 的 Java API画髓,需要檢查它們的實(shí)際類型。

字符串和字符字面量

在 Groovy 中平委,由單引號(hào)所創(chuàng)建的字面量屬于 String 類型對(duì)象奈虾,而雙引號(hào)創(chuàng)建的字面量則可能是 String 或 GString 對(duì)象,具體分類由字面量中是否有插值來決定廉赔。

assert 'c'.getClass()==String
assert "c".getClass()==String
assert "c${1}".getClass() in GString  

基礎(chǔ)語法

  • Groovy 語句無需使用分號(hào)(;)結(jié)尾肉微,當(dāng)然加上也不會(huì)報(bào)錯(cuò),畢竟完全兼容 Java 的語法蜡塌。
  • Groovy 中==等價(jià)于 Java 中的equals方法碉纳。

注釋

注釋和 Java 一樣,支持單行(//)馏艾、多行(/* */)和文檔注釋(/** */)

除此之外還支持 Shebang line(UNIX系統(tǒng)支持一種特殊的單行注釋叫作 Shebang line劳曹,用于指明腳本的運(yùn)行環(huán)境奴愉,便于在終端中可以直接運(yùn)行)#號(hào)必須是文件的第一個(gè)字符。

#!/usr/bin/env groovy
println "Hello from the shebang line"

變量

Groovy 中定義變量默認(rèn)訪問修飾符是public铁孵,變量定義時(shí)遵循 Java 變量命名規(guī)范锭硼,變量名以字母、下劃線或美元符號(hào)$開始蜕劝,可以包含字母檀头、數(shù)字、下劃線和美元符號(hào)$岖沛,但關(guān)鍵字除外暑始。除了這些規(guī)則之外,Groovy 定義變量時(shí)如果是一行定義一個(gè)類型婴削,末尾的分號(hào)可以省略廊镜,但是如果多個(gè)變量占一行,變量之間必須以分號(hào)分割馆蠕。

Groovy 定義變量的方式和 Java 是類似的期升,區(qū)別在于 Groovy 提供了def關(guān)鍵字供使用,它可以省略變量類型的定義互躬,根據(jù)變量的值進(jìn)行類型推導(dǎo)播赁。

def a = 123
def b = 'b'
def c = true 
boolean d = false
int e = 123

如果定義變量的字面量值為數(shù)字時(shí),類型會(huì)根據(jù)數(shù)字的大小自動(dòng)調(diào)整

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

對(duì)于浮點(diǎn)型字面量為了精度 Groovy 默認(rèn)使用的類型為 BigDecimal

def decimal = 123.456
println decimal.getClass() // class java.math.BigDecimal

Groovy為 數(shù)字類型提供一種更簡單的聲明類型的方式:類型后綴

- 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)容为。
普通字符串使用單引號(hào)

println 'hello'

插值字符串使用雙引號(hào)

def name = '張三'
println "hello $name"

除此之外,還支持三單引號(hào)的寫法寺酪,可以保留文本的換行及縮進(jìn)格式

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關(guān)鍵字
assert c2 instanceof Character

def c3 = (char) 'C' // 強(qiáng)制類型轉(zhuǎn)換
assert c3 instanceof Character

方法(函數(shù))

Groovy 方法的默認(rèn)訪問修飾符是public得滤,方法的返回類型可以不需要聲明,但需添加def關(guān)鍵字盒犹。有返回值的方法return可以被省略懂更,默認(rèn)返回最后一行代碼的運(yùn)行結(jié)果,如果使用了return關(guān)鍵字則返回指定的返回值急膀。

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

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

def method3() {
    'hello'
}
assert method3() == 'hello';

Groovy 方法的參數(shù)類型可以被省略沮协,默認(rèn)為Object類型。

def add(int a, int b) {
    return a + b
}
//與上面的等價(jià)
def add(a, b) {
    a + b
}

Groovy 方法的其他特性與Java一樣卓嫂,比如支持重載慷暂、不定長參數(shù)(...)等。

閉包

Groovy 提供了閉包的支持晨雳,語法和 Lambda 表達(dá)式有些類似行瑞,簡單來說就是一段可執(zhí)行的代碼塊或函數(shù)指針奸腺。閉包在 Groovy 中是groovy.lang.Closure類的實(shí)例,這使得閉包可以賦值給變量蘑辑,或者作為參數(shù)傳遞洋机。Groovy 定義閉包的語法很簡單,就像下面這樣洋魂。

//閉包的參數(shù)為可選項(xiàng)
def closure = { [closureParameters -> ] statements }

閉包可以訪問外部變量,而方法(函數(shù))則不能喜鼓。

def str = 'hello'
def closure={
    println str
}
closure()//hello 

閉包調(diào)用的方式有兩種副砍,閉包.call(參數(shù))或者閉包(參數(shù)),在調(diào)用的時(shí)候可以省略圓括號(hào)庄岖。

def closure = {
    param -> println param
}
 
closure('hello')
closure.call('hello')
closure 'hello'

閉包的參數(shù)是可選的豁翎,如果沒有參數(shù)的話可以省略->操作符。

def closure = {println 'hello'}
closure()

多個(gè)參數(shù)以逗號(hào)分隔隅忿,參數(shù)類型和方法一樣可以顯式聲明也可省略心剥。

def closure = { String x, int y ->                                
    println "hey ${x} the value is ${y}"
}

如果只有一個(gè)參數(shù)的話,也可省略參數(shù)的定義背桐,Groovy提供了一個(gè)隱式的參數(shù)it來替代它优烧。

def closure = { it -> println it } 
//和上面是等價(jià)的
def closure = { println it }   
closure('hello')

閉包可以作為參數(shù)傳入,閉包作為方法的唯一參數(shù)或最后一個(gè)參數(shù)時(shí)可省略括號(hào)链峭。

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

eachLine('a'..'z',{ println it }) 
//可省略括號(hào)畦娄,與上面等價(jià)
eachLine('a'..'z') { println it }

Lists

Groovy 定義 List 的方式非常簡潔,使用中括號(hào)([])弊仪,以逗號(hào)(,)分隔元素即可熙卡。Groovy中的 List 其實(shí)就是java.util.List,實(shí)現(xiàn)類默認(rèn)使用的是java.util.ArrayList励饵。

def numbers = [1, 2, 3]         

assert numbers instanceof List  
assert numbers.class == java.util.ArrayList  
assert numbers.size() == 3 

Arrays

Groovy 定義數(shù)組的語法和 List 非常類似驳癌,區(qū)別在于數(shù)組的定義必須指定類型為數(shù)組類型,可以直接定義類型或者使用def定義然后通過as關(guān)鍵字來指定其類型役听。

String[] arrStr = ['Ananas', 'Banana', 'Kiwi'] //直接聲明類型為數(shù)組類型  String[]

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

def numArr = [1, 2, 3] as int[]     //痛過as關(guān)鍵字指定類型為數(shù)組類型 int[] 

assert numArr instanceof int[]       
assert numArr.size() == 3

Maps

Groovy 定義 Map 的方式非常簡潔颓鲜,通過中括號(hào)包含key、val的形式禾嫉,key和value以冒號(hào)分隔([key:value])灾杰。Groovy中的Map其實(shí)就是java.util.Map,實(shí)現(xiàn)類默認(rèn)使用的是java.util.LinkedHashMap熙参。

// key雖然沒有加引號(hào)艳吠,不過Groovy會(huì)默認(rèn)將其轉(zhuǎn)換為字符串
def colors = [red: '#FF0000', green: '#00FF00', blue: '#0000FF']

assert colors['red'] == '#FF0000' // 使用中括號(hào)訪問
assert colors.green == '#00FF00' // 使用點(diǎn)表達(dá)式訪問

colors['pink'] = '#FF00FF' // 使用中括號(hào)添加元素,相當(dāng)于Java Map 的 put(key,value)方法
colors.yellow = '#FFFF00'// 使用點(diǎn)表達(dá)式添加元素
assert colors.pink == '#FF00FF'
assert colors['yellow'] == '#FFFF00'
assert colors instanceof java.util.LinkedHashMap // 默認(rèn)使用LinkedHashMap類型

// Groovy Map的key默認(rèn)語法不支持變量孽椰,這里的key時(shí)間上是字符串'keyVal'而不是keyVal變量的值'name'
def keyVal = 'name'
def persons = [keyVal: 'Guillaume'] 
assert !persons.containsKey('name')
assert persons.containsKey('keyVal')

//要使用變量作為key昭娩,需要使用括號(hào)
def keyVal = 'name'
def persons = [(keyVal): 'Guillaume'] 
assert persons.containsKey('name')
assert !persons.containsKey('keyVal')

Range

在 Groovy 中可以使用..操作符來定義一個(gè)區(qū)間對(duì)象凛篙,簡化范圍操作的代碼。

def range = 0..5
assert (0..5).collect() == [0, 1, 2, 3, 4, 5]
assert (0..<5).collect() == [0, 1, 2, 3, 4] // 相當(dāng)于左閉右開區(qū)間
assert (0..5) instanceof List // Range實(shí)際上是List接口的實(shí)現(xiàn)
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 是一個(gè)基于 Groovy 語言栏渺,構(gòu)建于 Spring/Spring Boot呛梆、Hibernate 之上的高生產(chǎn)力、一站式 Web 開發(fā)框架磕诊。特別適合小型團(tuán)隊(duì)進(jìn)行敏捷開發(fā)填物,效率非常高。由于性能和學(xué)習(xí)成本的原因霎终,普及率比較低滞磺,大分部公司還是更傾向于選擇 Spring Boot 作為開發(fā)框架。

Gradle

Gradle 是一個(gè)基于 Apache Ant 和 Apache Maven 概念的項(xiàng)目自動(dòng)化構(gòu)建工具莱褒。它使用一種基于 Groovy 的特定領(lǐng)域語言(DSL)來進(jìn)行構(gòu)建配置击困,拋棄了基于XML的各種繁瑣配置,主要以面向Java應(yīng)用為主广凸,支持從 Maven 倉庫下載依賴≡牟瑁現(xiàn)在越來越多的項(xiàng)目(Android項(xiàng)目居多)使用Gradle 來作為項(xiàng)目的構(gòu)建工具,相信未來 Gradle 也會(huì)逐漸代替 Maven谅海,就像 Maven 代替 Ant 那樣脸哀。

使用Maven構(gòu)建項(xiàng)目

<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構(gòu)建項(xiàng)目

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 版本開始對(duì)動(dòng)態(tài)腳本語言進(jìn)行了支持(Spring 官方文檔該部分地址),其中便包括 Groovy 胁赢,Spring 提供了<lang:groovy/>標(biāo)簽來定義 Groovy Bean 企蹭。Groovy Bean 可以通過script-source屬性加載腳本文件,腳本源文件可以來自本地或者網(wǎng)絡(luò)智末,并且可以通過refresh-check-delay屬性監(jiān)聽腳本內(nèi)代碼變更重新裝載 Bean 實(shí)現(xiàn)動(dòng)態(tài) 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環(huán)境中,利用其動(dòng)態(tài)性來做規(guī)則引擎尼酿、流程引擎爷狈、動(dòng)態(tài)腳本環(huán)境,非常適合那些不不需要經(jīng)常發(fā)布但又經(jīng)常變更的場景下使用裳擎。在Java中集成(調(diào)用)Groovy 代碼有下面幾種方式涎永。

Eval

groovy.util.Eval 類是最簡單的用來在運(yùn)行時(shí)動(dòng)態(tài)執(zhí)行 Groovy 代碼的類,提供了幾個(gè)靜態(tài)工廠方法供使用,內(nèi)部其實(shí)就是對(duì)GroovyShell的封裝羡微。

//執(zhí)行Groovy代碼
Eval.me("println 'hello world'");
//綁定自定義參數(shù)
Object result = Eval.me("age", 22, "if(age < 18){'未成年'}else{'成年'}");
assertEquals(result, "成年");
//綁定一個(gè)名為 x 的參數(shù)的簡單計(jì)算
assertEquals(Eval.x(4, "2*x"), 8);
//帶有兩個(gè)名為 x 與 y 的綁定參數(shù)的簡單計(jì)算
assertEquals(Eval.xy(4, 5, "x*y"), 20);
//帶有三個(gè)綁定參數(shù)(x谷饿、y 和 z)的簡單計(jì)算
assertEquals(Eval.xyz(4, 5, 6, "x*y+z"), 26); 
GroovyShell

groovy.lang.GroovyShell除了可以執(zhí)行 Groovy 代碼外,提供了更豐富的功能妈倔,比如可以綁定更多的變量博投,從文件系統(tǒng)、網(wǎng)絡(luò)加載代碼等盯蝴。

GroovyShell shell = new GroovyShell();
//可以綁定更多變量
shell.setVariable("age",22);
//直接求值
shell.evaluate("if(age < 18){'未成年'}else{'成年'}");
//解析為腳本毅哗,延遲執(zhí)行或者緩存起來
Script script = shell.parse("if(age < 18){'未成年'}else{'成年'}");
assertEquals(script.run(), "成年");
//可以從更多位置加載/執(zhí)行腳本
//shell.evaluate(new File("script.groovy"));
//shell.evaluate(new URI("http://wwww.a.com/script.groovy"));
GroovyClassLoader

groovy.lang.GroovyClassLoader是一個(gè)定制的類加載器,可以在運(yùn)行時(shí)加載 Groovy 代碼捧挺,生成 Class 對(duì)象黎做。

 GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
 String scriptText = "class Hello { void hello() { println 'hello' } }";
 //將Groovy腳本解析為Class對(duì)象
 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 代碼的動(dòng)態(tài)編譯與加載,可以從統(tǒng)一的位置加載腳本松忍,并且能夠監(jiān)聽腳本的變化,當(dāng)腳本發(fā)生變化時(shí)會(huì)重新加載筷厘。

//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內(nèi)代碼修改為println "hi $name"鸣峭, GroovyScriptEngine會(huì)重新進(jìn)行加載
hi groovy
hi groovy
JSR 223 javax.script API

JSR-223 是 Java 中調(diào)用腳本語言的標(biāo)準(zhǔn) API。從 Java 6 開始引入進(jìn)來酥艳,主要目的是用來提供一種統(tǒng)一的框架摊溶,以便在 Java 中調(diào)用多種腳本語言。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 自身已經(jīng)提供了更豐富的集成機(jī)制,如果在 Java 應(yīng)用中只是使用 Groovy 一種腳本語言的話惰爬,使用 Groovy 提供的集成機(jī)制可能會(huì)更合適一點(diǎn)喊暖。

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

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末撕瞧,一起剝皮案震驚了整個(gè)濱河市陵叽,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌丛版,老刑警劉巖巩掺,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異页畦,居然都是意外死亡胖替,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來刊殉,“玉大人殉摔,你說我怎么就攤上這事〖呛福” “怎么了逸月?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長遍膜。 經(jīng)常有香客問我碗硬,道長,這世上最難降的妖魔是什么瓢颅? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任恩尾,我火速辦了婚禮,結(jié)果婚禮上挽懦,老公的妹妹穿的比我還像新娘翰意。我一直安慰自己,他們只是感情好信柿,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布冀偶。 她就那樣靜靜地躺著,像睡著了一般渔嚷。 火紅的嫁衣襯著肌膚如雪进鸠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天形病,我揣著相機(jī)與錄音客年,去河邊找鬼。 笑死漠吻,一個(gè)胖子當(dāng)著我的面吹牛量瓜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播侥猩,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼榔至,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了欺劳?” 一聲冷哼從身側(cè)響起唧取,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎划提,沒想到半個(gè)月后枫弟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鹏往,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年淡诗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡韩容,死狀恐怖款违,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情群凶,我是刑警寧澤插爹,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站请梢,受9級(jí)特大地震影響赠尾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜毅弧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一气嫁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧够坐,春花似錦寸宵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蛾坯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間疏遏,已是汗流浹背脉课。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留财异,地道東北人倘零。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像戳寸,于是被迫代替她去往敵國和親呈驶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理疫鹊,服務(wù)發(fā)現(xiàn)袖瞻,斷路器,智...
    卡卡羅2017閱讀 134,599評(píng)論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,748評(píng)論 6 342
  • Groovy是一門基于JVM的動(dòng)態(tài)語言拆吆,很多語法和Java類似聋迎。大部分Java代碼也同時(shí)是合法的Groovy代碼。...
    樂百川閱讀 3,577評(píng)論 0 15
  • 最近AACTP的換屆選舉的風(fēng)波總結(jié)一波接著一波牺堰!在網(wǎng)上瘋狂的傳播著~點(diǎn)擊量不斷的被刷新著拄轻!就是一場選舉風(fēng)波創(chuàng)造了太...
    祥祥布魯斯閱讀 1,100評(píng)論 3 4
  • 現(xiàn)在的天氣扒俯,春裝好像一直在衣櫥里掛著奶卓。上一秒穿的還是羽絨服,下一秒恨不得秒變 T恤撼玄。悶熱讓你心煩意亂夺姑? 便利店冷鮮...
    食而不惑閱讀 488評(píng)論 4 8