Spring Framework 為 Web 和企業應用程序提供了堅實的基礎通過支持 Groovy 等動態語言Spring 添加了一些功能從而使應用程序架構更加靈活更具動態性在包含 部分的系列文章 的第一部分中您將學習將 Groovy 集成到 Spring 應用程序的基礎知識
Spring 支持將動態語言集成到基於 Spring 的應用程序中Spring 開箱即用地支持 GroovyJRuby 和 BeanShell以 GroovyJRuby 或任何受支持的語言(當然包括 Java? 語言)編寫的應用程序部分可以無縫地集成到 Spring 應用程序中應用程序其他部分的代碼不需要知道或關心單個 Spring bean 的實現語言
Spring 支持動態語言意味著應用程序可以獲得靈活性和動態性並且沒有任何附加條件在本系列的第 部分中您將看到如何將 Spring 和 Groovy 一起使用以及這個強大集成如何為應用程序增加有趣的功能例如您可能需要頻繁地更改小塊的業務邏輯應用程序發出的 email 消息中包含的文本應用程序生成的 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 如何訪問實際的腳本源代碼例如通過文件系統或 URLGroovy 語言集成通過 ScriptFactory 的 GroovyScriptFactory 實現得到支持
為什麼是 Groovy?
根據官方的 Groovy 站點Groovy 是 用於 Java 虛擬機的一種敏捷的動態語言它 以 Java 的強大功能為基礎同時又包含由 PythonRuby 和 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 的接口如清單 所示
清單 PdfGenerator 接口
public interface PdfGenerator {
byte[] pdfFor(Invoice invoice);
}
PdfGenerator 接口被用作 Groovy 實現類必須遵從的約定這很容易因為 Groovy 類可以像 Java 類那樣實現接口清單 顯示了 PdfGenerator 的 Groovy 實現它使用 iText 庫(參見 參考資料)完成實際的 PDF 生成它返回一個包含 PDF 內容的字節數組
清單 GroovyPdfGenerator
class GroovyPdfGenerator implements PdfGenerator {
String companyName
public byte[] pdfFor(Invoice invoice) {
Document document = new Document(PageSizeLETTER)
ByteArrayOutputStream output = new ByteArrayOutputStream()
PdfWritergetInstance(document output)
documentopen()
Font headerFont = new Font(family: FontHELVETICA size: style: FontITALIC)
documentadd(new Paragraph($companyName headerFont))
documentadd(new Paragraph(Invoice $invoiceorderNumber))
documentadd(new Paragraph(Total amount: \$ ${invoicetotal}))
documentclose()
outputtoByteArray()
}
}
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 (參見 參考資料)開始 — 使用注釋進行配置後者可以顯著減少 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如清單 所示
清單 使用 XML 配置預編譯的 GroovyPdfGenerator
<bean id=pdfGenerator class=groovierspringGroovyPdfGenerator>
<property name=companyName value=Groovy Bookstore/>
</bean>
清單 中的配置是一個簡單的舊的 Spring bean 定義它是用 Groovy 實現的但這一點不重要在包含 pdfGenerator bean 的 Spring 應用程序中任何其他組件都可以使用它而不必知道或關心它的實現細節或語言還可以像往常一樣使用 <property> 元素在 bean 上設置屬性(Spring 引入了 p 名稱空間以便更簡練地定義屬性但是我堅持使用 <property> 元素因為我發現它們可讀性更好 — 這完全是個人的喜好)
另外如果使用 Spring 或更高版本還可以使用基於注釋的 GroovyPdfGenerator 的配置在此情況下不必在 XML 應用程序上下文中實際定義 bean相反可以用 @Component 構造型注釋來注釋類如清單 所示
清單 用 @Component 注釋 GroovyPdfGenerator
@Component(pdfGenerator)
class GroovyPdfGenerator implements PdfGenerator {
}
然後在 Spring 應用程序上下文 XML 配置中啟用注釋配置和組件掃描如清單 所示
清單 啟用 Spring 注釋配置和組件掃描
<context:annotationconfig/>
<context:componentscan basepackage=groovierspring/>
不管使用 XML 還是注釋來配置編譯後的 Groovy bean這種配置與普通的基於 Java bean 的配置是一樣的
配置來自 Groovy 腳本的 bean
配置來自 groovy 腳本的 Groovy bean 與配置編譯後的 Groovy bean 大不相同在這裡事情開始變得更加有趣將 Groovy 腳本轉換為 bean 的機制包括讀取並編譯 Groovy 腳本然後使之可以在 Spring 應用程序上下文中作為 bean 使用第一步是定義一個 bean它的類型可以認為是 GroovyScriptFactory並且指向 Groovy 腳本的位置如清單 所示
清單 定義 GroovyScriptFactory bean
<bean id=pdfGenerator
class=orgspringframeworkscriptinggroovyGroovyScriptFactory>
<constructorarg value=classpath:groovierspring/GroovyPdfGeneratorgroovy/>
<property name=companyName value=Groovier Bookstore/>
</bean>
在這個清單中pdfGenerator bean 被定義為 GroovyScriptFactory<constructorarg> 元素定義要配置的 Groovy 腳本的位置特別要注意這指向一個 Groovy 腳本而不是一個已編譯的 Groovy 類可以使用定義 Spring bean 的語法設置用腳本編寫的對象的屬性正如您預期的那樣清單 中的 <property> 元素設置 companyName 屬性
GroovyPdfGeneratorgroovy 腳本 必須包含至少一個實現接口的類通常最好的做法是遵從標准 Java 實現每個 groovy 文件定義一個 Groovy 類但是您可能想在腳本中實現用於確定創建哪種類型的 bean 的邏輯例如可以在 GroovyPdfGeneratorgroovy 中定義 PdfGenerator 接口的兩種不同的實現並直接在腳本中執行確定應該返回哪種實現的邏輯清單 定義兩種不同的 PdfGenerator 實現並根據系統的屬性選擇使用一種實現
清單 Groovy 腳本中的多個類定義
class SimpleGroovyPdfGenerator implements PdfGenerator {
}
class ComplexGroovyPdfGenerator implements PdfGenerator {
}
def type = Systemproperties[generatorType]
if (type == simple)
return new SimpleGroovyPdfGenerator()
}
else {
return new ComplexGroovyPdfGenerator()
}
如這段代碼所示可以通過用腳本編寫的 bean 根據系統屬性選擇不同的實現當 generatorType 系統屬性為 simple 時該腳本創建並返回一個 SimpleGroovyPdfGenerator否則它返回一個 ComplexGroovyPdfGenerator由於簡單和復雜的實現都實現了 PdfGenerator 接口因此 Spring 應用程序中使用 pdfGenerator bean 的代碼不必知道也不必關心實際的實現是什麼
注意仍然可以像 清單 那樣在從腳本返回的 bean 上設置屬性所以如果腳本返回一個 ComplexGroovyPdfGenerator則設置該 bean 上的 companyName 屬性如果不需要定義多個實現那麼可以在 Groovy 腳本文件中僅定義一個類如清單 所示在這種情況下Spring 發現並實例化這個惟一的類
清單 典型的 Groovy 腳本實現
class GroovyPdfGenerator implements PdfGenerator {
}
至此您可能想知道為什麼 清單 將 bean 定義為一個 GroovyScriptFactory那是因為 Spring 通過一個與 ScriptFactoryPostProcessor bean 結合的 ScriptFactory 實現(在這裡是一個 Groovy 工廠)創建腳本對象ScriptFactoryPostProcessor bean 負責用由工廠創建的實際對象替換工廠 bean清單 顯示添加後處理器 bean 的附加配置
清單 定義 ScriptFactoryPostProcessor bean
<bean class=orgspringframeworkscriptingsupportScriptFactoryPostProcessor/>
當 Spring 裝載應用程序上下文時它首先創建工廠 bean(例如 GroovyScriptFactory bean)然後執行 ScriptFactoryPostProcessor bean用實際的腳本對象替換所有的工廠 bean例如清單 和 清單 中的配置產生一個名為 pdfGenerator 的 bean它的類型是 groovierspringGroovyPdfGenerator(如果啟用 Spring 中的 debug 級日志記錄並觀察應用程序上下文的啟動將會看到 Spring 首先創建一個名為 scriptFactorypdfGenerator 的工廠 bean然後 ScriptFactoryPostProcessor 從該工廠 bean 創建 pdfGenerator bean)
現在您已知道使用 GroovyScriptFactory 和 ScriptFactoryPostProcessor 配置腳本編寫的 Groovy bean 的底層細節接下來我將展示一種更簡單更整潔的方法這種方法可以得到相同結果Spring 專門為創建腳本 bean 提供了 lang XML 模式清單 使用 lang 模式定義 pdfGenerator bean
清單 使用 <langgroovy> 定義腳本 bean
<lang:groovy id=pdfGenerator
scriptsource=classpath:groovierspring/GroovyPdfGeneratorgroovy>
<lang:property name=companyName value=Really Groovy Bookstore/>
</lang:groovy>
這段代碼產生的 pdfGenerator bean 與 清單 和 清單 中更冗長的配置產生的 bean 是一樣的但是它更整潔更簡練而且意圖更清晰<langgroovy> bean 定義需要 scriptsource 屬性這告訴 Spring 如何找到 Groovy 腳本源代碼此外可以使用 <langproperty> 元素為腳本 bean 設置屬性使用 <langgroovy> 定義基於 Groovy 的 bean 是一種更好的選擇對閱讀 Spring 配置的人而言這種選項也更加清晰
配置內聯 Groovy 腳本
為了實現完整性我將介紹Spring 還支持直接在 bean 定義中編寫 Groovy 腳本清單 使用一個內聯腳本創建 pdfGenerator
清單 內聯定義腳本 bean
<lang:groovy id=pdfGenerator>
<lang:inlinescript>
<![CDATA[
class GroovyPdfGenerator implements PdfGenerator {
}
]]>
</lang:inlinescript>
<lang:property name=companyName value=Icky Groovy Bookstore/>
</lang:groovy>
這段代碼使用 <langgroovy> 和 <langinlinescript> 標記定義 pdfGenerator bean它包含定義類的 Groovy 腳本可以像前面一樣使用 <langproperty> 設置屬性您可能已經猜到我不建議在 XML 配置文件中定義腳本 bean(或這一方面的任何類型的代碼)
使用 Grails Bean Builder 配置 bean
Grails Web framework 在幕後依賴於 SpringGrails 提供了 Bean Builder這是一個很棒的特性讓您可以使用 Groovy 代碼編程式地 定義 Spring bean(參見 參考資料)編程式地定義 bean 比 XML 配置更靈活因為可以在 bean 定義腳本中嵌入邏輯而這在 XML 中是不可能的通過使用 Bean Builder可以為已編譯 Groovy 類和用腳本編寫的 Groovy bean 創建 bean 定義清單 使用已編譯的 Groovy 類定義 pdfGenerator bean
清單 使用 Bean Builder 定義已編譯的 Groovy bean
def builder = new grailsspringBeanBuilder()
builderbeans {
pdfGenerator(GroovyPdfGenerator) {
companyName = Compiled BeanBuilder Bookstore
}
}
def appContext = buildercreateApplicationContext()
def generator = contextpdfGenerator
清單 中的代碼首先實例化一個 BeanBuilder然後通過方法調用創建 bean每個方法調用和可選的閉包參數定義一個 bean並設置 bean 屬性例如pdfGenerator(GroovyPdfGenerator) 定義一個名為 pdfGenerator 的 bean其類型為 GroovyPdfGenerator閉包中的代碼則設置 companyName 屬性當然在 beans 閉包中可以定義多個 bean
通過使用 Bean Builder還可以從 Groovy 腳本而不是已編譯的 Groovy 類創建 bean但是Bean Builder 沒有 <langgroovy> 配置中的語法糖(syntactic sugar即在計算機語言中添加的某種語法這種語法對語言的功能並沒有影響但是更方便程序員使用)所以需要將 bean 定義為 GroovyScriptFactory並創建一個 ScriptFactoryPostProcessor bean清單 是一個例子展示如何使用 Bean Builder 配置用腳本編寫的 Groovy bean
清單 使用 Bean Builder 定義用腳本編寫的 Groovy bean
def builder = new grailsspringBeanBuilder()
builderbeans {
pdfGenerator(GroovyScriptFactory
classpath:groovierspring/GroovyPdfGeneratorgroovy) {
companyName = Scripted BeanBuilder Bookstore
}
scriptFactoryPostProcessor(ScriptFactoryPostProcessor)
}
def appContext = buildercreateApplicationContext()
def generator = contextpdfGenerator
清單 中的代碼在邏輯上等同於 清單 和 清單 中的 XML 配置當然清單 是使用 Groovy 代碼來定義 bean為了定義 pdfGenerator bean清單 將類型指定為 GroovyScriptFactory第二個參數指定腳本源代碼的位置和前面一樣在閉包中設置 companyName 屬性它還定義一個名為 scriptFactoryPostProcessor 的 bean其類型為 ScriptFactoryPostProcessor它將用實際的用腳本編寫的對象替換工廠 bean
哪種配置選項最好?
至此您已經看到配置基於 Groovy 的 bean(無論是已編譯的還是用腳本編寫的)的幾種不同的方式如果您僅是使用 Groovy 替代 Java 作為應用程序的主要語言那麼配置這些 bean 與配置基於 Java 的 bean 沒有區別對於已編譯的 Groovy 類可以使用 XML 或基於注釋的配置進行配置
對於用腳本編寫的 Groovy 對象雖然可以用幾種不同的方式來配置它們但是 <langgroovy> 選項卻是最簡潔的方式與使用 GroovyScriptFactory 和 ScriptFactoryPostProcessor 或者使用 <langinlinescript> 進行配置相比這種選項能夠最清晰地表現意圖
您還看到了 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 和與第三方框架集成
清單 展示了一個簡單的 Groovy 腳本它從 XML 配置文件創建一個 Spring 應用程序上下文獲取 PDF 生成器 bean並使用它生成一個發票的 PDF 版本
清單 在腳本中使用 Groovy bean
def context = new ClassPathXmlApplicationContext(applicationContextxml)
def generator = contextgetBean(pdfGenerator)
Invoice invoice = new Invoice(orderNumber: orderDate: new Date())
invoicelineItems = [
new LineItem(quantity: description: Groovy in Action (ebook) price: )
new LineItem(quantity: description: Programming Erlang price: )
new LineItem(quantity: description: iText in Action (ebook) price: )
]
byte[] invoicePdf = generatorpdfFor(invoice)
FileOutputStream file = new FileOutputStream(Invoice${invoiceorderNumber}pdf)
filewithStream {
filewrite(invoicePdf)
}
println Generated invoice $invoiceorderNumber
在 清單 中大部分代碼用於創建 Spring ApplicationContext創建發票並將它寫出到一個文件使用 pdfGenerator bean 生成發票僅需一行代碼在通常的 Spring 應用程序中在應用程序啟動時引導一次應用程序上下文然後應用程序中的組件只需使用 Spring 為它們提供的依賴項在 Spring Web 應用程序中可以配置一個 servlet 上下文偵聽器在應用程序啟動時引導 Spring例如可以定義一個 PDF 發票生成服務如清單 所示
清單 使用 PDF 生成器的服務類
@Service
public class InvoicePdfServiceImpl implements InvoicePdfService {
@Autowired
private PdfGenerator pdfGenerator;
public byte[] generatePdf(Long invoiceId) {
Invoice invoice = getInvoiceSomehow(invoiceId);
return pdfGeneratorpdfFor(invoice);
}
// Rest of implementation
}
清單 中的 InvoicePdfServiceImpl 類剛好被實現為一個 Java 類它依賴於 PdfGenerator可以很方便地將它實現為 Groovy bean可以通過任何以編譯的或用腳本編寫的 bean 配置來使用 GroovyPdfGenerator 實現而 InvoicePdfServiceImpl 對此一無所知因此使用 Groovy(或任何動態語言)對應用程序代碼而言是透明的這樣很好因為實現了組件之間的松散耦合從而使單元測試更加容易並且可以使用最適合的實現語言
結束語
您已經看到了配置 Groovy 語言 bean 的一些不同的方式以及在基於 Spring 的應用程序中使用它們是多麼容易您可以像使用 Java 類一樣使用已編譯的 Groovy 類您還看到了配置用腳本編寫的 Groovy 對象的一些不同的方式應該選擇的選項取決於如何在應用程序中使用 Groovy還可以在同一個應用程序中結合使用已編譯的和用腳本編寫的 Groovy bean實際上如果希望的話還可以在同一個應用程序中同時使用 JavaGroovyJRuby 和 BeanShell bean但我不建議這樣做作為開發人員必須權衡在同一應用程序中使用多種語言的優點和缺點
作為一種語言Groovy 比 Java 更靈活這使它成為很有吸引力的選擇即使僅選擇編譯 Groovy 類也是如此Spring 可以集成用腳本編寫的動態語言 bean這使人們更加喜歡選擇 Groovy因為可以在用腳本編寫的 bean 中引入附加的邏輯和靈活性例如正如前面看到的那樣可以根據業務邏輯添加確定應用程序啟動時應該實例化的 bean 類型的邏輯或者可以將用腳本編寫的對象部署到 groovy 文件中使 Web 應用程序的部署更加靈活groovy 文件位於應用程序的 CLASSPATH 中或文件系統中的某個地方而不是打包在 WAR 文件中
到目前為止您看到的所有東西都為 Spring 工具箱增加了靈活性和威力但是Spring 動態語言支持中最引人注目的特性可能是在應用程序運行時 監視和檢測對動態語言腳本的更改並在 Spring 應用程序上下文中自動重新裝載 更改後的 bean第 部分將深入探索這個功能包含 bean 的靜態配置在運行時不能更改與之對比這個功能提供了很大的靈活性
下載
描述
名字
大小
下載
樣例代碼
jgroovierspringcodezip
MB
點擊
參考資料
您可以參閱本文在 developerWorks 全球網站上的 英文原文
From:http://tw.wingwit.com/Article/program/Java/ky/201311/28438.html