約兩年前,我試了試Spring Project,這個(gè)被Hibernate站點(diǎn)顯著提到的東西。感覺就像那舊艇,十分適合我。為企業(yè)應(yīng)用核心部分的發(fā)展,Spring深深地融入了我的編程當(dāng)中,所以我將其作為我的第4本java書 Spring:A Developer’s??Notebook的主題。在這篇文章中我會(huì)告訴你原因。
1.Spring提供更好的平衡
在河中,我學(xué)會(huì)更多地利用我的腰部和背部的肌肉來劃船,因?yàn)槲业氖直奂∪鉄o法堅(jiān)持整天劃船。我變得更有效率,更平衡地利用自己的肌肉。通過spring,我可以在每行代碼中做更多的事。通過spring你會(huì)發(fā)現(xiàn)更多其優(yōu)勢(shì),其中最重要的是在對(duì)象持久化上。這是一個(gè)來自hibernate訪問數(shù)據(jù)對(duì)象的函數(shù)。
public List getReservations( ) {
??return getHibernateTemplate( ).find("from Reservation");
}
注意那些你沒看到的東西。這里沒有任何事務(wù)處理。Spring允許你建立配置代碼去處理它。你不一定要通過關(guān)閉session來管理資源。你不一定寫你自己的配置。你不一定在這個(gè)層次上管理異常,因?yàn)檫@些異常是未經(jīng)檢查的。你可以自由地在最適當(dāng)?shù)奈恢萌ス芾硭麄?。沒用spring的hibernate方法的代碼會(huì)是這樣的:
public List getBikesOldWay( ) throws Exception {
??List bikes = null;
??Session s = null;
??try {
????s = mySessionFactory.openSession( );
????bikes = s.find("from Bike");
??}catch (Exception ex) {
????//handle exception gracefully
??}finally {
????s.close( );
??}
??return bikes;
}
Spring給我更多優(yōu)勢(shì),讓我編程更快,更易維護(hù)程序。
2.Spring支持POJO編程
在EJB 2.x徹底失敗之后,我們都在尋找更多方式避免在每個(gè)bean中加入笨重的模型去表達(dá)企業(yè)服務(wù)。當(dāng)然。我們需要事務(wù),安全,持久化,有時(shí)還需要遠(yuǎn)程調(diào)用。用EJB時(shí),我不得不去學(xué)龐大的API以及通過新的工具和部署過程來工作。結(jié)果我變成容器(container)提供的服務(wù)的奴隸。而在用Spring時(shí),我可以選擇我自己的服務(wù)和持久化框架。我在POJOs上編程并通過配置文件添加企業(yè)服務(wù)。
在Sping:A Developer’s notebook這本書中,我建立了一個(gè)RentaBike的程序。我用我的POJOhibRentaBike取代了session bean 或者entity bean,它充當(dāng)了我的數(shù)據(jù)訪問對(duì)象。我還在別處添加了服務(wù)。Spring配置文件是一個(gè)XML文件,被稱為上下文。它含有在容器中的所有bean以及這些bean的屬性,還有這些bean需要的服務(wù)。讓我們來看看下面的例子。
Target:
??
????Bruce's Bikes
??
??
????
??
??
????
??
Interceptor:
????class="org.springframework.transaction.interceptor.TransactionInterceptor">
??
????
??
??
????
??????com.springbook.RentABike.transferReservation=
??????PROPAGATION_REQUIRED,-ReservationTransferException
??????com.springbook.RentABike.save*=PROPAGATION_REQUIRED
??????com.springbook.RentABike.*=PROPAGATION_REQUIRED,readOnly
????
??
proxy:
??
????com.springbook.RentABike
??
??
????transactionInterceptor,rentaBikeTarget
??
注意這3個(gè)不同的bean: The Proxy , The target, and The interceptors. The proxy將調(diào)用POJO,以及POJO需要的任何服務(wù)。Interceptors包含粘合各調(diào)用服務(wù)的代碼,他們也說明了如何去對(duì)待The target中的每個(gè)方法。所有需要訪問RantaBike的人調(diào)用The proxy,這個(gè)開始事務(wù)訪問The target(The POJO)的事務(wù)攔截器。Thet target做自己的事返回給事務(wù)攔截器(提交事務(wù)的對(duì)象),返回到proxy和proxy的調(diào)用者。

