我對http://xglw.51.net/5team/springframework/viewtopic.php?t=18的翻譯進(jìn)行了一些修訂,并且接著翻譯了未完成的60%。
——————————————————————
Introducing to Spring Framework
作者:Rod Johnson
譯者:yanger,taowen
校對:taowen
關(guān)于Spring Framework,今年夏天你可能已經(jīng)聽見很多的議論。在本文中,我將試圖解釋Spring能完成什么,和我怎么會認(rèn)為它能幫助你開發(fā)J2EE應(yīng)用程序。
又來一個framework?
你可能正在想“不過是另外一個的framework”。當(dāng)已經(jīng)有許多開放源代碼(和專有) J2EE framework時,為什么你還要耐下心子讀這篇文章或去下載Spring Framework?
我相信Spring是獨特的,有幾個原因:
它關(guān)注的領(lǐng)域是其他許多流行的Framework未曾關(guān)注的。Spring要提供的是一種管理你的業(yè)務(wù)對象的方法。
Spring既是全面的又是模塊化的。Spring有分層的體系結(jié)構(gòu),這意味著你能選擇僅僅使用它任何一個獨立的部分,而它的架構(gòu)又是內(nèi)部一致。因此你能從你的學(xué)習(xí)中,得到最大的價值。例如,你可能選擇僅僅使用Spring來簡單化JDBC的使用,或用來管理所有的業(yè)務(wù)對象。
它的設(shè)計從一開始就是要幫助你編寫易于測試的代碼。Spring是使用測試驅(qū)動開發(fā)的工程的理想框架。
Spring不會給你的工程添加對其他的框架依賴。Spring也許稱得上是個一站式解決方案,提供了一個典型應(yīng)用所需要的大部分基礎(chǔ)架構(gòu)。它還涉及到了其他framework沒有考慮到的內(nèi)容。
盡管它僅僅是一個從2003年2月才開始的開源項目,但Spring有深厚的歷史根基。這個開源工程是起源自我在2002年晚些時候出版的《Expert One-on-One J2EE設(shè)計與開發(fā)》書中的基礎(chǔ)性代碼。這本書展示了Spring背后的基礎(chǔ)性架構(gòu)思想。然而,對這個基礎(chǔ)架構(gòu)的概念可以追溯到2000年的早些時候,并且反映了我為一系列商業(yè)工程開發(fā)基礎(chǔ)結(jié)構(gòu)的成功經(jīng)驗。
2003年1月,Spring已經(jīng)落戶于SourceForge上了。現(xiàn)在有10個開發(fā)人員,其中6個是高度投入的積極分子。
Spring架構(gòu)上的好處
在我們進(jìn)入細(xì)節(jié)之前,讓我們來看看Spring能夠給工程帶來的種種好處:
Spring能有效地組織你的中間層對象,不管你是否選擇使用了EJB。如果你僅僅使用了Struts或其他為J2EE的 API特制的framework,Spring致力于解決剩下的問題。
Spring能消除在許多工程中常見的對Singleton的過多使用。根據(jù)我的經(jīng)驗,這是一個很大的問題,它降低了系統(tǒng)的可測試性和面向?qū)ο蟮某潭取?
通過一種在不同應(yīng)用程序和項目間一致的方法來處理配置文件,Spring能消除各種各樣自定義格式的屬性文件的需要。曾經(jīng)對某個類要尋找的是哪個魔法般的屬性項或系統(tǒng)屬性感到不解,為此不得不去讀Javadoc甚至源編碼?有了Spring,你僅僅需要看看類的JavaBean屬性。Inversion of Control的使用(在下面討論)幫助完成了這種簡化。
通過把對接口編程而不是對類編程的代價幾乎減少到?jīng)]有,Spring能夠促進(jìn)養(yǎng)成好的編程習(xí)慣。
Spring被設(shè)計為讓使用它創(chuàng)建的應(yīng)用盡可能少的依賴于他的APIs。在Spring應(yīng)用中的大多數(shù)業(yè)務(wù)對象沒有依賴于Spring。
使用Spring構(gòu)建的應(yīng)用程序易于單元測試。
Spring能使EJB的使用成為一個實現(xiàn)選擇,而不是應(yīng)用架構(gòu)的必然選擇。你能選擇用POJOs或local EJBs來實現(xiàn)業(yè)務(wù)接口,卻不會影響調(diào)用代碼。
Spring幫助你解決許多問題而無需使用EJB。Spring能提供一種EJB的替換物,它們適用于許多web應(yīng)用。例如,Spring能使用AOP提供聲明性事務(wù)管理而不通過EJB容器,如果你僅僅需要與單個數(shù)據(jù)庫打交道,甚至不需要一個JTA實現(xiàn)。
Spring為數(shù)據(jù)存取提供了一個一致的框架,不論是使用的是JDBC還是O/R mapping產(chǎn)品(如Hibernate)。
Spring確實使你能通過最簡單可行的解決辦法來解決你的問題。而這是有有很大價值的。
Spring做了些什么?
Spring提供許多功能,在此我將依次快速地展示其各個主要方面。
任務(wù)描述
首先,讓我們明確Spring范圍。盡管Spring覆蓋了許多方面,但我們對它應(yīng)該涉什么,什么不應(yīng)該涉及有清楚的認(rèn)識。
Spring的主要目的是使J2EE易用和促進(jìn)好編程習(xí)慣。
Spring不重新輪子。因此,你發(fā)現(xiàn)在Spring中沒有l(wèi)ogging,沒有連接池,沒有分布式事務(wù)調(diào)度。所有這些東西均有開源項目提供(例如我們用于處理所有日志輸出的Commons Logging以及Commons DBCP),或由你的應(yīng)用程序服務(wù)器提供了。出于同樣的的原因,我們沒有提供O/R mapping層。對于這個問題已經(jīng)有了像Hibernate和JDO這樣的優(yōu)秀解決方案。
Spring的目標(biāo)就是讓已有的技術(shù)更加易用。例如,盡管我們沒有底層事務(wù)協(xié)調(diào)處理,但我們提供了一個抽象層覆蓋了JTA或任何其他的事務(wù)策略。
Spring沒有直接和其他的開源項目競爭,除非我們感到我們能提供新的一些東西。例如,象許多開發(fā)人員一樣,我們從來沒有對Struts感到高興過,并且覺得到在MVC web framework中還有改進(jìn)的余地。在某些領(lǐng)域,例如輕量級的IoC容器和AOP框架,Spring確實有直接的競爭,但是在這些領(lǐng)域還沒有已經(jīng)較為流行的解決方案。(Spring在這些領(lǐng)域是開路先鋒。)
Spring也得益于內(nèi)在的一致性。所有的開發(fā)者都在唱同樣的的贊歌,基礎(chǔ)想法依然與Expert One-on-One J2EE設(shè)計與開發(fā)中提出的差不多。 并且我們已經(jīng)能夠在多個領(lǐng)域中使用一些中心的概念,例如Inversion of Control。
Spring在應(yīng)用服務(wù)器之間是可移植的。當(dāng)然保證可移植性總是一種挑戰(zhàn),但是我們避免使用任何平臺特有或非標(biāo)準(zhǔn)的東西,并且支持在WebLogic,Tomcat,Resin,JBoss,WebSphere和其他的應(yīng)用服務(wù)器上的用戶。
Inversion of Control 容器
Spring設(shè)計的核心是 org.springframework.beans 包, 它是為與JavaBeans一起工作而設(shè)計的。 這個包一般不直接被用戶使用,而是作為許多其他功能的基礎(chǔ)。
下一個層面高一些的抽象是"Bean Factory"。一個Spring bean factory 是一個通用的Factory,它使對象能夠按名稱獲取,并且能管理對象之間的關(guān)系。
Bean factories 支持兩種模式的對象:
Singleton:在此模式中,有一個具有特定名稱的共享對象實例,它在查找時被獲取。這是默認(rèn)的,而且是最為經(jīng)常使用的。它對于無狀態(tài)對象是一種理想的模式。
Prototype:在此模式中,每次獲取將創(chuàng)建一個獨立的對象。例如,這可以被用于讓用戶擁有他們自己的對象。
由于 org.springframwork.beans.factory.BeanFactory是一個簡單的接口,它能被大量底層存儲方法實現(xiàn)。你能夠方便地實現(xiàn)你自己的BeanFactory,盡管很少用戶需要這么做。最為常用的BeanFactory定義是:
XmlBeanFactory: 可解析簡單直觀的定義類和命名對象屬性的XML結(jié)構(gòu)。 我們提供了一個DTD來使編寫更容易。
ListableBeanFactoryImpl:提供了解析存放在屬性文件中的bean定義的能力,并且可通過編程創(chuàng)建BeanFactories。
每個bean定義可能是一個POJO(通過類名和JavaBean初始屬性定義),或是一個FactoryBean。FactoryBean接口添加了一個間接層。通常,這用于創(chuàng)建使用AOP或其他方法的代理對象:例如,添加聲明性事務(wù)管理的代理。(這在概念上和EJB的interception相似,但實現(xiàn)得更簡單。)
BeanFactories能在一個層次結(jié)構(gòu)中選擇性地參與,繼承ancestor(祖先)的定義。這使得在整個應(yīng)用中公共配置的共享成為可能,雖然個別資源,如controller servlets,還擁有他們自己的獨立的對象集合。
這種使用JavaBeans的動機(jī)在《Expert One-on-One J2EE Design and Development》的第四章中有描述,在TheServerSide網(wǎng)站上的有免費的PDF版本(http://www.theserverside.com/resources/article.jsp?l=RodJohnsonInterview)。
通過BeanFactory概念,Spring成為一個Inversion of Control的容器。(我不怎么喜歡container這個詞,因為它使人聯(lián)想到重量級容器,如EJB容器。Spring的BeanFactory是一個可通過一行代碼創(chuàng)建的容器,并且不需要特殊的部署步驟。)
Inversion of Control背后的概念經(jīng)常表述為Hollywood原則的:“Don’t call me, I’ll call you。” IoC將控制創(chuàng)建的職責(zé)搬進(jìn)了框架中,并把它從應(yīng)用代碼脫離開來。涉及到配置的地方,意思是說在傳統(tǒng)的容器體系結(jié)構(gòu)中,如EJB,一個組件可以調(diào)用容器并問“我需要它給我做工作的對象X在哪里?”;使用IoC容器則只需指出組件需要X對象,在運行時容器會提供給它。容器是通過查看方法的參數(shù)表(例如JavaBean的屬性)做到的,也可能根據(jù)配置數(shù)據(jù)如XML。
IoC有幾個重要的好處,例如:
因為組件不需要在運行時間尋找合作者,所以他們可以更簡單的編寫和維護(hù)。在Spring版的IoC里,組件通過暴露JavaBean的setter方法表達(dá)他們依賴的其他組件。這相當(dāng)于EJB通過JNDI來查找,EJB查找需要開發(fā)人員編寫代碼。
同樣原因,應(yīng)用代碼更容易測試。JavaBean屬性是簡單的,屬于Java核心的,并且是容易測試的:僅編寫一個自包含的Junit測試方法用來創(chuàng)建對象和設(shè)置相關(guān)屬性即可。
一個好的IoC實現(xiàn)保留了強(qiáng)類型。如果你需要使用一個通用的factory來尋找合作者,你必須通過類型轉(zhuǎn)換將返回結(jié)果轉(zhuǎn)變?yōu)橄胍念愋汀_@不是一個大不了的問題,但是不雅觀。使用IoC,你在你的代碼中表達(dá)了強(qiáng)類型依賴,框架將負(fù)責(zé)類型轉(zhuǎn)換。這意味著在框架配置應(yīng)用時,類型不匹配將導(dǎo)致錯誤;在你的代碼中,你無需擔(dān)心類型轉(zhuǎn)換異常。
大部分業(yè)務(wù)對象不依賴于IoC容器的APIs。這使得很容易使用遺留下來的代碼,且很容易的使用對象無論在容器內(nèi)或不在容器內(nèi)。例如,Spring用戶經(jīng)常配置Jakarta Commons DBCP數(shù)據(jù)源為一個Spring bean:不需要些任何定制代碼去做這件事。我們說一個IoC容器不是侵入性的:使用它并不會使你的代碼依賴于它的APIs。任何JavaBean在Spring bean factory中都能成為一個組件。
最后應(yīng)該強(qiáng)調(diào)的是,IoC 不同于傳統(tǒng)的容器的體系結(jié)構(gòu),如EJB,應(yīng)用代碼最小程度地依靠于容器。這意味著你的業(yè)務(wù)對象可以潛在的被運行在不同的IoC 框架上——或者在任何框架之外——不需要任何代碼的改動。
以我和其他Spring用戶的經(jīng)驗來說,再怎么強(qiáng)調(diào)IoC給應(yīng)用程序代碼帶來的好處也不為過。
IoC不是一個新概念,但是它在J2EE團(tuán)體里面剛剛到達(dá)黃金時間。 有一些可供選擇的IoC 容器: 例如 Apache Avalon, PicoContainer 和 HiveMind。Avalon 從沒怎么流行,盡管它很強(qiáng)大而且有很長的歷史。Avalon相當(dāng)?shù)闹睾蛷?fù)雜,并且看起來比新的IoC解決方案更具侵入性。 PicoContainer是一個輕量級而且更強(qiáng)調(diào)通過構(gòu)造函數(shù)表達(dá)依賴性而不是JavaBean 屬性。 與Spring不同,它的設(shè)計允許每個類型一個對象的定義(可能是因為它拒絕任何Java代碼外的元數(shù)據(jù)導(dǎo)致的局限性)。在Spring, PicoContainer 和其他 IoC frameworks之間做比較,可參看文章Spring網(wǎng)站上的"The Spring Framework - A Lightweight Container"位于http://www.springframework.org/docs/lightweight_container.html。這個頁面里面包含了PicoContainer站點的鏈接 。
Spring BeanFactories 是非常輕量級的。用戶已經(jīng)成功地將他們應(yīng)用在applets和單獨的Swing應(yīng)用中。(它們也很好地工作在EJB容器中。) 沒有特殊的部署步驟和察覺得到的啟動時間。這個能力表明一個容器在應(yīng)用的任何層面幾乎立即可以發(fā)揮非常大的價值。
Spring BeanFactory 概念貫穿于Spring始終, 而且是Spring如此內(nèi)在一致的關(guān)鍵原因。在IoC容器中,Spring也是唯一的,它使用IoC作為基礎(chǔ)概念貫穿于整個功能豐富的框架。
對應(yīng)用開發(fā)人員,最重要的是,一個或多個BeanFactory提供了一個定義明確的業(yè)務(wù)對象層。這類似于local session bean層,但比它更簡單。與EJBs不同,在這個層中的對象可能是相關(guān)的,并且他們的關(guān)系被擁有它們的factory管理。有一個定義明確的業(yè)務(wù)對象層對于成功的體系結(jié)構(gòu)是非常重要的。
Spring ApplicationContext 是BeanFactory的子接口,為下列東西提供支持:
信息查找,支持著國際化
事件機(jī)制,允許發(fā)布應(yīng)用對象以及可選的注冊以接收到事件
可移植的文件和資源訪問
XmlBeanFactory 例子
Spring用戶通常在XML的“bean定義”文件中配置他們的應(yīng)用。Spring的XML bean定義文檔的根是<beans> 元素。該元素包含一個或多個 <bean>定義。我們一般給每個bean定義的指定類和屬性。我們還必須指定ID作為標(biāo)識,這將成為在代碼中使用該bean的名字。
讓我們來看一個簡單的例子,它配置了三個應(yīng)用程序?qū)ο螅g的關(guān)系在J2EE應(yīng)用中常常能夠看到:
J2EE DataSource
使用DataSource的DAO
在處理過程中使用DAO的業(yè)務(wù)對象
在下面的例子中,我們使用一個來自Jakarta Commons DBCP項目的BasicDataSource。這個class(和其他許多已有的class一樣)可以簡單地被應(yīng)用在Spring bean factory中,只要它提供了JavaBean格式的配置。需要在shutdown時被調(diào)用的Close方法可通過Spring的"destroy-method"屬性被注冊,以避免BasicDataSource需要實現(xiàn)任何Spring 的接口。
java代碼: |
<beans>
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property> <property name="url"><value>jdbc:mysql://localhost:3306/mydb</value></property> <property name="username"><value>root</value></property> </bean>
|
BasicDataSource中我們感興趣的所有屬性都是String類型的,因此我們用<value>元素來指定他們的值。如果必要的話,Spring使用標(biāo)準(zhǔn)的 JavaBean屬性編輯器機(jī)制來把String轉(zhuǎn)換為其他的類型。
現(xiàn)在,我們定義DAO,它有一個對DataSource的bean引用。Bean間關(guān)系通過<ref>元素來指定:
java代碼: |
<bean id="exampleDataAccessObject" class="example.ExampleDataAccessObject"> <property name="dataSource"><ref bean="myDataSource"/></property> </bean>
The business object has a reference to the DAO, and an int property (exampleParam): <bean id="exampleBusinessObject" class="example.ExampleBusinessObject"> <property name="dataAccessObject"><ref bean="exampleDataAccessObject"/></property> <property name="exampleParam"><value>10</value></property> </bean>
</beans>
|
對象間的關(guān)系一般在配置中明確地設(shè)置,象這個例子一樣。我們認(rèn)為這樣做是件好事情。然而Spring還提供了我們稱做"autowire"的支持, 一個la PicoContainer,其中它指出了bean間的依賴關(guān)系。這樣做的局限性——PicoContainer也是如此——是如果有一個特殊類型的多個Bean,要確定那個類型所依賴的是哪個實例是不可能。好的方面是,不滿足的依賴可以在factory初始化后被捕獲到。(Spring 也為顯式的配置提供了一種可選的依賴檢查,它可以完成這個目的)
在上面的例子中,如果我們不想顯式的編寫他們的關(guān)系,可使用如下的autowire特性:
java代碼: |
<bean id="exampleBusinessObject" class="example.ExampleBusinessObject" autowire="byType">
<property name="exampleParam"><value>10</value></property> </bean>
|
使用這個特性,Spring會找出exampleBusinessObject的dataSource屬性應(yīng)該被設(shè)置為在當(dāng)前BeanFactory中找到的DataSource實現(xiàn)。在當(dāng)前的BeanFactory中,如果所需要類型的bean不存在或多于一個,將產(chǎn)生一個錯誤。我們依然要設(shè)置exampleParam屬性,因為它不是一個引用。
Autowire支持和依賴檢查剛剛加入CVS并將在Spring 1.0 M2(到10/20,2003)中提供。本文中所討論的所有其他特性都包含在當(dāng)前1.0 M1版本中。
把管理從Java代碼中移出來比硬編碼有很大的好處,因為這樣可以只改變XML文件而無需改變一行Java代碼。例如,我們可以簡單地改變myDataSource的bean定義引用不同的bean class以使用別的連接池,或者一個用于測試的數(shù)據(jù)源。 XML節(jié)變成另一種,我們可以用Spring的JNDI location FactoryBean從應(yīng)用服務(wù)器獲取一個數(shù)據(jù)源。
現(xiàn)在讓我們來看看例子中業(yè)務(wù)對象的java 代碼。注意下面列出的代碼中沒有對Spring的依賴。不像EJB容器,Spring BeanFactory不具有侵入性:在應(yīng)用對象里面你通常不需要對Spring的存在硬編碼。
java代碼: |
public class ExampleBusinessObject implements MyBusinessObject {
private ExampleDataAccessObject dao; private int exampleParam;
public void setDataAccessObject(ExampleDataAccessObject dao) { this.dao = dao; }
public void setExampleParam(int exampleParam) { this.exampleParam = exampleParam; }
public void myBusinessMethod() { // do stuff using dao } }
|
注意那些property setter,它們對應(yīng)于bean定義文檔中的XML引用。這些將在對象被使用之前由Spring調(diào)用。
這些應(yīng)用程序的bean不需要依賴于Spring:他們不需要實現(xiàn)任何Spring的接口或者繼承Spring的類。他們只需要遵守JavaBeans的命名習(xí)慣。在Spring 應(yīng)用環(huán)境之外重用它們是非常簡單的,例如,在一個測試環(huán)境中。只需要用它們的缺省構(gòu)造函數(shù)實例化它們,并且通過調(diào)用setDataSource()和setExampleParam()手工設(shè)置它的屬性。如果你想以一行代碼支持程序化的創(chuàng)建,只要你有一個無參數(shù)的構(gòu)造器,你就可以自由定義其他需要多個屬性的構(gòu)造函數(shù)。
注意在業(yè)務(wù)接口中沒有聲明將會一起使用的JavaBean屬性。 他們是一個實現(xiàn)細(xì)節(jié)。我們可以“插入”帶有不同bean屬性的不同的實現(xiàn)類而不影響連接著的對象或者調(diào)用的代碼。
當(dāng)然,Spring XML bean factories 有更多的功能沒有在這里描述,但是,應(yīng)當(dāng)讓你對基本使用有了一些感覺。以及,簡單的屬性,有JavaBean屬性編輯器的屬性,Spring可以自動處理lists,maps和java.util.Properties。
Bean factories 和application contexts 通常和J2EE server定義的一個范圍相關(guān)聯(lián),例如:
Servlet context.:在spring 的MVC 框架里, 每一個包含common objects的web 應(yīng)用都定義有一個應(yīng)用程序的context。Spring提供了通過listener或者servlet實例化這樣的context的能力而不需要依賴于Spring 的MVC 框架,因而它也可以用于Struts,WebWork 或者其他的web框架之中。
A Servlet:在Spring MVC 框架里每一個servlet控制器都有它自己的應(yīng)用程序context,派生于根(全應(yīng)用程序范圍的)應(yīng)用程序context。在Struts或者其他MVC框架中實現(xiàn)這些也很容意。
EJB:Spring 為EJB提供方便的超類,它們簡化了EJB的創(chuàng)建并且提供了一個從EJB Jar 文件中的XML文檔載入的BeanFactory。
這些J2EE規(guī)范提供的hook通常避免了使用Singleton來創(chuàng)造一個bean factory。
然而,如果我們愿意的話可以用代碼創(chuàng)建一個BeanFactory,雖然是沒有什么意義的。例如,我們在以下三行代碼中可以創(chuàng)建bean factory并且得到一個業(yè)務(wù)對象的引用:
java代碼: |
InputStream is = getClass().getResourceAsStream("myFile.xml"); XmlBeanFactory bf = new XmlBeanFactory(is); MyBusinessObject mbo = (MyBusinessObject) bf.getBean("exampleBusinessObject");
|
這段代碼將能工作在一個應(yīng)用服務(wù)器之外:甚至不依賴J2EE,因為Spring 的IoC容器是純java的。
JDBC 抽象和數(shù)據(jù)存儲異常層次
數(shù)據(jù)訪問是Spring 的另一個閃光點。
JDBC 提供了還算不錯的數(shù)據(jù)庫抽象,但是需要用痛苦的API。這些問題包括:
需要冗長的錯誤處理代碼來確保ResultSets,Statements以及(最重要的)Connections在使用后關(guān)閉。這意味著對JDBC的正確使用可以快速地導(dǎo)致大量的代碼量。它還是一個常見的錯誤來源。Connection leak可以在有負(fù)載的情況下快速宕掉應(yīng)用程序。
SQLException相對來說不能說明任何問題。JDBC不提供異常的層次,而是用拋出SQLException來響應(yīng)所有的錯誤。找出到底哪里出錯了——例如,問題是死鎖還是無效的SQL?——要去檢查SQLState或錯誤代碼。這意味著這些值在數(shù)據(jù)庫之間是變化的。
Spring用兩種方法解決這些問題:
提供API,把冗長乏味和容易出錯的異常處理從程序代碼移到框架之中。框架處理所有的異常處理;程序代碼能夠集中精力于編寫恰當(dāng)?shù)腟QL和提取結(jié)果上。
為你本要處理SQLException程序代碼提供有意義的異常層次。當(dāng)Spring第一次從數(shù)據(jù)源取得一個連接時,它檢查元數(shù)據(jù)以確定數(shù)據(jù)庫。它使用這些信息把SQLException映射為自己從org.springframework.dao.DataAccessException派生下來的類層次中正確的異常。因而你的代碼可以與有意義的異常打交道,并且不需要為私有的SQLState或者錯誤碼擔(dān)心。Spring的數(shù)據(jù)訪問異常不是JDBC特有的,因而你的DAO并不一定會因為它們可能拋出的異常而綁死在JDBC上。
Spring提供兩層JDBC API。第一個時,在org.springframework.jdbc.core包中,使用回調(diào)機(jī)制移動控制權(quán)——并且因而把錯誤處理和連接獲取和釋放——從程序的代碼移到了框架之中。這是一種不同的Inversion of Control,但是和用于配置管理的幾乎有同等重要的意義。
Spring使用類似的回調(diào)機(jī)制關(guān)注其他包含特殊獲取和清理資源步驟的API,例如JDO(獲取和釋放是由PersistenceManager完成的),事務(wù)管理(使用JTA)和JNDI。Spring中完成這些回調(diào)的類被稱作template。
例如,Spring的JdbcTemplate對象能夠用于執(zhí)行SQL查詢并且在如下的列表中保存結(jié)果:
java代碼: |
JdbcTemplate template = new JdbcTemplate(dataSource); final List names = new LinkedList(); template.query("SELECT USER.NAME FROM USER", new RowCallbackHandler() { public void processRow(ResultSet rs) throws SQLException { names.add(rs.getString(1)); } });
|
注意回調(diào)中的程序代碼是能夠自由拋出SQLException的:Spring將會捕捉到這些異常并且用自己的類層次重新拋出。程序的開發(fā)者可以選擇哪個異常,如果有的話,被捕捉然后處理。
JdbcTemplate提供許多支持不同情景包括prepared statements和批量更新的方法。Spring的JDBC抽象有比起標(biāo)準(zhǔn)JDBC來說性能損失非常小,甚至在當(dāng)應(yīng)用中需要的結(jié)果集數(shù)量很大的時候。
在org.springframework.jdbc.object包中是對JDBC的更高層次的抽象。這是建立在核心的JDBC回調(diào)功能基礎(chǔ)紙上的,但是提供了一個能夠?qū)DBMS操作——無論是查詢,更新或者是存儲過程——使用Java對象來建模的API。這個API部分是受到JDO查詢API的影響,我發(fā)現(xiàn)它直觀而且非常有用。
一個用于返回User對象的查詢對象可能是這樣的:
java代碼: |
class UserQuery extends MappingSqlQuery {
public UserQuery(DataSource datasource) { super(datasource, "SELECT * FROM PUB_USER_ADDRESS WHERE USER_ID = ?"); declareParameter(new SqlParameter(Types.NUMERIC)); compile(); }
// Map a result set row to a Java object protected Object mapRow(ResultSet rs, int rownum) throws SQLException { User user = new User(); user.setId(rs.getLong("USER_ID")); user.setForename(rs.getString("FORENAME")); return user; }
public User findUser(long id) { // Use superclass convenience method to provide strong typing return (User) findObject(id); } }
|
這個類可以在下面用上:
java代碼: |
User user = userQuery.findUser(25);
|
這樣的對象經(jīng)常可以用作DAO的inner class。它們是線程安全的,除非子類作了一些超出常規(guī)的事情。
在org.springframework.jdbc.object包中另一個重要的類是StoredProcedure類。Spring讓存儲過程通過帶有一個業(yè)務(wù)方法的Java類進(jìn)行代理。如果你喜歡的話,你可以定義一個存儲過程實現(xiàn)的接口,意味著你能夠把你的程序代碼從對存儲過程的依賴中完全解脫出來。
Spring數(shù)據(jù)訪問異常層次是基于unchecked(運行時)exception的。在幾個工程中使用了Spring之后,我越來越確信這個決定是正確的。
數(shù)據(jù)訪問異常一般是不可恢復(fù)的。例如,如果我們不能鏈接到數(shù)據(jù)庫,某個業(yè)務(wù)對象很有可能就不能完成要解決的問題了。一個可能的異常是optimistic locking violation,但是不是所有的程序使用optimistic locking。強(qiáng)制編寫捕捉其無法有效處理的致命的異常通常是不好的。讓它們傳播到上層的handler,比如servlet或者EJB 容器通常更加合適。所有的Spring對象訪問異常都是DataAccessException的子類,因而如果我們確實選擇了捕捉所有的Spring數(shù)據(jù)訪問異常,我們可以很容易做到這點。
注意如果我們確實需要從unchecked數(shù)據(jù)訪問異常中恢復(fù),我們?nèi)匀豢梢赃@么做。我們可以編寫代碼僅僅處理可恢復(fù)的情況。例如,如果我們認(rèn)為只有optimistic locking violation是可恢復(fù)的,我們可以在Spring的DAO中如下這么寫:
java代碼: |
try { // do work } catch (OptimisticLockingFailureException ex) { // I'm interested in this }
|
如果Spring的數(shù)據(jù)訪問異常是checked的,我們需要編寫如下的代碼。注意我們還是可以選擇這么寫:
java代碼: |
try { // do work } catch (OptimisticLockingFailureException ex) { // I'm interested in this } catch (DataAccessException ex) { // Fatal; just rethrow it }
|
第一個例子的潛在缺陷是——編譯器不能強(qiáng)制處理可能的可恢復(fù)的異常——這對于第二個也是如此。因為我們被強(qiáng)制捕捉base exception(DataAccessException),編譯器不會強(qiáng)制對子類(OptimisticLockingFailureException)的檢查。因而編譯器可能強(qiáng)制我們編寫處理不可恢復(fù)問題的代碼,但是對于強(qiáng)制我們處理可恢復(fù)的問題并未有任何幫助。
Spring對于數(shù)據(jù)訪問異常的unchecked使用和許多——可能是大多數(shù)——成功的持久化框架是一致的。(確實,它部分是受到JDO的影響。)JDBC是少數(shù)幾個使用checked exception的數(shù)據(jù)訪問API之一。例如TopLink和JDO大量使用unchecked exception。Gavin King現(xiàn)在相信Hibernate也應(yīng)該選擇使用unchecked exception。
Spring的JDBC能夠用以下辦法幫助你:
你決不需要在使用JDBC時再編寫finally block。
總的來說你需要編寫的代碼更少了
你再也不需要挖掘你的RDBMS的文檔以找出它為錯誤的列名稱返回的某個罕見的錯誤代碼。你的程序不再依賴于RDBMS特有的錯誤處理代碼。
無論使用的是什么持久化技術(shù),你都會發(fā)現(xiàn)容易實現(xiàn)DAO模式,讓業(yè)務(wù)代碼無需依賴于任何特定的數(shù)據(jù)訪問API。
在實踐中,我們發(fā)現(xiàn)所有這些都確實有助于生產(chǎn)力的提高和更少的bug。我過去常常厭惡編寫JDBC代碼;現(xiàn)在我發(fā)現(xiàn)我能夠集中精力于我要執(zhí)行的SQL,而不是煩雜的JDBC資源管理。
如果需要的話Spring的JDBC抽象可以獨立使用——不強(qiáng)迫你把它們用作Spring的一部分。
O/R mapping 集成
當(dāng)然你經(jīng)常需要使用O/R mapping,而不是使用關(guān)系數(shù)據(jù)訪問。你總體的應(yīng)用程序框架也必須支持它。因而提供了對Hibernate 2.x和JDO的集成支持。它的數(shù)據(jù)訪問架構(gòu)使得它能和任何底層的數(shù)據(jù)訪問技術(shù)集成。Spring和Hibernate集成得尤其好。
為什么你要使用Hibernate加Spring,而不是直接使用Hibernate?
Session 管理 Spring提供有效率的,簡單的以并且是安全的處理Hibernate Session。使用Hibernate的相關(guān)代碼為了效率和恰當(dāng)?shù)氖聞?wù)處理一般需要使用相同的Hibernate “Session”對象。Spring讓它容易透明地創(chuàng)建和綁定Session到當(dāng)前的線程,要么使用聲明式,AOP的method interceptor方法,要么在Java代碼層面使用顯式的,“template”包裝類。因而Spring解決了在Hibernate論壇上經(jīng)常出現(xiàn)的用法問題。
資源管理 Spring的應(yīng)用程序context能夠處理Hiberante SessionFactories的位置和配置,JDBC數(shù)據(jù)源和其他相關(guān)資源。這使得這些值易于管理和改變。
集成的事務(wù)管理 Spring讓你能夠把你的Hibernate代碼包裝起來,要么使用聲明式,AOP風(fēng)格的method interceptor,要么在Java代碼層面顯式使用“template”包裝類。在兩種做法中,事務(wù)語義都為你處理了,并且在異常時也做好了恰當(dāng)?shù)氖聞?wù)處理(回滾,等)。如下面討論的,你還獲得了能夠使用和替換不同transaction manager,而不會讓你相關(guān)Hibernate代碼受到影響的能力。額外的,JDBC相關(guān)的代碼能夠完全事務(wù)性的和Hibernate代碼集成。這對于處理沒有在Hibernate實現(xiàn)的功能很有用。
如上描述的異常包裝 Spring能夠包裝Hibernate異常,把它們從私有的,checked異常轉(zhuǎn)換為一套抽象的運行時異常。這使得你能夠僅僅在恰當(dāng)?shù)膶用嫣幚泶蟛糠植豢苫謴?fù)的持久化異常,而不影響樣板catch/throw,和異常聲明。你仍然能夠在任何你需要的地方捕捉和處理異常。記住JDBC異常(包括DB特有的方言)也被轉(zhuǎn)換到相同的層次中,意味著你能在一致的編程模型中對JDBC執(zhí)行相同的操作。
為了避免和廠商綁定 Hibernate是強(qiáng)大的,靈活的,開放源代碼并且免費,但是它仍然使用私有的API。給出了一些選擇,使用標(biāo)準(zhǔn)或者抽象API實現(xiàn)主要的程序功能通常是你想要的,當(dāng)你需要因為功能,性能,或者其他考慮要轉(zhuǎn)換到使用其他實現(xiàn)時。
讓測試變簡單 Spring的Inversion of Control方法使得改變Hibernate的session factories,數(shù)據(jù)源,transaction manager的實現(xiàn)和位置很容易,如果需要的話還能改變mapper object的實現(xiàn)。這使得更加容易分離和測試持久化相關(guān)的代碼。
事務(wù)管理
抽象出一個數(shù)據(jù)訪問的API是不夠的;我們還需要考慮事務(wù)管理。JTA是顯而易見的選擇,但是它是一個直接用起來很笨重的API,因而許多J2EE開發(fā)者感到EJB CMT是對于事務(wù)管理唯一合理的選擇。
Spring提供了它自己對事務(wù)管理的抽象。Spring提供了這些:
通過類似于JdbcTemplate的回調(diào)模板編程管理事務(wù),比起直接使用JTA要容易多了
類似于EJB CMT的聲明式事務(wù)管理,但是不需要EJB容器
Spring的事務(wù)抽象式唯一的,它不綁定到JTA或者任何其他事務(wù)管理技術(shù)。Spring使用事務(wù)策略的概念把程序代碼和底層的事務(wù)架構(gòu)(例如JDBC)解藕。
為什么你要關(guān)心這些?JTA不是所有事務(wù)管理的最好答案嗎?如果你正在編寫僅僅使用一個數(shù)據(jù)庫的程序,你不需要JTA的復(fù)雜度。你不關(guān)心XA事務(wù)或者兩階段提交。你甚至不需要提供這些東西的高端應(yīng)用服務(wù)器。但是另一方面,你不會希望在需要和多個數(shù)據(jù)源打交道的時候重寫你的代碼。
假定你決定通過直接使用JDBC或者Hibernate的事務(wù)以避免JTA帶來的額外負(fù)擔(dān)。一旦你需要處理多個數(shù)據(jù)源,你必須剝開所有的事務(wù)管理代碼并且使用JTA事務(wù)來替代。這不是非常有吸引力的并且導(dǎo)致大部分J2EE程序員,包括我自己,推薦只使用全局JTA事務(wù)。然而使用Spring事務(wù)抽象,你只需要重新配置Spring讓它使用JTA,而不是JDBC或者Hibernate的事務(wù)策略,就一切OK了。這是一個配置上的改變,而不是代碼的改動。因而,Spring使得你能夠自由縮放應(yīng)用。
AOP
最近在應(yīng)用AOP來解決企業(yè)關(guān)注點方面大家有了很大的興趣,例如事務(wù)管理,這些都是EJB所要解決的。
Spring的AOP支持的首要目標(biāo)是要給POJOs提供J2EE服務(wù)。這類似于JBoss 4的目標(biāo),Spring AOP由它能夠在應(yīng)用服務(wù)器之間移植的優(yōu)勢,因而沒有綁死在廠商身上的風(fēng)險。它既可以在web或者EJB容器中使用,也能夠在WebLogic,Tomcat,JBoss,Resin,Jetty,Orion和許多其他應(yīng)用服務(wù)器和web容器上使用。
Spring AOP支持method interception。所支持關(guān)鍵的AOP概念包括:
Interception:自定義行為能夠在對接口和類的調(diào)用之前和之后插入。這類似于AspectJ術(shù)語中類似的“around advice”。
Introduction:指定advice會導(dǎo)致對象實現(xiàn)額外的接口。這混亂了繼承。
靜態(tài)和動態(tài)的pointcuts:在interception發(fā)生的程序執(zhí)行處指定points。靜態(tài)pointcuts concern函數(shù)簽名;動態(tài)pointcuts也可以在point被求值的地方考慮函數(shù)的參數(shù)。Pointcuts獨立interceptors單獨定義,使得標(biāo)準(zhǔn)interceptor可以應(yīng)用于不同應(yīng)用程序和代碼上下文。
Spring既支持有狀態(tài)(一個advised對象一個實例)也支持無狀態(tài)的interceptors(所有advice使用一個實例)。
Spring不支持field interception。這是一個經(jīng)過深思熟慮的設(shè)計決定。我總是感覺field interception違反了封裝。我比較傾向于把AOP作為補(bǔ)全物,而不是與OOP沖突的東西。如果在5年或者10年后,我們在AOP學(xué)習(xí)曲線上走得更遠(yuǎn)了并且覺得應(yīng)該在程序設(shè)計的桌面上給AOP一個位置,我不會驚訝的。(然而在那個時候基于語言的解決方案例如AspectJ可能比它們今天看來更加具有吸引力。)
Spring使用動態(tài)代理實現(xiàn)AOP(其中存在一個接口)或者在運行時使用CGLIB生成字節(jié)碼(這使得能夠代理類)。兩種方法都能夠在任何應(yīng)用服務(wù)器中使用。
Spring是第一個實現(xiàn)AOP Alliance interfaces的AOP 框架(www.sourceforge.net/projects/aopalliance)。這些是定義在不同AOP框架中能夠互操作interceptors的嘗試。
在TheServerSide和其他地方有一個正在進(jìn)行但是不是那么引人注目的爭論,就是這種interception是不是“true AOP”。我倒不怎么在意它叫什么;僅僅需要知道它是否在實踐中有用就好了。我也樂于稱它為“declarative middleware”(聲明式中間件)。把Spring AOP認(rèn)做簡單,輕量級的無狀態(tài)beans的替代物,這樣就不需要monolithic EJB容器了,而這些僅僅是讓你能夠構(gòu)建有你需要的服務(wù)的容器。我不推薦advising任何一個POJO,對local SLSBs的類比有助于你理解推薦的粒度。(然而,與EJB不同的是,在恰當(dāng)?shù)巧僖姷那闆r下,你可以自由地把Spring的AOP應(yīng)用到粒度更好的對象上。)
因為Spring在實例上advises 對象,而不是在class loader層面上,使用有不同advice的同一個類的多個實例是可能的,或者與advised實例一道使用unadvised 實例。
可能Spring AOP最常見的應(yīng)用是聲明式事務(wù)管理。這是基于前面描述的TansactionTemplate抽象上的,并且可以給任何POJO提供聲明式事務(wù)管理。取決于事務(wù)策略,底層的機(jī)制可以是JTA,JDBC,Hibernate或者任何其他提供事務(wù)管理的API。
Spring的聲明式事務(wù)管理類似于EJB CMT,在以下方面有些不同:
事務(wù)管理能夠應(yīng)用于任何POJO。我們推薦業(yè)務(wù)對象實現(xiàn)接口,但是這只是一個好的編程習(xí)慣的問題,而不是由框架強(qiáng)制的。
通過使用Spring的事務(wù)API能夠在事務(wù)性POJO中實現(xiàn)編程回調(diào)。我們?yōu)榇颂峁╈o態(tài)的方法,使用ThreadLoacal變量,因而你不需要傳播諸如EJBContext這樣的context對象來確保回滾。
你可以聲明式地定義“回滾規(guī)則”。EJB不會在未捕捉程序異常的時候自動回滾(僅僅在unchecked exceptions和其他Throwables的時候),應(yīng)用程序開發(fā)者經(jīng)常需要在任何異常發(fā)生時回滾。Spring事務(wù)管理讓你能夠聲明式地指定什么異常什么子類能夠?qū)е伦詣踊貪L。缺省的行為和EJB是一致的,但是你能夠在checked和unchecked異常時自動回滾。這個在最少化自編程回調(diào)代碼方面有很大好處,而回調(diào)依賴于Spring的事務(wù)API(因為EJB的編程回調(diào)時在EJBContext中完成的)。
事務(wù)管理不綁定于JTA。如前面解釋過的,Spring的事務(wù)管理能夠在不同事務(wù)策略中使用。
當(dāng)然還可以使用Spring AOP實現(xiàn)程序特有的aspects。取決于你對AOP概念的接受程度,決定你是否選擇這么做,而不是Spring的能力,但是它確實非常有用。我們所見過的成功例子包括:
自定義的security interception,當(dāng)安全檢查的復(fù)雜度超出了J2EE安全架構(gòu)的能力的時候
在開發(fā)中使用的調(diào)試和profiling aspects
發(fā)送email通知管理員用戶不尋常的舉動的Interceptors
程序自定的aspects能夠成為消除需要許多函數(shù)的樣板代碼的有利武器。
Spring AOP透明地與Spring BeanFactory概念集成。包含一個來自Spring BeanFactory對象地代碼不需要知道它是還是不是advised。和任何對象一樣,契約實在接口和對象實現(xiàn)中定義的。
下面的XML片斷展示了如何定義一個AOP代理:
java代碼: |
<bean id="myTest" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyInterfaces"> <value>org.springframework.beans.ITestBean</value> </property> <property name="interceptorNames"> <list> <value>txInterceptor</value> <value>target</value> </list> </property> </bean>
|
注意bean類的定義總是AOP框架的ProxyFactoryBean,雖然bean的類型在引用中使用或者由BeanFactory的getBean()方法返回時依賴的是代理接口。(多個代理方法是被支持的。)ProxyFactoryBean的“interceptorNames”屬性需要一個字符串列表。(因為如果代理是一個“prototype”而不是singleton,有狀態(tài)interceptors可能需要創(chuàng)建新的實例,所以必須使用Bean的名字而不是bean的引用。)列表中的名字可以是interceptor或者pointcuts(interceptors和有關(guān)它們合適被使用的信息)。列表中的“target”值自動創(chuàng)建一個“invoker interceptor”封裝target對象。實現(xiàn)代理接口的是在factory中的bean的名字。這個例子中的myTest可以和其他bean factory中的bean一樣使用。例如,其他對象可以使用<ref>元素引用它而且這些引用是由Spring IoC設(shè)置的。
還可以不用BeanFactory,編程構(gòu)建AOP代理,雖然這個很少用得上:
java代碼: |
TestBean target = new TestBean(); DebugInterceptor di = new DebugInterceptor(); MyInterceptor mi = new MyInterceptor(); ProxyFactory factory = new ProxyFactory(target); factory.addInterceptor(0, di); factory.addInterceptor(1, mi); // An "invoker interceptor" is automatically added to wrap the target ITestBean tb = (ITestBean) factory.getProxy();
|
我們相信最好把程序裝配從Java代碼中移出來,而AOP也不例外。
Spring在它的AOP能力方面的直接競爭者是Jon Tirsen的Nanning Aspects(http://nanning.codehaus.org)。
我覺得AOP作為EJB的替代無提供企業(yè)服務(wù)這個用法方面的進(jìn)步是重要的。隨著時間,這將成為Spring很重要的關(guān)注點。 MVC web 框架
Spring包括一個強(qiáng)大而且高度可配置的MVC web 框架。
Spring的MVC model類似于Struts。在多線程服務(wù)對象這點上,Spring的Controller類似于Struts Action,只有一個實例處理所有客戶的請求。然而,我們相信Spring的MVC比起Struts有很多優(yōu)點,例如:
Spring在controllers,JavaBean,models和views提供了一個非常清晰的劃分。
Spring的MVC是非常靈活的。不像Struts,它強(qiáng)制你的Action和Form對象進(jìn)入固化的層次之中(因而你迫使你使用Java的實體繼承),Spring MVC完全是基于接口的。而且,通過插入你自己的接口幾乎Spring MVC 框架的所有部分都是可配置的。當(dāng)然我們也提供了方便的類作為實現(xiàn)選擇。
Spring MVC是真正的view無關(guān)的。你不會被強(qiáng)制使用JSP,如果你不想那么做的話。你可以使用Velocity,XSLT或其他view技術(shù)。如果你想要使用自定義的view機(jī)制——例如,你自己的模板語言——你可以簡單實現(xiàn)Spring的View接口并且把它集成進(jìn)來。
和其他對象一樣,Spring的Controllers是通過IoC配置的。著使得它們易于測試,并且完美地和其他由Spring管理的對象集成。
Web層變成了業(yè)務(wù)對象層之上的薄薄一層。這鼓勵了好的習(xí)慣。Struts和其他專門的web框架讓你去實現(xiàn)你自己的業(yè)務(wù)對象;Spring提供了你應(yīng)用程序所有層的集成。
如在Struts 1.1中所見的,你可以有和你在Spring MVC 應(yīng)用程序中所需要的一樣多的dispatcher servlets。
下面的例子展示了一個簡單的Spring Controller如何能夠訪問定義在應(yīng)用程序context中的業(yè)務(wù)對象。這個controller在它的handleRequest()方法中執(zhí)行了Google搜索:
java代碼: |
public class GoogleSearchController implements Controller {
private IGoogleSearchPort google;
private String googleKey;
public void setGoogle(IGoogleSearchPort google) { this.google = google; }
public void setGoogleKey(String googleKey) { this.googleKey = googleKey; }
public ModelAndView handleRequest( HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String query = request.getParameter("query"); GoogleSearchResult result = // Google property definitions omitted...
// Use google business object google.doGoogleSearch(this.googleKey, query, start, maxResults, filter, restrict, safeSearch, lr, ie, oe);
return new ModelAndView("googleResults", "result", result); } }
|
這段代碼使用的prototype中,IGoogleSearchPort是一個GLUE web services代理,由Spring FActoryBean返回。然而,Spring把controller從底層web service庫中分離出來。接口可以使用普通的Java對象,test stub,mock對象或者如下面要討論的EJB代理實現(xiàn)。這個contorller不包括資源查找;除了支持它的web交互的必要代碼之外沒有別的什么了。
Spring還提供了數(shù)據(jù)綁定,forms,wizards和更復(fù)雜的工作流的支持。
對Spring MVC 框架的優(yōu)秀簡介是Thomas Risberg的Spring MVC 教程(http://www.springframework.org/docs/MVC-step-by-step/Spring-MVC-step-by-step.html)。還可以參見“Web MVC with the Spring Framework”(http://www.springframework.org/docs/web_mvc.html)。
如果你樂于使用你鐘情的MVC框架,Spring的分層架構(gòu)使得你能夠使用Spring的其他部分而不用MVC層。我們有使用Spring做中間層管理和數(shù)據(jù)訪問,但是在web層使用Struts,WebWork或者Tapestry的用戶。
實現(xiàn)EJB
如果你選擇使用EJB,Spring能在EJB實現(xiàn)和客戶端訪問EJB兩方面都提供很大的好處。
對業(yè)務(wù)邏輯進(jìn)行重構(gòu),把它從EJB facades中取出到POJO已經(jīng)得到了廣泛的認(rèn)同。(不講別的,這使得業(yè)務(wù)邏輯更容易單元測試,因為EJB嚴(yán)重依賴于容器而難于分離測試。)Spring為session bean和message driver bean提供了方便的超類,使得通過自動載入基于包含在EJB Jar文件中的XML文檔BeanFactory讓這變得很容易。
這意味著stateless session EJB可以這么獲得和使用所需對象:
java代碼: |
import org.springframework.ejb.support.AbstractStatelessSessionBean;
public class MyEJB extends AbstractStatelessSessionBean implements MyBusinessInterface { private MyPOJO myPOJO;
protected void onEjbCreate() { this.myPOJO = getBeanFactory().getBean("myPOJO"); }
public void myBusinessMethod() { this.myPOJO.invokeMethod(); } }
|
假定MyPOJO是一個接口,它的實現(xiàn)類——以及任何它需要的配置,注入基本的屬性和更多的合作者——在XML bean factory 定義中隱藏。
我們通過在ejb-jar.xmldeployment descriptor中名為ejb/BeanFactoryPath的環(huán)境變量定義告訴Spring去哪兒裝載XML文檔。如下:
java代碼: |
<session> <ejb-name>myComponent</ejb-name> <local-home>com.test.ejb.myEjbBeanLocalHome</local-home> <local>com.mycom.MyComponentLocal</local> <ejb-class>com.mycom.MyComponentEJB</ejb-class> <session-type>Stateless</session-type> <transaction-type>Container</transaction-type>
<env-entry> <env-entry-name>ejb/BeanFactoryPath</env-entry-name> <env-entry-type>java.lang.String</env-entry-type> <env-entry-value>/myComponent-ejb-beans.xml</env-entry-value></env-entry> </env-entry> </session>
|
myComponent-ejb-beans.xml 文件將會從classpath裝載:在本例中,是EJB Jar文件的根目錄。每個EJB都能指定自己的XML文檔,因而這個機(jī)制能在每個EJB Jar文件中使用多次。
Spring 的超類實現(xiàn)了EJB中諸如setSessionContext()和ejbCreate()的生命周期管理的方法,讓應(yīng)用程序開發(fā)者只需選擇是否實現(xiàn)Spring的onEjbCreate()方法。
使用EJB
Spring還讓實現(xiàn)EJB變得更加容易。許多EJB程序使用Service Locator和Business Delegate模式。這些比在客戶代碼中遍布JNDI查找強(qiáng)多了,但是它們常見的實現(xiàn)方式有顯著的缺點,例如:
使用EJB的典型代碼依賴Service Locator或者Business Delegate singletons,使得測試難于進(jìn)行。
在Service Locator模式?jīng)]有使用Business Delegate的情況下,程序代碼還要在EJB home中調(diào)用create()方法,并且處理可能導(dǎo)致的異常。因而仍然綁定在EJB API身上,忍受著EJB 編程模型的復(fù)雜度。
實現(xiàn)Business Delegate模式通常導(dǎo)致顯著的代碼重復(fù),其中我們必須編寫大量僅僅是調(diào)用EJB同等方法的方法。
基于這些和其他原因,傳統(tǒng)的EJB訪問,如在Sun Adventure Builder和OTN J2EE Virtual Shopping Mall中展示的那樣,會降低生產(chǎn)率并且?guī)盹@著的復(fù)雜度。
Spring通過引入codeless business delegate前進(jìn)了一步。有了Spring,你不再需要再編寫另一個Service Locator,另一個JNDI查找,或者在硬編碼的Business Delegate中重復(fù)代碼,除非你肯定這增加了價值。
例如,假定我們有使用local EJB的web controller。我們將遵循最佳實踐,使用EJB Business Methods Interface模式,EJB的local interface extend非EJB專有的業(yè)務(wù)方法接口。(這么做的主要的一個原因是確保在本地接口和bean實現(xiàn)類中方法簽名的自動同步。)讓我們調(diào)用這個業(yè)務(wù)方法接口MyComponent。當(dāng)然我們還需要實現(xiàn)local home接口并且提供實現(xiàn)SessionBean和MyComponent業(yè)務(wù)方法的bean的實現(xiàn)類。
用了Spring EJB 訪問,我們把我們的web層controller和EJB實現(xiàn)掛接上所需要進(jìn)行的Java編碼僅僅是在我們的controller中暴露一個類型MyComponent的setter方法。這將如下保存作為實例變量的引用:
java代碼: |
private MyComponent myComponent;
public void setMyComponent(MyComponent myComponent) { this.myComponent = myComponent; }
|
我們隨后在任何業(yè)務(wù)方法中使用這個實例變量。
Spring自動完稱剩下的工作,通過像這樣的XML bean定義。LocalStatelessSessionProxyFactoryBean是一個可以用于任何EJB的通用factory bean。它創(chuàng)建的對象能夠自動被Spring轉(zhuǎn)型為MyComponent類型。
java代碼: |
<bean id="myComponent" class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
<property name="jndiName"><value>myComponent</value></property> <property name="businessInterface"><value>com.mycom.MyComponent</value></property> </bean>
<bean id="myController" class = "com.mycom.myController" > <property name="myComponent"><ref bean="myComponent"/></property> </bean>
|
在幕后有許多魔法般的事情發(fā)生,Spring AOP framework的殷勤,雖然不強(qiáng)迫你使用AOP的概念享受這些結(jié)果。“myComponent”bean定義為EJB創(chuàng)建一個代理,它實現(xiàn)了業(yè)務(wù)方法的接口。EJB local home在啟動的時候被緩存,因而只需要一次JNDI查找。每次EJB被調(diào)用的時候,代理調(diào)用local EJB中的create()方法并且調(diào)用EJB中對應(yīng)的業(yè)務(wù)方法。
myController bean定義為這個代理設(shè)置controller類的myController屬性。
這個EJB訪問機(jī)制極大簡化了應(yīng)用程序的代碼:
Web層的代碼不依賴于EJB的使用。如果你想要使用POJO,mock object或者其他test stub替代EJB引用,我們可以簡單地改動一下myComponent bean定義而不影響一行Java代碼
我們還不需要寫一行JNDI查找或者其他EJB plumbing code。
在實際程序中的性能測試和經(jīng)驗標(biāo)明這種方法(包括對目標(biāo)EJB的反射調(diào)用)的性能影響是很小的,在典型的應(yīng)用中檢測不出。記住無論如何我們都不希望使用fine-grained的EJB調(diào)用,因為會有有關(guān)應(yīng)用服務(wù)器上的EJB的底層架構(gòu)方面的代價。
我們可以把相同方法應(yīng)用于遠(yuǎn)程EJB,通過類似org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean factory bean的方法。然而我們無法隱藏遠(yuǎn)程EJB的業(yè)務(wù)方法接口中的RemoteException。
測試
如你可能已經(jīng)注意到的,我和其他Spring開發(fā)這是全面單元測試重要性的堅定支持者。我們相信框架被徹底單元測試過的是非常重要的,而且我們框架設(shè)計的主要目標(biāo)是讓建立在框架之上的程序易于單元測試。
Spring自身有一個極好的單元測試包。我們的1.0 M1的單元測試覆蓋率是75%,而且我們希望在1.0 RC1的時候能夠達(dá)到80%的單元測試覆蓋率。我們發(fā)現(xiàn)在這個項目中測試優(yōu)先的開發(fā)帶來的好處是實實在在的。例如,它使得作為國際化分布式開發(fā)團(tuán)隊的工作極端有效率,而且用戶評論CVS snapshots趨向于穩(wěn)定和使用安全。
因為以下理由,我們相信用Spring構(gòu)建的應(yīng)用程序是非常易于測試的:
IoC推動了單元測試
應(yīng)用程序不包括直接使用注入JNDI的J2EE服務(wù)的plumbing code,這些代碼一般讓測試難于進(jìn)行
Spring bean factories和contexts能夠在容器外設(shè)置
在容器外可以設(shè)置Spring bean factory的能力提供了對開發(fā)過程有趣的可選項。在幾個使用Spring的web應(yīng)用中,工作是從定義業(yè)務(wù)接口和在web容器外集成測試開始的。在業(yè)務(wù)功能已經(jīng)足夠完整之后,web接口不過是添加在其上的薄薄一層。
誰在使用Spring
雖然相對來說Spring還是一個新的項目,但是我們已經(jīng)有了一個令人印象深刻并且不斷增長的用戶群。它們已經(jīng)有許多產(chǎn)品使用著Spring。用戶包括一個主要的全球投資銀行(做大型項目的),一些知名的網(wǎng)絡(luò)公司,幾個web開發(fā)顧問機(jī)構(gòu),衛(wèi)生保健公司,以及學(xué)院機(jī)構(gòu)。
許多用戶完整地使用Spring,但是一些只單獨使用一些組件。例如,大量用戶使用我們地JDBC或者其他數(shù)據(jù)訪問功能。
Roadmap
在今年晚些時候我們主要要做的是讓Spring發(fā)布release 1.0。然而,我們還有一些更長遠(yuǎn)的目標(biāo)。
為1.0 final規(guī)劃地主要改進(jìn)式源代碼級地元數(shù)據(jù)支持,它主要用于(但不局限于)AOP框架。這將使得C#風(fēng)格的attribute驅(qū)動的事務(wù)管理,并且讓聲明式企業(yè)服務(wù)在典型應(yīng)用情況下非常容易配置。Attribute支持將會在Spring的1.0 final release支持中加入,并且設(shè)計的是在發(fā)布的那個時候能與JSR-175集成。
1.0之后,一些可能的改進(jìn)地方包括:
通過對我們的JDBC和事務(wù)支持的一個相當(dāng)抽象來支持JMS
支持bean factories的動態(tài)重配置
提供web services的能力
IDE和其他工具支持
作為一個敏捷項目,我們主要是受到用戶需求的驅(qū)動。因而我們不會開發(fā)沒有一個用戶需要的特性,并且我們會仔細(xì)傾聽來自用戶群的聲音。
總結(jié)
Spring是一個解決了許多在J2EE開發(fā)中常見的問題的強(qiáng)大框架。
Spring提供了管理業(yè)務(wù)對象的一致方法并且鼓勵了注入對接口編程而不是對類編程的良好習(xí)慣。Spring的架構(gòu)基礎(chǔ)是基于使用JavaBean屬性的Inversion of Control容器。然而,這僅僅是完整圖景中的一部分:Spring在使用IoC容器作為構(gòu)建完關(guān)注所有架構(gòu)層的完整解決方案方面是獨一無二的。
Spring提供了唯一的數(shù)據(jù)訪問抽象,包括簡單和有效率的JDBC框架,極大的改進(jìn)了效率并且減少了可能的錯誤。Spring的數(shù)據(jù)訪問架構(gòu)還集成了Hibernate和其他O/R mapping解決方案。
Spring還提供了唯一的事務(wù)管理抽象,它能夠在各種底層事務(wù)管理技術(shù),例如JTA或者JDBC紙上提供一個一致的編程模型。
Spring提供了一個用標(biāo)準(zhǔn)Java語言編寫的AOP框架,它給POJOs提供了聲明式的事務(wù)管理和其他企業(yè)事務(wù)——如果你需要——還能實現(xiàn)你自己的aspects。這個框架足夠強(qiáng)大,使得應(yīng)用程序能夠拋開EJB的復(fù)雜性,同時享受著和傳統(tǒng)EJB相關(guān)的關(guān)鍵服務(wù)。
Spring還提供了可以和總體的IoC容器集成的強(qiáng)大而靈活的MVC web框架。
更多信息
參見以下資源獲得關(guān)于Spring的更多信息:
Expert One-on-One J2EE Design and Development(Rod Johnson,Wrox,2002)。雖然Spring在書出版之后已經(jīng)極大地進(jìn)步和改進(jìn)了,它仍然是理解Spring動機(jī)的極佳途徑。
Spring的主頁:http://www.springframework.org。這里包括Javadoc和幾個教程。
在Sourceforge上的論壇和下載
Spring用戶和Spring開發(fā)者的郵件列表
我們正在盡我們可能去改進(jìn)Spring的文檔和示例。我們還為在信件和郵件列表中極好的回復(fù)率自豪。我們希望你能快速融入我們的社區(qū)!
關(guān)于作者
Rod Johnson 作為Java開發(fā)者和架構(gòu)師已經(jīng)有了7年的經(jīng)驗了并且在J2EE平臺出現(xiàn)之初就在其上進(jìn)行開發(fā)了。他是《Expert One-on-One J2EE Design and Development》(Wrox,2002)的作者并且貢獻(xiàn)了其他好幾本關(guān)于J2EE的書。他當(dāng)前正在為Wiley撰寫另外一本有關(guān)J2EE架構(gòu)的書。Rod在兩個Java標(biāo)準(zhǔn)委員會服務(wù)并且經(jīng)常師大會發(fā)言人。現(xiàn)在他在UK做一個咨詢顧問。