如果您一直在閱讀這個(gè)系列,那么您應(yīng)該已經(jīng)看到有各種各樣使用 Groovy 的有趣方式,Groovy 的主要優(yōu)勢(shì)之一就是它的生產(chǎn)力。Groovy 代碼通常要比 Java 代碼更容易編寫(xiě),而且編寫(xiě)起來(lái)也更快,這使得它有足夠的資格成為開(kāi)發(fā)工作包中的一個(gè)附件。在另一方面,正如我在這個(gè)系列中反復(fù)強(qiáng)調(diào)的那樣,Groovy 并不是 —— 而且也不打算成為 —— Java 語(yǔ)言的替代。所以,這里存在的問(wèn)題是,能否把 Groovy 集成到 Java 的編程實(shí)踐中?或者說(shuō)這樣做有什么用?什么時(shí)候 這樣做有用?
這個(gè)月,我將嘗試回答這個(gè)問(wèn)題。我將從一些熟悉的事物開(kāi)始,即從如何將 Groovy 腳本編譯成與 Java 兼容的類文件開(kāi)始,然后進(jìn)一步仔細(xì)研究 Groovy 的編譯工具(groovyc
)是如何讓這個(gè)奇跡實(shí)現(xiàn)的。了解 Groovy 在幕后做了什么是在 Java 代碼中使用 Groovy 的第一步。
注意,本月示例中演示的一些編程技術(shù)是 Groovlets
框架和 Groovy 的 GroovyTestCase
的核心,這些技術(shù)我在前面的文章中已經(jīng)討論過(guò)。
關(guān)于本系列
把任何工具集成到自己的開(kāi)發(fā)實(shí)踐的關(guān)鍵就是知道什么時(shí)候使用它,而什么時(shí)候應(yīng)當(dāng)把它留在箱子里。腳本語(yǔ)言能夠成為工具箱中極為強(qiáng)大的附件,但只在將它恰當(dāng)應(yīng)用到合適場(chǎng)景時(shí)才這樣。為此, 實(shí)戰(zhàn) Groovy 的一系列文章專門(mén)探索了 Groovy 的實(shí)際應(yīng)用,并告訴您什么時(shí)候應(yīng)用它們,以及如何成功地應(yīng)用它們。
在本系列中以前的文章中,當(dāng)我介紹如何 用 Groovy 測(cè)試普通 Java 程序 的時(shí)候,您可能已經(jīng)注意到一些奇怪的事:我 編譯了 那些 Groovy 腳本。實(shí)際上,我將 groovy 單元測(cè)試編譯成普通的 Java .class 文件,然后把它們作為 Maven 構(gòu)建的一部分來(lái)運(yùn)行。
這種編譯是通過(guò)調(diào)用 groovyc
命令進(jìn)行的,該命令將 Groovy 腳本編譯成普通的 Java 兼容的 .class 文件。例如,如果腳本聲明了一個(gè)類,那么調(diào)用 groovyc
會(huì)生成至少三個(gè) .class 。文件本身會(huì)遵守標(biāo)準(zhǔn)的 Java 規(guī)則:.class 文件名稱要和聲明的類名匹配。
作為示例,請(qǐng)參見(jiàn)清單 1,它創(chuàng)建了一個(gè)簡(jiǎn)單的腳本,腳本聲明了幾個(gè)類。然后,您自己就可以看出 groovyc
命令生成的結(jié)果:
清單 1. Groovy 中的類聲明和編譯
package com.vanward.groovy class Person { fname lname age address contactNumbers String toString(){ numstr = new StringBuffer() if (contactNumbers != null){ contactNumbers.each{ numstr.append(it) numstr.append(" ") } } "first name: " + fname + " last name: " + lname + " age: " + age + " address: " + address + " contact numbers: " + numstr.toString() } } class Address { street1 street2 city state zip String toString(){ "street1: " + street1 + " street2: " + street2 + " city: " + city + " state: " + state + " zip: " + zip } } class ContactNumber { type number String toString(){ "Type: " + type + " number: " + number } } nums = [new ContactNumber(type:"cell", number:"555.555.9999"), new ContactNumber(type:"office", number:"555.555.5598")] addr = new Address(street1:"89 Main St.", street2:"Apt #2", city:"Utopia", state:"VA", zip:"34254") pers = new Person(fname:"Mollie", lname:"Smith", age:34, address:addr, contactNumbers:nums) println pers.toString() |
在清單 1 中,我聲明了三個(gè)類 —— Person
、Address
和 ContactNumber
。之后的代碼根據(jù)這些新定義的類型創(chuàng)建對(duì)象,然后調(diào)用toString()
方法。迄今為止,Groovy 中的代碼還非常簡(jiǎn)單,但現(xiàn)在來(lái)看一下清單 2 中 groovyc
產(chǎn)生什么樣的結(jié)果:
清單 2. groovyc 命令生成的類
aglover@12d21 /cygdrive/c/dev/project/target/classes/com/vanward/groovy $ ls -ls total 15 4 -rwxrwxrwx+ 1 aglover user 3317 May 3 21:12 Address.class 3 -rwxrwxrwx+ 1 aglover user 3061 May 3 21:12 BusinessObjects.class 3 -rwxrwxrwx+ 1 aglover user 2815 May 3 21:12 ContactNumber.class 1 -rwxrwxrwx+ 1 aglover user 1003 May 3 21:12 Person$_toString_closure1.class 4 -rwxrwxrwx+ 1 aglover user 4055 May 3 21:12 Person.class |
哇!五個(gè) .class 文件!我們了解 Person
、Address
和 ContactNumber
文件的意義,但是其他兩個(gè)文件有什么作用呢?
研究發(fā)現(xiàn),Person$_toString_closure1.class
是 Person
類的 toString()
方法中發(fā)現(xiàn)的閉包的結(jié)果。它是 Person
的一個(gè)內(nèi)部類,但是 BusinessObjects.class
文件是怎么回事 —— 它可能是什么呢?
對(duì) 清單 1 的深入觀察指出:我在腳本主體中編寫(xiě)的代碼(聲明完三個(gè)類之后的代碼)變成一個(gè) .class 文件,它的名稱采用的是腳本名稱。在這個(gè)例子中,腳本被命名為 BusinessObjects.groovy
,所以,類定義中沒(méi)有包含的代碼被編譯到一個(gè)名為 BusinessObjects
的 .class 文件。
反編譯這些類可能會(huì)非常有趣。由于 Groovy 處于代碼頂層,所以生成的 .java 文件可能相當(dāng)巨大;不過(guò),您應(yīng)當(dāng)注意的是 Groovy 腳本中聲明的類(如 Person
) 與類之外的代碼(比如 BusinessObjects.class
中找到的代碼)之間的區(qū)別。在 Groovy 文件中定義的類完成了 GroovyObject
的實(shí)現(xiàn),而在類之外定義的代碼則被綁定到一個(gè)擴(kuò)展自 Script
的類。
例如,如果研究由 BusinessObjects.class 生成的 .java 文件,可以發(fā)現(xiàn):它定義了一個(gè) main()
方法和一個(gè) run()
方法。不用驚訝,run()
方法包含我編寫(xiě)的、用來(lái)創(chuàng)建這些對(duì)象的新實(shí)例的代碼,而 main()
方法則調(diào)用 run()
方法。
這個(gè)細(xì)節(jié)的全部要點(diǎn)再一次回到了:對(duì) Groovy 的理解越好,就越容易把它集成到 Java 程序中。有人也許會(huì)問(wèn):“為什么我要這么做呢?”好了,我們想說(shuō)您用 Groovy 開(kāi)發(fā)了一些很酷的東西;那么如果能把這些東西集成到 Java 程序中,那不是很好嗎?
只是為了討論的原因,我首先試圖 用 Groovy 創(chuàng)建一些有用的東西,然后我再介紹把它嵌入到普通 Java 程序中的各種方法。
我熱愛(ài)音樂(lè)。實(shí)際上,我的 CD 收藏超過(guò)了我計(jì)算機(jī)圖書(shū)的收藏。多年以來(lái),我把我的音樂(lè)截取到不同的計(jì)算機(jī)上,在這個(gè)過(guò)程中,我的 MP3 收藏亂到了這樣一種層度:只是表示品種豐富的音樂(lè)目錄就有一大堆。
最近,為了讓我的音樂(lè)收藏回歸有序,我采取了第一步行動(dòng)。我編寫(xiě)了一個(gè)快速的 Groovy 腳本,在某個(gè)目錄的 MP3 收藏上進(jìn)行迭代,然后把每個(gè)文件的詳細(xì)信息(例如藝術(shù)家、專輯名稱等)提供給我。腳本如清單 3 所示:
清單 3. 一個(gè)非常有用的 Groovy 腳本
package com.vanward.groovy import org.farng.mp3.MP3File import groovy.util.AntBuilder class Song { mp3file Song(String mp3name){ mp3file = new MP3File(mp3name) } getTitle(){ mp3file.getID3v1Tag().getTitle() } getAlbum(){ mp3file.getID3v1Tag().getAlbum() } getArtist(){ mp3file.getID3v1Tag().getArtist() } String toString(){ "Artist: " + getArtist() + " Album: " + getAlbum() + " Song: " + getTitle() } static getSongsForDirectory(sdir){ println "sdir is: " + sdir ant = new AntBuilder() scanner = ant.fileScanner { fileset(dir:sdir) { include(name:"**/*.mp3") } } songs = [] for(f in scanner){ songs << new Song(f.getAbsolutePath()) } return songs } } songs = Song.getSongsForDirectory(args[0]) songs.each{ println it } |
正如您所看到的,腳本非常簡(jiǎn)單,對(duì)于像我這樣的人來(lái)說(shuō)特別有用。而我要做的全部工作只是把某個(gè)具體的目錄名傳遞給它,然后我就會(huì)得到該目錄中每個(gè) MP3 文件的相關(guān)信息(藝術(shù)家名稱、歌曲名稱和專輯) 。
現(xiàn)在讓我們來(lái)看看,如果要把這個(gè)干凈的腳本集成到一個(gè)能夠通過(guò)數(shù)據(jù)庫(kù)組織音樂(lè)甚至播放 MP3 的普通 Java 程序中,我需要做些什么。
正如前面討論過(guò)的,我的第一個(gè)選項(xiàng)可能只是用 groovyc
編譯腳本。在這個(gè)例子中,我期望 groovyc
創(chuàng)建 至少 兩個(gè) .class 文件 —— 一個(gè)用于 Song
類,另一個(gè)用于 Song
聲明之后的腳本代碼。
實(shí)際上,groovyc
可能創(chuàng)建 5 個(gè) .class 文件。這是與 Songs.groovy
包含三個(gè)閉包有關(guān),兩個(gè)閉包在 getSongsForDirectory()
方法中,另一個(gè)在腳本體中,我在腳本體中對(duì) Song
的集合進(jìn)行迭代,并調(diào)用 println
。
因?yàn)?.class 文件中有三個(gè)實(shí)際上是 Song.class 和 Songs.class 的內(nèi)部類,所以我只需要把注意力放在兩個(gè) .class 文件上。Song.class 直接映射到 Groovy 腳本中的 Song
聲明,并實(shí)現(xiàn)了 GroovyObject
,而 Songs.class 則代表我在定義 Song
之后編寫(xiě)的代碼,所以也擴(kuò)展了 Script
。
此時(shí)此刻,關(guān)于如何把新編譯的 Groovy 代碼集成到 Java 代碼,我有兩個(gè)選擇:可以通過(guò) Songs.class 文件中的 main()
方法運(yùn)行代碼 (因?yàn)樗鼣U(kuò)展了 Script
),或者可以將 Song.class 包含到類路徑中,就像在 Java 代碼中使用其他對(duì)象一樣使用它。
通過(guò) java
命令調(diào)用 Songs.class 文件非常簡(jiǎn)單,只要您記得把 Groovy 相關(guān)的依賴關(guān)系和 Groovy 腳本需要的依賴關(guān)系包含進(jìn)來(lái)就可以。把 Groovy 需要的類全都包含進(jìn)來(lái)的最簡(jiǎn)單方法就是把包含全部?jī)?nèi)容的 Groovy 可嵌入 jar 文件添加到類路徑中。在我的例子中,這個(gè)文件是 groovy-all-1.0-beta-10.jar。要運(yùn)行 Songs.class,需要記得包含將要用到的 MP3 庫(kù)(jid3lib-0.5.jar>),而且因?yàn)槲沂褂?nbsp;AntBuilder
,所以我還需要在類路徑中包含 Ant
。清單 4 把這些放在了一起:
清單 4. 通過(guò) Java 命令行調(diào)用 Groovy
c:\dev\projects>java -cp ./target/classes/;c:/dev/tools/groovy/ groovy-all-1.0-beta-10.jar;C:/dev/tools/groovy/ant-1.6.2.jar; C:/dev/projects-2.0/jid3lib-0.5.jar com.vanward.groovy.Songs c:\dev09\music\mp3s Artist: U2 Album: Zooropa Song: Babyface Artist: James Taylor Album: Greatest Hits Song: Carolina in My Mind Artist: James Taylor Album: Greatest Hits Song: Fire and Rain Artist: U2 Album: Zooropa Song: Lemon Artist: James Taylor Album: Greatest Hits Song: Country Road Artist: James Taylor Album: Greatest Hits Song: Don't Let Me Be Lonely Tonight Artist: U2 Album: Zooropa Song: Some Days Are Better Than Others Artist: Paul Simon Album: Graceland Song: Under African Skies Artist: Paul Simon Album: Graceland Song: Homeless Artist: U2 Album: Zooropa Song: Dirty Day Artist: Paul Simon Album: Graceland Song: That Was Your Mother |
雖然命令行的解決方案簡(jiǎn)單有趣,但它并不是所有問(wèn)題的最終解決方案。如果對(duì)更高層次的完善感興趣,那么可能將 MP3 歌曲工具直接導(dǎo)入 Java 程序。在這個(gè)例子中,我想導(dǎo)入 Song.class ,并像在 Java 語(yǔ)言中使用其他類那樣使用它。類路徑的問(wèn)題與上面相同 :我需要確保包含了 uber-Groovy jar 文件、Ant
和 jid3lib-0.5.jar 文件。在清單 5 中,可以看到如何將 Groovy MP3 工具導(dǎo)入簡(jiǎn)單的 Java 類中:
清單 5. 嵌入的 Groovy 代碼
package com.vanward.gembed; import com.vanward.groovy.Song; import java.util.Collection; import java.util.Iterator; public class SongEmbedGroovy{ public static void main(String args[]) { Collection coll = (Collection)Song.getSongsForDirectory ("C:\\music\\temp\\mp3s"); for(Iterator it = coll.iterator(); it.hasNext();){ System.out.println(it.next()); } } } |
就在您以為自己已經(jīng)掌握全部的時(shí)候,我要告訴您的是,還有更多在 Java 語(yǔ)言中使用 Groovy 的方法。除了通過(guò)直接編譯把 Groovy 腳本集成到 Java 程序中的這個(gè)選擇之外,當(dāng)我想直接嵌入腳本時(shí),還有其他一些選擇。
例如,我可以用 Groovy 的 GroovyClassLoader
,動(dòng)態(tài)地 加載一個(gè)腳本并執(zhí)行它的行為,如清單 6 所示:
清單 6. GroovyClassLoader 動(dòng)態(tài)地加載并執(zhí)行 Groovy 腳本
package com.vanward.gembed; import groovy.lang.GroovyClassLoader; import groovy.lang.GroovyObject; import groovy.lang.MetaMethod; import java.io.File; public class CLEmbedGroovy{ public static void main(String args[]) throws Throwable{ ClassLoader parent = CLEmbedGroovy.class.getClassLoader(); GroovyClassLoader loader = new GroovyClassLoader(parent); Class groovyClass = loader.parseClass( new File("C:\\dev\\groovy-embed\\src\\groovy\\ com\\vanward\\groovy\\Songs.groovy")); GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance(); Object[] path = {"C:\\music\\temp\\mp3s"}; groovyObject.setProperty("args", path); Object[] argz = {}; groovyObject.invokeMethod("run", argz); } } |
Meta,寶貝
如果您屬于那群瘋狂的人中的一員,熱愛(ài)反射,喜歡利用它們能做的精彩事情,那么您將熱衷于 Groovy 的 Meta
類。就像反射一樣,使用這些類,您可以發(fā)現(xiàn)GroovyObject
的各個(gè)方面(例如它的方法),這樣就可以實(shí)際地 創(chuàng)建 新的行為并執(zhí)行它。而且,這是 Groovy 的核心 —— 想想運(yùn)行腳本時(shí)它將如何發(fā)威吧!
注意,默認(rèn)情況下,類加載器將加載與腳本名稱對(duì)應(yīng)的類 —— 在這個(gè)例子中是 Songs.class,而不是 Song.class>。因?yàn)槲遥ê湍┲?Songs.class 擴(kuò)展了 Groovy 的 Script 類,所以不用想也知道接下來(lái)要做的就是執(zhí)行 run()
方法。
您記起,我的 Groovy 腳本也依賴于運(yùn)行時(shí)參數(shù)。所以,我需要恰當(dāng)?shù)嘏渲?nbsp;args
變量,在這個(gè)例子中,我把第一個(gè)元素設(shè)置為目錄名。
對(duì)于使用編譯好的類,而且,通過(guò)類加載器來(lái)動(dòng)態(tài)加載 GroovyObject
的替代,是使用 Groovy 優(yōu)美的 GroovyScriptEngine
和GroovyShell
動(dòng)態(tài)地執(zhí)行 Groovy 腳本。
把 GroovyShell
對(duì)象嵌入普通 Java 類,可以像類加載器所做的那樣動(dòng)態(tài)執(zhí)行 Groovy 腳本。除此之外,它還提供了大量關(guān)于控制腳本運(yùn)行的選項(xiàng)。在清單 7 中,可以看到 GroovyShell
嵌入到普通 Java 類的方式:
清單 7. 嵌入 GroovyShell
package com.vanward.gembed; import java.io.File; import groovy.lang.GroovyShell; public class ShellRunEmbedGroovy{ public static void main(String args[]) throws Throwable{ String[] path = {"C:\\music\\temp\\mp3s"}; GroovyShell shell = new GroovyShell(); shell.run(new File("C:\\dev\\groovy-embed\\src\\groovy\\ com\\vanward\\groovy\\Songs.groovy"), path); } } |
可以看到,運(yùn)行 Groovy 腳本非常容易。我只是創(chuàng)建了 GroovyShell
的實(shí)例,傳遞進(jìn)腳本名稱,然后調(diào)用 run()
方法。
還可以做其他事情。如果您喜歡,那么也可以得到自己腳本的 Script
類型的 GroovyShell
實(shí)例。使用 Script
類型,您就可以傳遞進(jìn)一個(gè) Binding
對(duì)象,其中包含任何運(yùn)行時(shí)值,然后再繼續(xù)調(diào)用 run()
方法,如清單 8 所示:
清單 8. 有趣的 GroovyShell
package com.vanward.gembed; import java.io.File; import groovy.lang.Binding; import groovy.lang.GroovyShell; import groovy.lang.Script; public class ShellParseEmbedGroovy{ public static void main(String args[]) throws Throwable{ GroovyShell shell = new GroovyShell(); Script scrpt = shell.parse( new File("C:\\dev\\groovy-embed\\src\\groovy\\ com\\vanward\\groovy\\Songs.groovy")); Binding binding = new Binding(); Object[] path = {"C:\\music\\temp\\mp3s"}; binding.setVariable("args",path); scrpt.setBinding(binding); scrpt.run(); } } |
GroovyScriptEngine
對(duì)象動(dòng)態(tài)運(yùn)行腳本的時(shí)候,非常像 GroovyShell
。區(qū)別在于:對(duì)于 GroovyScriptEngine
,您可以在實(shí)例化的時(shí)候給它提供一系列目錄,然后讓它根據(jù)要求去執(zhí)行多個(gè)腳本,如清單 9 所示:
清單 9. GroovyScriptEngine 的作用
package com.vanward.gembed; import java.io.File; import groovy.lang.Binding; import groovy.util.GroovyScriptEngine; public class ScriptEngineEmbedGroovy{ public static void main(String args[]) throws Throwable{ String[] paths = {"C:\\dev\\groovy-embed\\src\\groovy\\ com\\vanward\\groovy"}; GroovyScriptEngine gse = new GroovyScriptEngine(paths); Binding binding = new Binding(); Object[] path = {"C:\\music\\temp\\mp3s"}; binding.setVariable("args",path); gse.run("Songs.groovy", binding); gse.run("BusinessObjects.groovy", binding); } } |
在清單 9 中,我向?qū)嵗?nbsp;GroovyScriptEngine
傳入了一個(gè)數(shù)組,數(shù)據(jù)中包含我要處理的路徑,然后創(chuàng)建大家熟悉的 Binding
對(duì)象,然后再執(zhí)行仍然很熟悉的 Songs.groovy
腳本。只是為了好玩,我還執(zhí)行了BusinessObjects.groovy
腳本,您或許還能回憶起來(lái),它在開(kāi)始這次討論的時(shí)候出現(xiàn)過(guò)。
最后,當(dāng)然并不是最不重要的,是來(lái)自 Jakarta 的古老的 Bean 腳本框架( Bean Scripting Framework —— BSF)。BSF 試圖提供一個(gè)公共的 API,用來(lái)在普通 Java 應(yīng)用程序中嵌入各種腳本語(yǔ)言(包括 Groovy)。這個(gè)標(biāo)準(zhǔn)的、但是有爭(zhēng)議的最小公因子方法,可以讓您毫不費(fèi)力地將 Java 應(yīng)用程序嵌入 Groovy 腳本。
還記得前面的 BusinessObjects
腳本嗎?在清單 10 中,可以看到 BSF 可以多么容易地讓我把這個(gè)腳本插入普通 Java 程序中:
清單 10. BSF 開(kāi)始工作了
package com.vanward.gembed; import org.apache.bsf.BSFManager; import org.codehaus.groovy.runtime.DefaultGroovyMethods; import java.io.File; import groovy.lang.Binding; public class BSFEmbedGroovy{ public static void main(String args[]) throws Exception { String fileName = "C:\\dev\\project\\src\\groovy\\ com\\vanward\\groovy\\BusinessObjects.groovy"; //this is required for bsf-2.3.0 //the "groovy" and "gy" are extensions BSFManager.registerScriptingEngine("groovy", "org.codehaus.groovy.bsf.GroovyEngine", new String[] { "groovy" }); BSFManager manager = new BSFManager(); //DefaultGroovyMethods.getText just returns a //string representation of the contents of the file manager.exec("groovy", fileName, 0, 0, DefaultGroovyMethods.getText(new File(fileName))); } } |
如果在本文中有一件事是清楚的話,那么只件事就是 Groovy 為了 Java 代碼內(nèi)部的重用提供了一堆選擇。從把 Groovy 腳本編譯成普通 Java .class 文件,到動(dòng)態(tài)地加載和運(yùn)行腳本,這些問(wèn)題需要考慮的一些關(guān)鍵方面是靈活性和耦合。把 Groovy 腳本編譯成普通 .class 文件是 使用 您打算嵌入的功能的最簡(jiǎn)單選擇,但是動(dòng)態(tài)加載腳本可以使添加或修改腳本的行為變得更容易,同時(shí)還不必在編譯上犧牲時(shí)間。(當(dāng)然,這個(gè)選項(xiàng)只是在接口不變的情況下才有用。)
把腳本語(yǔ)言嵌入普通 Java 不是每天都發(fā)生,但是機(jī)會(huì)確實(shí)不斷出現(xiàn)。這里提供的示例把一個(gè)簡(jiǎn)單的目錄搜索工具嵌入到基于 Java 的應(yīng)用程序中,這樣 Java 應(yīng)用程序就可以很容易地變成 MP3 播放程序或者其他 MP3 播放工具。雖然我 可以 用 Java 代碼重新編寫(xiě) MP3 文件搜索器,但是我不需要這么做:Groovy 極好地兼容 Java 語(yǔ)言,而且,我很有興趣去擺弄所有選項(xiàng)!
- 您可以參閱本文在 developerWorks 全球站點(diǎn)上的 英文原文。
- 請(qǐng)閱讀完整的 實(shí)戰(zhàn) Groovy 文章系列,這些文章是根據(jù)系列的進(jìn)度建立在前面文章的基礎(chǔ)之上。與本期討論特別有關(guān)的是:
- “實(shí)戰(zhàn) Groovy: 用 Groovy 更迅速地對(duì) Java 代碼進(jìn)行單元測(cè)試” (developerWorks,2004 年 11 月),介紹了用 Groovy 和
GroovyTestCase
對(duì)普通 Java 程序進(jìn)行單元測(cè)試。 - “實(shí)戰(zhàn) Groovy: 用 Groovy 打造服務(wù)器端” (developerWorks,2005 年 3 月)介紹了 Groovlet。
- “實(shí)戰(zhàn) Groovy: 用 Groovy 更迅速地對(duì) Java 代碼進(jìn)行單元測(cè)試” (developerWorks,2004 年 11 月),介紹了用 Groovy 和
- 通過(guò) Teodor Zlatanov 撰寫(xiě)的文章 “Fun with MP3 and Perl”(developerWorks,2003 年 12 月),了解更多用腳本語(yǔ)言操作 MP3 文件的優(yōu)勢(shì)。
- 從“Customizing WebSphere Studio to use the Struts Scripting tool with ActionClasses in Jython”(developerWorks,2005 年 3 月)中獲得一些使用 Bean 腳本框架以及 Struts 和 Jython 的親自體驗(yàn)。
- 可以從 Groovy open source project page 下載 Groovy。
- 開(kāi)源的 Java ID3 Tag Library 是一個(gè)有趣的庫(kù),用它可以閱讀 MP3 文件的信息,例如歌曲標(biāo)題、藝術(shù)家、專輯等。
- Bean Scripting Framework 是作為 IBM alphaWorks 的一個(gè)項(xiàng)目開(kāi)始的,但現(xiàn)在已經(jīng)捐助給了 Apache Jakarta 項(xiàng)目。
- 在 developerWorks Java 技術(shù)專區(qū) 中可以找到 Java 編程各方面的文章。
- 還請(qǐng)參閱 Java 技術(shù)專區(qū)教程頁(yè)面 ,獲得 developerWorks 上免費(fèi)的、側(cè)重于 Java 的那些教程的完整清單。
- 通過(guò)參與 developerWorks blogs 加入 developerWorks 社區(qū)。
Andrew Glover 是 Vanward Technologies 的 CTO,該公司位于華盛頓特區(qū)大都會(huì)地區(qū),專門(mén)進(jìn)行自動(dòng)測(cè)試工作和框架的構(gòu)建,自動(dòng)測(cè)試工作和框架可以降低軟件 bug 的數(shù)量,減少集成和測(cè)試時(shí)間,提高整體的代碼穩(wěn)定性。Andrew Glover 還是 Java Testing Patterns (Wiley,2004 年 9 月)一書(shū)的合著者之一。