Figure 1. POJO programming in action
你在POJO外建立了你的程序,配置了它,Spring會(huì)隱藏其他的東西。我是一個(gè)POJO編程者。
3.依賴注入有助易測(cè)性
Spring通過叫依賴注入(Dependency Injection)的設(shè)計(jì)模式來提高你的易測(cè)性。當(dāng)一個(gè)消費(fèi)者(consumer)依賴一個(gè)從屬物(我們會(huì)叫它一個(gè)服務(wù)),你會(huì)為consumer建立一個(gè)屬性。Spring將會(huì)建立這個(gè)consumer和服務(wù),以及設(shè)置這個(gè)consumer的屬性為服務(wù)的值。換種說法,Spring在上下文中管理beans的生命周期,解決依賴性。這是個(gè)不通過spring的依賴注入的例子。首先是消費(fèi)者(consumer),被作為程序的基本模樣。
public class CommandLineView {
??private RentABike rentaBike;
??public CommandLineView( ) {rentaBike = new ArrayListRentABike("Bruce's Bikes"); }
public void setRentABike(RentABike rentABike){
??this.rentABike = rentABike;
}
??public void printAllBikes( ) {
????System.out.println(rentaBike.toString( ));
????Iterator iter = rentaBike.getBikes().iterator( );
????while(iter.hasNext( )) {
??????Bike bike = (Bike)iter.next( );
??????System.out.println(bike.toString( ));
????}
??}
??public static final void main(String[] args) {
????CommandLineView clv = new CommandLineView( );
????clv.printAllBikes( );
??}
接著是service這個(gè)模型。這是個(gè)簡(jiǎn)單的通過數(shù)組表的實(shí)現(xiàn),其依賴于在這個(gè)模型(RentaBike)。
interface RentABike {
List getBikes( );
Bike getBike(String serialNo);
}
public class ArrayListRentABike implements RentABike {
?? private String storeName;
?? final List bikes = new ArrayList();
?? public ArrayListRentABike(String storeName) {
??????this.storeName = storeName;
??????bikes.add(new Bike("Shimano", "Roadmaster", 20, "11111", 15, "Fair"));
??????bikes.add(new Bike("Cannondale", "F2000 XTR", 18, "22222",12,"Excellent"));
??????bikes.add(new Bike("Trek","6000", 19, "33333", 12.4, "Fair"));
?? }
?? public String toString() { return "RentABike: " + storeName; }
?? public List getBikes() { return bikes; }
?? public Bike getBike(String serialNo) {
??????Iterator iter = bikes.iterator();
??????while(iter.hasNext()) {
???????? Bike bike = (Bike)iter.next();
???????? if(serialNo.equals(bike.getSerialNo())) return bike;
??????}
??????return null;
?? }
}
這是裝配程序。粗體的代碼就是依賴注入。裝配程序演示了服務(wù)和消費(fèi)者,通過設(shè)置rentaBike的屬性解決了依賴性。
public class RentABikeAssembler {
??public static final void main(String[] args) {
????CommandLineView clv = new CommandLineView( );
????RentABike rentaBike = new ArrayListRentABike("Bruce's Bikes");
????clv.setRentaBike(rentaBike);
????clv.printAllBikes( );
??}
}
當(dāng)然,Spring將最終符合裝配的法則。如果你將服務(wù)隱藏到接口程序中,那樣你將可以向容器注入接口程序(interface)的任何實(shí)現(xiàn)(implementation)。
依賴注入(Dependency injection)讓你編寫一個(gè)生產(chǎn)依賴和一個(gè)測(cè)試依賴。例如這個(gè)例子建立一個(gè)stub 對(duì)象,它讓測(cè)試這個(gè)程序更輕松。(要更多地了解stubs和mocks,請(qǐng)看“Mocks Aren’t Stubs.”).
你已經(jīng)看到RentaBike的Hibernate實(shí)現(xiàn),以及其數(shù)組表版本。我也許不想在完全Hibernates實(shí)現(xiàn)的代碼運(yùn)行我的所有用戶界面測(cè)試。而我更愿意簡(jiǎn)單地通過數(shù)組表實(shí)現(xiàn)接口。
依賴注入讓我聯(lián)合成品版(通過HibRentaBike),開發(fā)版(通過ArrayListRentaBike列表)和測(cè)試版(通過mock對(duì)象)。當(dāng)我用java編程的時(shí)候,我必須有依賴注入去取得這些mocks進(jìn)入那些難以進(jìn)入的位置。
4.控制轉(zhuǎn)入簡(jiǎn)化JDBC
JDBC程序是丑陋的,冗長(zhǎng)的和乏味的。一個(gè)好的抽象層可以改進(jìn)它,Spring讓你通過查詢語句和匿名的inner class來定制默認(rèn)JDBC方法來去除那大部分苦力工作。這是一個(gè)簡(jiǎn)單的JDBC的例子。
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));
??????}
??}
);
想想這個(gè)例子,查詢?nèi)缫粋€(gè)默認(rèn)JDBC方法的方法。Spring會(huì)為結(jié)果集中的每一行執(zhí)行在匿名inner class里的processRow方法的。你在上下文中設(shè)置了數(shù)據(jù)源。而不需要擔(dān)心開或者關(guān)的狀態(tài),或者連接,配置數(shù)據(jù)源,或者管理事務(wù)。你不需要說明一個(gè)外部的結(jié)果集,或者在更低的層次上管理異常,因?yàn)閟pring將你的SQLException折疊放入一個(gè)共同的未檢查的異常集。其他語言例如Ruby和smalltalk經(jīng)常通過代碼塊使用控制轉(zhuǎn)入,但是在java中不經(jīng)常使用。Spring簡(jiǎn)化了艱巨的任務(wù)。
5.Spring的社區(qū)興旺
雖然一些開源項(xiàng)目不需要變得相當(dāng)活躍而使其變得有用。例如Juit做已定目標(biāo)的工作。如果你喜歡編程模型的話,它有你需要的所有基本東西。而輕量級(jí)容器如Spring需要一個(gè)充滿活力的社區(qū)。Spring是你可以找到的最活躍的社區(qū)之一,你可以擁有許多好處。
服務(wù)(Service):通過Spring你可以找到數(shù)百種不同的服務(wù),從安全到系統(tǒng)管理,到工作流。在持久化上,你可以插入JDO,Hibernate,Top Link,JDBC或者OJB.
支持和教育(Support and education)::許多獨(dú)立顧問提供Spring服務(wù),你可以在全世界得到出色的培訓(xùn)。
增強(qiáng)(Enhancements):Spring一年放出數(shù)個(gè)主要的發(fā)行版。在框架內(nèi)的出色的測(cè)試和清晰的(Factored 擴(kuò)展)意味著每個(gè)發(fā)行版都是質(zhì)量?jī)?yōu)良的。Spring以及取得正在進(jìn)行中的Hbernate 3的支持,提供了一個(gè)全新的web流程框架。這所有都在最新的發(fā)行版中。
商業(yè)上支持(Commercial support):像我這樣的作者寫Spring的書。現(xiàn)在,你可以找到5本關(guān)于Spring的書,以及許多含有部分Spring內(nèi)容的書。許多產(chǎn)品商也支持Spring。許多開源框架例如Geronimo和Hibernate具有對(duì)Spring的特殊支持。
Spring社區(qū)讓人們用這個(gè)框架變得更加容易。我可以雇用Spring開發(fā)人員,培訓(xùn)他們。我可以閱讀一些書籍來補(bǔ)充我的知識(shí),取得一些幫助我做那些我需要做的所有事情。我沒有找到為另一個(gè)容器的社區(qū)來得如此親近。
資料:
如果你想讀得更多,則有許多你可以取得地方:
Martinfowler.com 有一些關(guān)于stubs and mocks 及 dependency injection.的文章。
這里有Spring的框架。
我的第一本書,Better, Faster, Lighter Java, 總結(jié)了輕量(lightweight)開發(fā)方法。
本文源于Spring: A Developer's Notebook 及其代碼。 這是一本程序員注解的書,因此請(qǐng)注意勘誤表,當(dāng)然,可以使用范例代碼簡(jiǎn)化工作。
還有這本O'Reilly出版的書:Hibernate: A Developer's Notebook.
作者簡(jiǎn)介:
Bruce A. Tate是劃艇和山地自行車愛好者,是兩個(gè)孩子的父親。在他的業(yè)余時(shí)間,他在得克薩斯州的奧斯丁當(dāng)獨(dú)立顧問。他是4本書的作者,其中包括暢銷的Bitter Java以及O’Reilly最近發(fā)行的Better,Faster,Lighter Java。