前言
由于項(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集成常見的坑