看看 Groovy 如何增加基於 Spring 的應用程序的靈活性
簡介:Spring Framework 為 Web 和企業應用程序提供了堅實的基礎。通過支 持 Groovy 等動態語言 ,Spring 添加了一些功能,從而使應用程序架構更加靈活、更具動態性。在包含 2 部分的系列文章 的 第一部分中,您將學習將 Groovy 集成到 Spring 應用程序的基礎知識。
Spring 2.0 支持將動態語言集成到基於 Spring 的應用程序中。Spring 開箱 即用地支持 Groovy、 JRuby 和 BeanShell。以 Groovy、JRuby 或任何受支持的語言(當然包括 Java™ 語言)編寫的 應用程序部分可以無縫地集成到 Spring 應用程序中。應用程序其他部分的代碼 不需要知道或關心單個 Spring bean 的實現語言。
Spring 支持動態語言意味著應用程序可以獲 得靈活性和動態性,並 且沒有任何附加條件。在本系列的第 1 部分中,您將看到如何將 Spring 和 Groovy 一起使用,以及這 個強大集成如何為應用程序增加有趣的功能。例如,您可能需要頻繁地更改小塊 的業務邏輯、應用程序 發出的 e-mail 消息中包含的文本、應用程序生成的 PDF 格式和布局等。為了進 行更改,傳統的應用程 序架構可能需要完全重新部署應用程序。Spring 支持 Groovy 之後,您可以這樣 更改一個已部署的應用 程序,並使這些更改立即生效。我將討論這一功能為應用程序所帶來的好處,以 及可能引發的問題。本 文中所有例子的完整的源代碼(參見 下載)都可以下載。
Spring 的動態 語言支持
動態 語言支持將 Spring 從一個以 Java 為中心的應用程序框架改變成一個以 JVM 為 中心的應用程序框架。 現在,Spring 不再只是讓 Java 開發變得更容易。它還允許將以靜態和動態語言 編寫的代碼輕松地插入 到 Spring 支持的分層架構方法中,從而使 JVM 的開發也變得更加容易。如果您 已經熟悉 Spring,那 麼您會感到很舒服:可以利用 Spring 已經提供的所有特性 — 控制反轉( IoC)和依賴項注入、 面向方面編程(AOP)、聲明式事務劃分、Web 和數據訪問框架集成、遠程調用等 — 同時又可以 使用靈活動態的語言,比如 Groovy。
Spring 通過 ScriptFactory 和 ScriptSource 接口支持 動態語言集成。ScriptFactory 接口定義用於創建和配置腳本 Spring bean 的機 制。理論上,所有在 JVM 上運行語言都受支持,因此可以選擇特定的語言來創建自己的實現。 ScriptSource 定義 Spring 如 何訪問實際的腳本源代碼;例如,通過文件系統或 URL。Groovy 語言集成通過 ScriptFactory 的 GroovyScriptFactory 實現得到支持。
為什麼是 Groovy?
根據官 方的 Groovy 站點, Groovy 是 “用於 Java 虛擬機的一種敏捷的動態語言”,它 “以 Java 的強大功能 為基礎,同時又包含由 Python、Ruby 和 Smalltalk 等語言帶來的強大附加功能 ”,例如動態類 型轉換、閉包和元編程(metaprogramming)支持(參見 參考資料)。它是一種 成熟的面向對象編程語 言,既可以用於面向對象編程,又可以用作純粹的腳本語言。我喜歡將它看作是 沒有討厭代碼,但又具 有閉包和動態語言中的其他特性的 Java 語言。
Groovy 特別適合與 Spring 的動態語言支持一 起使用,因為它是專門為 JVM 設計的,設計時充分考慮了 Java 集成,這使 Groovy 與 Java 代碼的互 操作很容易。它的類 Java 語法對於 Java 開發人員來說也很自然。
接下 來,看看如何將 Groovy 代碼集成到基於 Spring 的應用程序中。
更巧妙的 Spring bean
在 Spring 應用 程序中使用 Groovy bean 很容易,就像使用 Java bean 一樣。(但是,在後面 可以看到,對於如何配 置它們,則有很多選項)。首先,需要定義一個接口作為 Groovy bean 必須遵從 的約定。雖然不是非得 定義接口不可,但是大多數 Spring 應用程序會通過接口(而不是具體實現類) 來定義應用程序組件之 間的交互和依賴項,以促進松散耦合並為測試提供便利。
例如,假設有一 個定義如何從 Invoice 對象生成 PDF 的接口。如清單 1 所示:
清單 1. PdfGenerator 接口
public interface PdfGenerator { byte[] pdfFor(Invoice invoice); }
PdfGenerator 接口被用作 Groovy 實現類必須遵從的約定。這很容易 ,因為 Groovy 類可以 像 Java 類那樣實現接口。清單 2 顯示了 PdfGenerator 的 Groovy 實現,它使 用 iText 庫(參見 參 考資料)完成實際的 PDF 生成;它返回一個包含 PDF 內容的字節數組:
清單 2. GroovyPdfGenerator
class GroovyPdfGenerator implements PdfGenerator { String companyName public byte[] pdfFor(Invoice invoice) { Document document = new Document(PageSize.LETTER) ByteArrayOutputStream output = new ByteArrayOutputStream() PdfWriter.getInstance(document, output) document.open() Font headerFont = new Font(family: Font.HELVETICA, size: 24.0, style: Font.ITALIC) document.add(new Paragraph("$companyName", headerFont)) document.add(new Paragraph("Invoice $invoice.orderNumber")) document.add(new Paragraph("Total amount: \$ ${invoice.total}")) document.close() output.toByteArray() } }
GroovyPdfGenerator 已准備就緒。它定義了一個名為 companyName 的 string 屬性,該屬性在生成 的 PDF 發票上與訂單號和總額一起使用。此時,可以將 GroovyPdfGenerator 集 成到 Spring 應用程序 中。使用 Java 語言編寫的 bean 必須編譯成 .class 文件,但是在使用基於 Groovy 的 bean 時,則 有幾種選擇:
將 Groovy 類編譯成普通的 Java 類文件
在一個 .groovy 文件中定義 Groovy 類或腳本
在 Spring 配置文件中以內聯方式編寫 Groovy 腳本
可以選擇不同的方法在 Spring 應用程序上下文中定義和配置 Groovy bean, 這取決於 Groovy bean 采用的選項。接下來,我們將探討每一種配置選項。
Groovy bean 配置
通常,可以使用 XML 配置用 Java 代碼編寫的 Spring bean,或者 — 從 Spring 2.5(參見 參考 資料)開始 — 使用注釋進行配置,後者可以顯著減少 XML 配置。當配置 Groovy bean 時,可用的選 項取決於是使用編譯的 Groovy 類還是 .groovy 文件中定義的 Groovy 類。需要 記住的是,您可以使用 Groovy 實現 bean,然後可以像 Java 編程那樣編譯它們;或者在 .groovy 文件 中以類似腳本的形式實 現它們,然後由 Spring 負責在創建應用程序上下文時編譯它們。
如果選擇在 .groovy 文件中實現 bean,那麼您不必 自己編譯它們。相反, Spring 讀取文件,獲得 腳本源代碼並在運行時編譯它們,使它們可用於應用程序上下文。這比直接編譯 更靈活性,因為不一定 必須將 .groovy 文件部署在應用程序的 JAR 或 WAR 文件中,它們還可以來自文 件系統的某個地方或 URL。
接下來介紹各種不同的配置選項的應用。要記住在構建過程中自己編譯的 Groovy 類中定義的 bean 與在 .groovy 腳本中定義的 bean 之間的區別。
配置編譯的 Groovy 類
配置已經編譯成 .class 文件的 Groovy bean,這與配置基於 Java 的 bean 完全一樣。假設您已經 使用 groovyc 編譯器編譯了 GroovyPdfGenerator,那麼可以使用常規的 Spring XML 配置定義 bean, 如清單 3 所示:
清單 3. 使用 XML 配置預編譯的 GroovyPdfGenerator
<bean id="pdfGenerator" class="groovierspring.GroovyPdfGenerator"> <property name="companyName" value="Groovy Bookstore"/> </bean>
清單 3 中的配置是一個簡單的舊的 Spring bean 定義。它是用 Groovy 實現 的,但這一點不重要。 在包含 pdfGenerator bean 的 Spring 應用程序中,任何其他組件都可以使用它 ,而不必知道或關心它 的實現細節或語言。還可以像往常一樣使用 <property> 元素在 bean 上 設置屬性。(Spring 2.0 引入了 p 名稱空間,以便更簡練地定義屬性,但是我堅持使用 <property> 元素,因為我發 現它們可讀性更好 — 這完全是個人的喜好)。
另外,如果使用 Spring 2.5 或更高版本,還可以使用基於注釋的 GroovyPdfGenerator 的配置。在 此情況下,不必在 XML 應用程序上下文中實際定義 bean;相反,可以用 @Component 構造型注釋來注 釋類,如清單 4 所示:
清單 4. 用 @Component 注釋 GroovyPdfGenerator
@Component("pdfGenerator") class GroovyPdfGenerator implements PdfGenerator { ... }
然後,在 Spring 應用程序上下文 XML 配置中啟用注釋配置和組件掃描,如 清單 5 所示:
清單 5. 啟用 Spring 注釋配置和組件掃描
<context:annotation-config/> <context:component-scan base-package="groovierspring"/>
不管使用 XML 還是注釋來配置編譯後的 Groovy bean,這種配置與普通的基 於 Java bean 的配置是 一樣的。
配置來自 Groovy 腳本的 bean
配置來自 .groovy 腳本的 Groovy bean 與配置編譯後的 Groovy bean 大不 相同。在這裡,事情開 始變得更加有趣。將 Groovy 腳本轉換為 bean 的機制包括讀取並編譯 Groovy 腳本,然後使之可以在 Spring 應用程序上下文中作為 bean 使用。第一步是定義一個 bean,它的類型 可以認為是 GroovyScriptFactory,並且指向 Groovy 腳本的位置,如清單 6 所示:
清單 6. 定義 GroovyScriptFactory bean
<bean id="pdfGenerator" class="org.springframework.scripting.groovy.GroovyScriptFactory"> <constructor-arg value="classpath:groovierspring/GroovyPdfGenerator.groovy"/> <property name="companyName" value="Groovier Bookstore"/> </bean>
在這個清單中,pdfGenerator bean 被定義為 GroovyScriptFactory。 <constructor-arg> 元 素定義要配置的 Groovy 腳本的位置。特別要注意,這指向一個 Groovy 腳本, 而不是一個已編譯的 Groovy 類。可以使用定義 Spring bean 的語法設置用腳本編寫的對象的屬性。 正如您預期的那樣,清 單 6 中的 <property> 元素設置 companyName 屬性。
GroovyPdfGenerator.groovy 腳本 必須包含至少一個實現接口的類。通常, 最好的做法是遵從標准 Java 實現,每個 .groovy 文件定義一個 Groovy 類。但是,您可能想在腳本中 實現用於確定創建哪種 類型的 bean 的邏輯。例如,可以在 GroovyPdfGenerator.groovy 中定義 PdfGenerator 接口的兩種不 同的實現,並直接在腳本中執行確定應該返回哪種實現的邏輯。清單 7 定義兩種 不同的 PdfGenerator 實現,並根據系統的屬性選擇使用一種實現:
清單 7. Groovy 腳本中的多個類定義
class SimpleGroovyPdfGenerator implements PdfGenerator { ... } class ComplexGroovyPdfGenerator implements PdfGenerator { ... } def type = System.properties['generatorType'] if (type == 'simple') return new SimpleGroovyPdfGenerator() } else { return new ComplexGroovyPdfGenerator() }
如這段代碼所示,可以通過用腳本編寫的 bean 根據系統屬性選擇不同的實現 。當 generatorType 系統屬性為 simple 時,該腳本創建並返回一個 SimpleGroovyPdfGenerator;否 則,它返回一個 ComplexGroovyPdfGenerator。由於簡單和復雜的實現都實現了 PdfGenerator 接 口,因此 Spring 應用 程序中使用 pdfGenerator bean 的代碼不必知道也不必關心實際的實現是什麼。
注意,仍然可以像 清單 6 那樣在從腳本返回的 bean 上設置屬性。所以,如 果腳本返回一個 ComplexGroovyPdfGenerator,則設置該 bean 上的 companyName 屬性。如果不 需要定義多個實現,那 麼可以在 Groovy 腳本文件中僅定義一個類,如清單 8 所示。在這種情況下, Spring 發現並實例化這 個惟一的類。
清單 8. 典型的 Groovy 腳本實現
class GroovyPdfGenerator implements PdfGenerator { ... }
至此,您可能想知道為什麼 清單 6 將 bean 定義為一個 GroovyScriptFactory。那是因為 Spring 通過一個與 ScriptFactoryPostProcessor bean 結合的 ScriptFactory 實現( 在這裡是一個 Groovy 工廠)創建腳本對象。ScriptFactoryPostProcessor bean 負責用由工廠創建的 實際對象替換工廠 bean 。清單 9 顯示添加後處理器 bean 的附加配置:
清單 9. 定義 ScriptFactoryPostProcessor bean
<bean class="org.springframework.scripting.support.ScriptFactoryPostProcessor"/>
當 Spring 裝載應用程序上下文時,它首先創建工廠 bean(例如 GroovyScriptFactory bean)。然 後,執行 ScriptFactoryPostProcessor bean,用實際的腳本對象替換所有的工 廠 bean。例如,清單 6 和 清單 9 中的配置產生一個名為 pdfGenerator 的 bean,它的類型是 groovierspring.GroovyPdfGenerator。(如果啟用 Spring 中的 debug 級日志 記錄,並觀察應用程序 上下文的啟動,將會看到 Spring 首先創建一個名為 scriptFactory.pdfGenerator 的工廠 bean,然後 ScriptFactoryPostProcessor 從該工廠 bean 創建 pdfGenerator bean)。
現在,您已知道使用 GroovyScriptFactory 和 ScriptFactoryPostProcessor 配置腳本編寫的 Groovy bean 的底層細節,接下來我將展示一種更簡單、更整潔的方法。這種方 法可以得到相同結果。 Spring 專門為創建腳本 bean 提供了 lang XML 模式。清單 10 使用 lang 模式 定義 pdfGenerator bean:
清單 10. 使用 <lang:groovy> 定義腳本 bean
<lang:groovy id="pdfGenerator" script- source="classpath:groovierspring/GroovyPdfGenerator.groovy"> <lang:property name="companyName" value="Really Groovy Bookstore"/> </lang:groovy>
這段代碼產生的 pdfGenerator bean 與 清單 6 和 清單 9 中更冗長的配置 產生的 bean 是一樣的 ,但是它更整潔、更簡練,而且意圖更清晰。<lang:groovy> bean 定義需 要 script-source 屬 性;這告訴 Spring 如何找到 Groovy 腳本源代碼。此外,可以使用 <lang:property> 元素為腳 本 bean 設置屬性。使用 <lang:groovy> 定義基於 Groovy 的 bean 是一 種更好的選擇。對閱讀 Spring 配置的人而言,這種選項也更加清晰。
配置內聯 Groovy 腳本
為了實現完整性,我將介紹:Spring 還支持直接在 bean 定義中編寫 Groovy 腳本。清單 11 使用 一個內聯腳本創建 pdfGenerator:
清單 11. 內聯定義腳本 bean
<lang:groovy id="pdfGenerator"> <lang:inline-script> <![CDATA[ class GroovyPdfGenerator implements PdfGenerator { ... } ]]> </lang:inline-script> <lang:property name="companyName" value="Icky Groovy Bookstore"/> </lang:groovy>
這段代碼使用 <lang:groovy> 和 <lang:inline-script> 標記 定義 pdfGenerator bean,它包含定義類的 Groovy 腳本。可以像前面一樣使用 <lang:property> 設置屬性。您可能 已經猜到,我不建議在 XML 配置文件中定義腳本 bean(或這一方面的任何類型 的代碼)。
使用 Grails Bean Builder 配置 bean
Grails Web framework 在幕後依賴於 Spring。Grails 提供了 Bean Builder ,這是一個很棒的特性 ,讓您可以使用 Groovy 代碼編程式地 定義 Spring bean(參見 參考資料)。 編程式地定義 bean 比 XML 配置更靈活,因為可以在 bean 定義腳本中嵌入邏輯,而這在 XML 中是不可 能的。通過使用 Bean Builder,可以為已編譯 Groovy 類和用腳本編寫的 Groovy bean 創建 bean 定 義。清單 12 使用已編 譯的 Groovy 類定義 pdfGenerator bean:
清單 12. 使用 Bean Builder 定義已編譯的 Groovy bean
def builder = new grails.spring.BeanBuilder() builder.beans { pdfGenerator(GroovyPdfGenerator) { companyName = 'Compiled BeanBuilder Bookstore' } } def appContext = builder.createApplicationContext() def generator = context.pdfGenerator
清單 12 中的代碼首先實例化一個 BeanBuilder,然後通過方法調用創建 bean。每個方法調用和可 選的閉包參數定義一個 bean,並設置 bean 屬性。例如,pdfGenerator (GroovyPdfGenerator) 定義一 個名為 pdfGenerator 的 bean,其類型為 GroovyPdfGenerator,閉包中的代碼 則設置 companyName 屬 性。當然,在 beans 閉包中可以定義多個 bean。
通過使用 Bean Builder,還可以從 Groovy 腳本而不是已編譯的 Groovy 類 創建 bean。但是,Bean Builder 沒有 <lang:groovy> 配置中的語法糖(syntactic sugar,即在 計算機語言中添加的某 種語法,這種語法對語言的功能並沒有影響,但是更方便程序員使用),所以需 要將 bean 定義為 GroovyScriptFactory,並創建一個 ScriptFactoryPostProcessor bean。清單 13 是一個例子,展示如 何使用 Bean Builder 配置用腳本編寫的 Groovy bean:
清單 13. 使用 Bean Builder 定義用腳本編寫的 Groovy bean
def builder = new grails.spring.BeanBuilder() builder.beans { pdfGenerator(GroovyScriptFactory, 'classpath:groovierspring/GroovyPdfGenerator.groovy') { companyName = 'Scripted BeanBuilder Bookstore' } scriptFactoryPostProcessor(ScriptFactoryPostProcessor) } def appContext = builder.createApplicationContext() def generator = context.pdfGenerator
清單 13 中的代碼在邏輯上等同於 清單 6 和 清單 9 中的 XML 配置。當然 ,清單 13 是使用 Groovy 代碼來定義 bean。為了定義 pdfGenerator bean,清單 13 將類型指定 為 GroovyScriptFactory。第二個參數指定腳本源代碼的位置,和前面一樣,在閉包 中設置 companyName 屬性。它還定義一個名為 scriptFactoryPostProcessor 的 bean,其類型為 ScriptFactoryPostProcessor,它將用實際的用腳本編寫的對象替換工廠 bean。
哪種配置選項最好?
至此,您已經看到配置基於 Groovy 的 bean(無論是已編譯的還是用腳本編 寫的)的幾種不同的方 式。如果您僅是使用 Groovy 替代 Java 作為應用程序的主要語言,那麼配置這 些 bean 與配置基於 Java 的 bean 沒有區別。對於已編譯的 Groovy 類,可以使用 XML 或基於注釋 的配置進行配置。
對於用腳本編寫的 Groovy 對象,雖然可以用幾種不同的方式來配置它們,但 是 <lang:groovy> 選項卻是最簡潔的方式,與使用 GroovyScriptFactory 和 ScriptFactoryPostProcessor 或者使用 <lang:inline-script> 進行配置 相比,這種選項能夠最 清晰地表現意圖。
您還看到了 Grails Bean Builder,它以完全不同的方式創建大多數 Spring 應用程序使用的 Spring 應用程序上下文。如果要用 Groovy 創建所有的 bean,並且要能夠添加 邏輯到 bean 構建過程 中,Bean Builder 必須很好地符合要求。另一方面,使用 Bean Builder 定義 Groovy bean 時,需要 使用 GroovyScriptFactory 和 ScriptFactoryPostProcessor 來定義 bean。
使用 Groovy bean
bean 配置和可用的幾個選項是集成 Groovy 和 Spring 的難點(但是如您所 見,這並不是很難)。實際上,在 Spring 應用程序中使用 Groovy bean 很容易 。Spring 的動態語言支持使得 bean 的使用對於應用程序代碼是完全透明的,應 用程序代碼不需要知道也不需要關心實現細節。您可以像平常開發 Spring 應用 程序一樣編寫應用程序代碼,並且可以利用 Spring 提供的所有特性,例如依賴 項注入、AOP 和與第三方框架集成。
清單 14 展示了一個簡單的 Groovy 腳本,它從 XML 配置文件創建一個 Spring 應用程序上下文,獲取 PDF 生成器 bean,並使用它生成一個發票的 PDF 版本:
清單 14. 在腳本中使用 Groovy bean
def context = new ClassPathXmlApplicationContext ("applicationContext.xml")
def generator = context.getBean("pdfGenerator")
Invoice invoice = new Invoice(orderNumber: "12345", orderDate: new Date())
invoice.lineItems = [
new LineItem(quantity: 1, description: 'Groovy in Action (ebook)', price: 22.00),
new LineItem (quantity: 1, description: 'Programming Erlang', price: 45.00),
new LineItem(quantity: 2, description: 'iText in Action (ebook)', price: 22.00)
]
byte[] invoicePdf = generator.pdfFor(invoice)
FileOutputStream file = new FileOutputStream("Invoice- ${invoice.orderNumber}.pdf")
file.withStream {
file.write(invoicePdf)
}
println "Generated invoice $invoice.orderNumber"
在 清單 14 中,大部分代碼用於創建 Spring ApplicationContext,創建發 票並將它寫出到一個文件。使用 pdfGenerator bean 生成發票僅需一行代碼。在 通常的 Spring 應用程序中,在應用程序啟動時引導一次應用程序上下文,然後 ,應用程序中的組件只需使用 Spring 為它們提供的依賴項。在 Spring Web 應 用程序中,可以配置一個 servlet 上下文偵聽器,在應用程序啟動時引導 Spring。例如,可以定義一個 PDF 發票生成服務,如清單 15 所示:
清單 15. 使用 PDF 生成器的服務類
@Service
public class InvoicePdfServiceImpl implements InvoicePdfService {
@Autowired
private PdfGenerator pdfGenerator;
public byte[] generatePdf(Long invoiceId) {
Invoice invoice = getInvoiceSomehow(invoiceId);
return pdfGenerator.pdfFor(invoice);
}
// Rest of implementation...
}
清單 15 中的 InvoicePdfServiceImpl 類剛好被實現為一個 Java 類,它依 賴於 PdfGenerator。可以很方便地將它實現為 Groovy bean。可以通過任何以編 譯的或用腳本編寫的 bean 配置來使用 GroovyPdfGenerator 實現,而 InvoicePdfServiceImpl 對此一無所知。因此,使用 Groovy(或任何動態語言) 對應用程序代碼而言是透明的。這樣很好,因為實現了組件之間的松散耦合,從 而使單元測試更加容易,並且可以使用最適合的實現語言。
結束語
您已經看到了配置 Groovy 語言 bean 的一些不同的方式,以及在基於 Spring 的應用程序中使用它們是多麼容易。您可以像使用 Java 類一樣使用已編 譯的 Groovy 類。您還看到了配置用腳本編寫的 Groovy 對象的一些不同的方式 。應該選擇的選項取決於如何在應用程序中使用 Groovy。還可以在同一個應用程 序中結合使用已編譯的和用腳本編寫的 Groovy bean。實際上,如果希望的話, 還可以在同一個應用程序中同時使用 Java、Groovy、JRuby 和 BeanShell bean ,但我不建議這樣做。作為開發人員,必須權衡在同一應用程序中使用多種語言 的優點和缺點。
作為一種語言,Groovy 比 Java 更靈活,這使它成為很有吸引力的選擇,即 使僅選擇編譯 Groovy 類也是如此。Spring 可以集成用腳本編寫的動態語言 bean,這使人們更加喜歡選擇 Groovy,因為可以在用腳本編寫的 bean 中引入附 加的邏輯和靈活性。例如,正如前面看到的那樣,可以根據業務邏輯添加確定應 用程序啟動時應該實例化的 bean 類型的邏輯。或者,可以將用腳本編寫的對象 部署到 .groovy 文件中,使 Web 應用程序的部署更加靈活。.groovy 文件位於 應用程序的 CLASSPATH 中或文件系統中的某個地方,而不是打包在 WAR 文件中 。
到目前為止,您看到的所有東西都為 Spring 工具箱增加了靈活性和威力。但 是,Spring 動態語言支持中最引人注目的特性可能是在應用程序運行時 監視和 檢測對動態語言腳本的更改,並在 Spring 應用程序上下文中自動重新裝載 更改 後的 bean。第 2 部分將深入探索這個功能。包含 bean 的靜態配置在運行時不 能更改,與之對比,這個功能提供了很大的靈活性。
源碼:http://www.ibm.com/developerworks/cn/java/j- groovierspring1.html