吳高峰 常德卷煙廠(chǎng)信息技術(shù)部 2005 年 2 月 大多數IT 組織都必須解決三個(gè)主要問(wèn)題:1.幫助組織減少成本 2.增加并且保持客戶(hù) 3.加快業(yè)務(wù)效率。完成這些問(wèn)題一般都需要實(shí)現對多個(gè)業(yè)務(wù)系統的數據和業(yè)務(wù)邏輯的無(wú)縫訪(fǎng)問(wèn),也就是說(shuō),要實(shí)施系統集成工程,以便聯(lián)結業(yè)務(wù)流程、實(shí)現數據的訪(fǎng)問(wèn)與共享。 JpetStore 4.0是ibatis的最新示例程序,基于Struts MVC框架(注:非傳統Struts開(kāi)發(fā)模式),以ibatis作為持久化層。該示例程序設計優(yōu)雅,層次清晰,可以學(xué)習以及作為一個(gè)高效率的編程模型參考。本文是在其基礎上,采用Spring對其中間層(業(yè)務(wù)層)進(jìn)行改造。使開(kāi)發(fā)量進(jìn)一步減少,同時(shí)又擁有了Spring的一些好處… 1. 前言 JpetStore 4.0是ibatis的最新示例程序。ibatis是開(kāi)源的持久層產(chǎn)品,包含SQL Maps 2.0 和 Data Access Objects 2.0 框架。JpetStore示例程序很好的展示了如何利用ibatis來(lái)開(kāi)發(fā)一個(gè)典型的J2EE web應用程序。JpetStore有如下特點(diǎn): - ibatis數據層
- POJO業(yè)務(wù)層
- POJO領(lǐng)域類(lèi)
- Struts MVC
- JSP 表示層
以下是本文用到的關(guān)鍵技術(shù)介紹,本文假設您已經(jīng)對Struts,SpringFramewok,ibatis有一定的了解,如果不是,請首先查閱附錄中的參考資料。 - Struts 是目前Java Web MVC框架中不爭的王者。經(jīng)過(guò)長(cháng)達五年的發(fā)展,Struts已經(jīng)逐漸成長(cháng)為一個(gè)穩定、成熟的框架,并且占有了MVC框架中最大的市場(chǎng)份額。但是Struts某些技術(shù)特性上已經(jīng)落后于新興的MVC框架。面對Spring MVC、Webwork2 這些設計更精密,擴展性更強的框架,Struts受到了前所未有的挑戰。但站在產(chǎn)品開(kāi)發(fā)的角度而言,Struts仍然是最穩妥的選擇。本文的原型例子JpetStore 4.0就是基于Struts開(kāi)發(fā)的,但是不拘泥于Struts的傳統固定用法,例如只用了一個(gè)自定義Action類(lèi),并且在form bean類(lèi)的定義上也是開(kāi)創(chuàng )性的,令人耳目一新,稍后將具體剖析一下。
- Spring Framework 實(shí)際上是Expert One-on-One J2EE Design and Development 一書(shū)中所闡述的設計思想的具體實(shí)現。Spring Framework的功能非常多。包含AOP、ORM、DAO、Context、Web、MVC等幾個(gè)部分組成。Web、MVC暫不用考慮,JpetStore 4.0用的是更成熟的Struts和JSP;DAO由于目前Hibernate、JDO、ibatis的流行,也不考慮,JpetStore 4.0用的就是ibatis。因此最需要用的是AOP、ORM、Context。Context中,最重要的是Beanfactory,它能將接口與實(shí)現分開(kāi),非常強大。目前AOP應用最成熟的還是在事務(wù)管理上。
- ibatis 是一個(gè)功能強大實(shí)用的SQL Map工具,不同于其他ORM工具(如hibernate),它是將SQL語(yǔ)句映射成Java對象,而對于ORM工具,它的SQL語(yǔ)句是根據映射定義生成的。ibatis 以SQL開(kāi)發(fā)的工作量和數據庫移植性上的讓步,為系統設計提供了更大的自由空間。有ibatis代碼生成的工具,可以根據DDL自動(dòng)生成ibatis代碼,能減少很多工作量。
2. JpetStore簡(jiǎn)述
2.1. 背景 最初是Sun公司的J2EE petstore,其最主要目的是用于學(xué)習J2EE,但是其缺點(diǎn)也很明顯,就是過(guò)度設計了。接著(zhù)Oracle用J2EE petstore來(lái)比較各應用服務(wù)器的性能。微軟推出了基于.Net平臺的 Pet shop,用于競爭J2EE petstore。而JpetStore則是經(jīng)過(guò)改良的基于struts的輕便框架J2EE web應用程序,相比來(lái)說(shuō),JpetStore設計和架構更優(yōu)良,各層定義清晰,使用了很多最佳實(shí)踐和模式,避免了很多"反模式",如使用存儲過(guò)程,在java代碼中嵌入SQL語(yǔ)句,把HTML存儲在數據庫中等等。最新版本是JpetStore 4.0。 2.2. JpetStore開(kāi)發(fā)運行環(huán)境的建立 1、開(kāi)發(fā)環(huán)境 - Java SDK 1.4.2
- Apache Tomcat 4.1.31
- Eclipse-SDK-3.0.1-win32
- HSQLDB 1.7.2
2、Eclipse插件 - EMF SDK 2.0.1:Eclipse建??蚣?,lomboz插件需要,可以使用runtime版本。
- lomboz 3.0:J2EE插件,用來(lái)在Eclipse中開(kāi)發(fā)J2EE應用程序
- Spring IDE 1.0.3:Spring Bean配置管理插件
- xmlbuddy_2.0.10:編輯XML,用免費版功能即可
- tomcatPluginV3:tomcat管理插件
- Properties Editor:編輯java的屬性文件,并可以預覽以及自動(dòng)存盤(pán)為Unicode格式。免去了手工或者ANT調用native2ascii的麻煩。
3、示例源程序 - ibatis示例程序JpetStore 4.0 http://www.ibatis.com/jpetstore/jpetstore.html
- 改造后的源程序(+spring)(源碼鏈接)
2.3. 架構
圖1 JpetStore架構圖
圖1 是JPetStore架構圖,更詳細的內容請參見(jiàn)JPetStore的白皮書(shū)。參照這個(gè)架構圖,讓我們稍微剖析一下源代碼,得出JpetStore 4.0的具體實(shí)現圖(見(jiàn)圖2),思路一下子就豁然開(kāi)朗了。前言中提到的非傳統的struts開(kāi)發(fā)模式,關(guān)鍵就在struts Action類(lèi)和form bean類(lèi)上。 struts Action類(lèi)只有一個(gè):BeanAction。沒(méi)錯,確實(shí)是一個(gè)!與傳統的struts編程方式很不同。再仔細研究BeanAction類(lèi),發(fā)現它其實(shí)是一個(gè)通用類(lèi),利用反射原理,根據URL來(lái)決定調用formbean的哪個(gè)方法。BeanAction大大簡(jiǎn)化了struts的編程模式,降低了對struts的依賴(lài)(與struts以及WEB容器有關(guān)的幾個(gè)類(lèi)都放在com.ibatis.struts包下,其它的類(lèi)都可以直接復用)。利用這種模式,我們會(huì )很容易的把它移植到新的框架如JSF,spring。 這樣重心就轉移到form bean上了,它已經(jīng)不是普通意義上的form bean了。查看源代碼,可以看到它不僅僅有數據和校驗/重置方法,而且已經(jīng)具有了行為,從這個(gè)意義上來(lái)說(shuō),它更像一個(gè)BO(Business Object)。這就是前文講到的,BeanAction類(lèi)利用反射原理,根據URL來(lái)決定調用form bean的哪個(gè)方法(行為)。form bean的這些方法的簽名很簡(jiǎn)單,例如:
public String myActionMethod() { //..work return "success"; }
|
方法的返回值直接就是字符串,對應的是forward的名稱(chēng),而不再是ActionForward對象,創(chuàng )建ActionForward對象的任務(wù)已經(jīng)由BeanAction類(lèi)代勞了。 另外,程序還提供了ActionContext工具類(lèi),該工具類(lèi)封裝了request 、response、form parameters、request attributes、session attributes和 application attributes中的數據存取操作,簡(jiǎn)單而線(xiàn)程安全,form bean類(lèi)使用該工具類(lèi)可以進(jìn)一步從表現層框架解耦。 在這里需要特別指出的是,BeanAction類(lèi)是對struts擴展的一個(gè)有益嘗試,雖然提供了非常好的應用開(kāi)發(fā)模式,但是它還非常新,一直在發(fā)展中。 圖2 JpetStore 4.0具體實(shí)現
2.4. 代碼剖析 下面就讓我們開(kāi)始進(jìn)一步分析JpetStore4.0的源代碼,為下面的改造鋪路。 - BeanAction.java是唯一一個(gè)Struts action類(lèi),位于com.ibatis.struts包下。正如上文所言,它是一個(gè)通用的控制類(lèi),利用反射機制,把控制轉移到form bean的某個(gè)方法來(lái)處理。詳細處理過(guò)程參考其源代碼,簡(jiǎn)單明晰。
Form bean類(lèi)位于com.ibatis.jpetstore.presentation包下,命名規則為***Bean。Form bean類(lèi)全部繼承于BaseBean類(lèi),而B(niǎo)aseBean類(lèi)實(shí)際繼承于A(yíng)ctionForm,因此,Form bean類(lèi)就是Struts的 ActionForm,Form bean類(lèi)的屬性數據就由struts框架自動(dòng)填充。而實(shí)際上,JpetStore4.0擴展了struts中ActionForm的應用: Form bean類(lèi)還具有行為,更像一個(gè)BO,其行為(方法)由BeanAction根據配置(struts-config.xml)的URL來(lái)調用。雖然如此,我們還是把Form bean類(lèi)定位于表現層。 Struts-config.xml的配置里有3種映射方式,來(lái)告訴BeanAction把控制轉到哪個(gè)form bean對象的哪個(gè)方法來(lái)處理。 以這個(gè)請求連接為例http://localhost/jpetstore4/shop/viewOrder.do 1. URL Pattern <action path="/shop/viewOrder" type="com.ibatis.struts.BeanAction" name="orderBean" scope="session" validate="false"> <forward name="success" path="/order/ViewOrder.jsp"/> </action>
|
此種方式表示,控制將被轉發(fā)到"orderBean"這個(gè)form bean對象 的"viewOrder"方法(行為)來(lái)處理。方法名取"path"參數的以"/"分隔的最后一部分。 2. Method Parameter <action path="/shop/viewOrder" type="com.ibatis.struts.BeanAction" name="orderBean" parameter="viewOrder" scope="session" validate="false"> <forward name="success" path="/order/ViewOrder.jsp"/> </action>
|
此種方式表示,控制將被轉發(fā)到"orderBean"這個(gè)form bean對象的"viewOrder"方法(行為)來(lái)處理。配置中的"parameter"參數表示form bean類(lèi)上的方法。"parameter"參數優(yōu)先于"path"參數。 3. No Method call <action path="/shop/viewOrder" type="com.ibatis.struts.BeanAction" name="orderBean" parameter="*" scope="session" validate="false"> <forward name="success" path="/order/ViewOrder.jsp"/> </action>
|
此種方式表示,form bean上沒(méi)有任何方法被調用。如果存在"name"屬性,則struts把表單參數等數據填充到form bean對象后,把控制轉發(fā)到"success"。否則,如果name為空,則直接轉發(fā)控制到"success"。 這就相當于struts內置的org.apache.struts.actions.ForwardAction的功能 <action path="/shop/viewOrder" type="org.apache.struts.actions.ForwardAction" parameter="/order/ViewOrder.jsp " scope="session" validate="false"> </action>
|
- Service類(lèi)位于com.ibatis.jpetstore.service包下,屬于業(yè)務(wù)層。這些類(lèi)封裝了業(yè)務(wù)以及相應的事務(wù)控制。Service類(lèi)由form bean類(lèi)來(lái)調用。
- com.ibatis.jpetstore.persistence.iface包下的類(lèi)是DAO接口,屬于業(yè)務(wù)層,其屏蔽了底層的數據庫操作,供具體的Service類(lèi)來(lái)調用。DaoConfig類(lèi)是工具類(lèi)(DAO工廠(chǎng)類(lèi)),Service類(lèi)通過(guò)DaoConfig類(lèi)來(lái)獲得相應的DAO接口,而不用關(guān)心底層的具體數據庫操作,實(shí)現了如圖2中{耦合2}的解耦。
- com.ibatis.jpetstore.persistence.sqlmapdao包下的類(lèi)是對應DAO接口的具體實(shí)現,在JpetStore4.0中采用了ibatis來(lái)實(shí)現ORM。這些實(shí)現類(lèi)繼承BaseSqlMapDao類(lèi),而B(niǎo)aseSqlMapDao類(lèi)則繼承ibatis DAO 框架中的SqlMapDaoTemplate類(lèi)。ibatis的配置文件存放在com.ibatis.jpetstore.persistence.sqlmapdao.sql目錄下。這些類(lèi)和配置文件位于數據層
- Domain類(lèi)位于com.ibatis.jpetstore.domain包下,是普通的javabean。在這里用作數據傳輸對象(DTO),貫穿視圖層、業(yè)務(wù)層和數據層,用于在不同層之間傳輸數據。
剩下的部分就比較簡(jiǎn)單了,請看具體的源代碼,非常清晰。 2.5. 需要改造的地方 JpetStore4.0的關(guān)鍵就在struts Action類(lèi)和form bean類(lèi)上,這也是其精華之一(雖然該實(shí)現方式是試驗性,待擴充和驗證),在此次改造中我們要保留下來(lái),即控制層一點(diǎn)不變,表現層獲取相應業(yè)務(wù)類(lèi)的方式變了(要加載spring環(huán)境),其它保持不變。要特別關(guān)注的改動(dòng)是業(yè)務(wù)層和持久層,幸運的是JpetStore4.0設計非常好,需要改動(dòng)的地方非常少,而且由模式可循,如下: 1. 業(yè)務(wù)層和數據層用Spring BeanFactory機制管理。 2. 業(yè)務(wù)層的事務(wù)由spring 的aop通過(guò)聲明來(lái)完成。 3. 表現層(form bean)獲取業(yè)務(wù)類(lèi)的方法改由自定義工廠(chǎng)類(lèi)來(lái)實(shí)現(加載spring環(huán)境)。 3. JPetStore的改造
3.1. 改造后的架構
其中紅色部分是要增加的部分,藍色部分是要修改的部分。下面就讓我們逐一剖析。 3.2. Spring Context的加載 為了在Struts中加載Spring Context,一般會(huì )在struts-config.xml的最后添加如下部分:
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"><set-property property="contextConfigLocation"value="/WEB-INF/applicationContext.xml" /></plug-in>
|
Spring在設計時(shí)就充分考慮到了與Struts的協(xié)同工作,通過(guò)內置的Struts Plug-in在兩者之間提供了良好的結合點(diǎn)。但是,因為在這里我們一點(diǎn)也不改動(dòng)JPetStore的控制層(這是JpetStore4.0的精華之一),所以本文不準備采用此方式來(lái)加載ApplicationContext。我們利用的是spring framework 的BeanFactory機制,采用自定義的工具類(lèi)(bean工廠(chǎng)類(lèi))來(lái)加載spring的配置文件,從中可以看出Spring有多靈活,它提供了各種不同的方式來(lái)使用其不同的部分/層次,您只需要用你想用的,不需要的部分可以不用。 具體的來(lái)說(shuō),就是在com.ibatis.spring包下創(chuàng )建CustomBeanFactory類(lèi),spring的配置文件applicationContext.xml也放在這個(gè)目錄下。以下就是該類(lèi)的全部代碼,很簡(jiǎn)單:
public final class CustomBeanFactory { static XmlBeanFactory factory = null; static { Resource is = newInputStreamResource( CustomBeanFactory.class.getResourceAsStream("applicationContext.xml")); factory = new XmlBeanFactory(is); } public static Object getBean(String beanName){ return factory.getBean(beanName); }}
|
實(shí)際上就是封裝了Spring 的XMLBeanFactory而已,并且Spring的配置文件只需要加載一次,以后就可以直接用CustomBeanFactory.getBean("someBean")來(lái)獲得需要的對象了(例如someBean),而不需要知道具體的類(lèi)。CustomBeanFactory類(lèi)用于{耦合1}的解耦。 CustomBeanFactory類(lèi)在本文中只用于表現層的form bean對象獲得service類(lèi)的對象,因為我們沒(méi)有把form bean對象配置在applicationContext.xml中。但是,為什么不把表現層的form bean類(lèi)也配置起來(lái)呢,這樣就用不著(zhù)這CustomBeanFactory個(gè)類(lèi)了,Spring會(huì )幫助我們創(chuàng )建需要的一切?問(wèn)題的答案就在于form bean類(lèi)是struts的ActionForm類(lèi)!如果大家熟悉struts,就會(huì )知道ActionForm類(lèi)是struts自動(dòng)創(chuàng )建的:在一次請求中,struts判斷,如果ActionForm實(shí)例不存在,就創(chuàng )建一個(gè)ActionForm對象,把客戶(hù)提交的表單數據保存到ActionForm對象中。因此formbean類(lèi)的對象就不能由spring來(lái)創(chuàng )建,但是service類(lèi)以及數據層的DAO類(lèi)可以,所以只有他們在spring中配置。 所以,很自然的,我們就創(chuàng )建了CustomBeanFactory類(lèi),在表現層來(lái)銜接struts和spring。就這么簡(jiǎn)單,實(shí)現了另一種方式的{耦合一}的解耦。 3.3. 表現層 面分析到,struts和spring是在表現層銜接起來(lái)的,那么表現層就要做稍微的更改,即所需要的service類(lèi)的對象創(chuàng )建上。以表現層的AccountBean類(lèi)為例: 上 原來(lái)的源代碼如下
private static final AccountService accountService = AccountService.getInstance(); private static final CatalogService catalogService = CatalogService.getInstance();
|
改造后的源代碼如下
private static final AccountService accountService = (AccountService)CustomBeanFactory.getBean("AccountService"); private static final CatalogService catalogService = (CatalogService)CustomBeanFactory.getBean("CatalogService");
|
其他的幾個(gè)presentation類(lèi)以同樣方式改造。這樣,表現層就完成了。關(guān)于表現層的其它部分如JSP等一概不動(dòng)。也許您會(huì )說(shuō),沒(méi)有看出什么特別之處的好處???你還是額外實(shí)現了一個(gè)工廠(chǎng)類(lèi)。別著(zhù)急,帷幕剛剛開(kāi)啟,spring是在表現層引入,但您發(fā)沒(méi)發(fā)現: - presentation類(lèi)僅僅面向service類(lèi)的接口編程,具體"AccountService"是哪個(gè)實(shí)現類(lèi),presentation類(lèi)不知道,是在spring的配置文件里配置。(本例中,為了最大限度的保持原來(lái)的代碼不作變化,沒(méi)有抽象出接口)。Spring鼓勵面向接口編程,因為是如此的方便和自然,當然您也可以不這么做。
- CustomBeanFactory這個(gè)工廠(chǎng)類(lèi)為什么會(huì )如此簡(jiǎn)單,因為其直接使用了Spring的BeanFactory。Spring從其核心而言,是一個(gè)DI容器,其設計哲學(xué)是提供一種無(wú)侵入式的高擴展性的框架。為了實(shí)現這個(gè)目標,Spring 大量引入了Java 的Reflection機制,通過(guò)動(dòng)態(tài)調用的方式避免硬編碼方式的約束,并在此基礎上建立了其核心組件BeanFactory,以此作為其依賴(lài)注入機制的實(shí)現基礎。org.springframework.beans包中包括了這些核心組件的實(shí)現類(lèi),核心中的核心為BeanWrapper和BeanFactory類(lèi)。
3.4. 持久層 在討論業(yè)務(wù)層之前,我們先看一下持久層,如下圖所示:
在上文中,我們把iface包下的DAO接口歸為業(yè)務(wù)層,在這里不需要做修改。ibatis的sql配置文件也不需要改。要改的是DAO實(shí)現類(lèi),并在spring的配置文件中配置起來(lái)。 1、修改基類(lèi) 所有的DAO實(shí)現類(lèi)都繼承于BaseSqlMapDao類(lèi)。修改BaseSqlMapDao類(lèi)如下:
public class BaseSqlMapDao extends SqlMapClientDaoSupport { protected static final int PAGE_SIZE = 4; protected SqlMapClientTemplate smcTemplate = this.getSqlMapClientTemplate(); public BaseSqlMapDao() { }}
|
使BaseSqlMapDao類(lèi)改為繼承于Spring提供的SqlMapClientDaoSupport類(lèi),并定義了一個(gè)保護屬性smcTemplate,其類(lèi)型為SqlMapClientTemplate。關(guān)于SqlMapClientTemplate類(lèi)的詳細說(shuō)明請參照附錄中的"Spring中文參考手冊" 2、修改DAO實(shí)現類(lèi) 所有的DAO實(shí)現類(lèi)還是繼承于BaseSqlMapDao類(lèi),實(shí)現相應的DAO接口,但其相應的DAO操作委托SqlMapClientTemplate來(lái)執行,以AccountSqlMapDao類(lèi)為例,部分代碼如下:
public List getUsernameList() { return smcTemplate.queryForList("getUsernameList", null); } public Account getAccount(String username, String password) { Account account = new Account(); account.setUsername(username); account.setPassword(password); return (Account) smcTemplate.queryForObject("getAccountByUsernameAndPassword", account); } public void insertAccount(Account account) { smcTemplate.update("insertAccount", account); smcTemplate.update("insertProfile", account); smcTemplate.update("insertSignon", account); }
|
就這么簡(jiǎn)單,所有函數的簽名都是一樣的,只需要查找替換就可以了! 3、除去工廠(chǎng)類(lèi)以及相應的配置文件 除去DaoConfig.java這個(gè)DAO工廠(chǎng)類(lèi)和相應的配置文件dao.xml,因為DAO的獲取現在要用spring來(lái)管理。 4、DAO在Spring中的配置(applicationContext.xml)
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>org.hsqldb.jdbcDriver</value> </property> <property name="url"> <value>jdbc:hsqldb:hsql://localhost/xdb</value> </property> <property name="username"> <value>sa</value> </property> <property name="password"> <value></value> </property> </bean> <!-- ibatis sqlMapClient config --> <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="configLocation"> <value> classpath:com\ibatis\jpetstore\persistence\sqlmapdao\sql\sql-map-config.xml </value> </property> <property name="dataSource"> <ref bean="dataSource"/> </property> </bean> <!-- Transactions --> <bean id="TransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource"> <ref bean="dataSource"/> </property> </bean> <!-- persistence layer --> <bean id="AccountDao" class="com.ibatis.jpetstore.persistence.sqlmapdao.AccountSqlMapDao"> <property name="sqlMapClient"> <ref local="sqlMapClient"/> </property> </bean>
|
具體的語(yǔ)法請參照附錄中的"Spring中文參考手冊"。在這里只簡(jiǎn)單解釋一下: 1. 我們首先創(chuàng )建一個(gè)數據源dataSource,在這里配置的是hsqldb數據庫。如果是ORACLE數據庫,driverClassName的值是"oracle.jdbc.driver.OracleDriver",URL的值類(lèi)似于"jdbc:oracle:thin:@wugfMobile:1521:cdcf"。數據源現在由spring來(lái)管理,那么現在我們就可以去掉properties目錄下database.properties這個(gè)配置文件了;還有不要忘記修改sql-map-config.xml,去掉<properties resource="properties/database.properties"/>對它的引用。 2. sqlMapClient節點(diǎn)。這個(gè)是針對ibatis SqlMap的SqlMapClientFactoryBean配置。實(shí)際上配置了一個(gè)sqlMapClient的創(chuàng )建工廠(chǎng)類(lèi)。configLocation屬性配置了ibatis映射文件的名稱(chēng)。dataSource屬性指向了使用的數據源,這樣所有使用sqlMapClient的DAO都默認使用了該數據源,除非在DAO的配置中另外顯式指定。 3. TransactionManager節點(diǎn)。定義了事務(wù),使用的是DataSourceTransactionManager。 4. 下面就可以定義DAO節點(diǎn)了,如AccountDao,它的實(shí)現類(lèi)是com.ibatis.jpetstore.persistence.sqlmapdao.AccountSqlMapDao,使用的SQL配置從sqlMapClient中讀取,數據庫連接沒(méi)有特別列出,那么就是默認使用sqlMapClient配置的數據源datasource。 這樣,我們就把持久層改造完了,其他的DAO配置類(lèi)似于A(yíng)ccountDao。怎么樣?簡(jiǎn)單吧。這次有接口了:) AccountDao接口->AccountSqlMapDao實(shí)現。 3.5. 業(yè)務(wù)層 業(yè)務(wù)層的位置以及相關(guān)類(lèi),如下圖所示:
在這個(gè)例子中只有3個(gè)業(yè)務(wù)類(lèi),我們以OrderService類(lèi)為例來(lái)改造,這個(gè)類(lèi)是最復雜的,其中涉及了事務(wù)。 1、在A(yíng)pplicationContext配置文件中增加bean的配置:
<bean id="OrderService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager"> <ref local="TransactionManager"></ref> </property> <property name="target"> <bean class="com.ibatis.jpetstore.service.OrderService"> <property name="itemDao"> <ref bean="ItemDao"/> </property> <property name="orderDao"> <ref bean="OrderDao"/> </property> <property name="sequenceDao"> <ref bean="SequenceDao"/> </property> </bean> </property> <property name="transactionAttributes"> <props> <prop key="insert*">PROPAGATION_REQUIRED</prop> </props> </property> </bean>
|
定義了一個(gè)OrderService,還是很容易懂的。為了簡(jiǎn)單起見(jiàn),使用了嵌套bean,其實(shí)現類(lèi)是com.ibatis.jpetstore.service.OrderService,分別引用了ItemDao,OrderDao,SequenceDao。該bean的insert*實(shí)現了事務(wù)管理(AOP方式)。TransactionProxyFactoryBean自動(dòng)創(chuàng )建一個(gè)事務(wù)advisor, 該advisor包括一個(gè)基于事務(wù)屬性的pointcut,因此只有事務(wù)性的方法被攔截。 2、業(yè)務(wù)類(lèi)的修改 以OrderService為例:
public class OrderService { /* Private Fields */ private ItemDao itemDao; private OrderDao orderDao; private SequenceDao sequenceDao; /* Constructors */ public OrderService() { }/** * @param itemDao 要設置的 itemDao。 */public final void setItemDao(ItemDao itemDao) { this.itemDao = itemDao;}/** * @param orderDao 要設置的 orderDao。 */public final void setOrderDao(OrderDao orderDao) { this.orderDao = orderDao;}/** * @param sequenceDao 要設置的 sequenceDao。 */public final void setSequenceDao(SequenceDao sequenceDao) { this.sequenceDao = sequenceDao;}//剩下的部分…….}
|
紅色部分為修改部分。Spring采用的是Type2的設置依賴(lài)注入,所以我們只需要定義屬性和相應的設值函數就可以了,ItemDao,OrderDao,SequenceDao的值由spring在運行期間注入。構造函數就可以為空了,另外也不需要自己編寫(xiě)代碼處理事務(wù)了(事務(wù)在配置中聲明),daoManager.startTransaction();等與事務(wù)相關(guān)的語(yǔ)句也可以去掉了。和原來(lái)的代碼比較一下,是不是處理精簡(jiǎn)了很多!可以更關(guān)注業(yè)務(wù)的實(shí)現。 4. 結束語(yǔ) ibatis是一個(gè)功能強大實(shí)用的SQL Map工具,可以直接控制SQL,為系統設計提供了更大的自由空間。其提供的最新示例程序JpetStore 4.0,設計優(yōu)雅,應用了迄今為止很多最佳實(shí)踐和設計模式,非常適于學(xué)習以及在此基礎上創(chuàng )建輕量級的J2EE WEB應用程序。JpetStore 4.0是基于struts的,本文在此基礎上,最大程度保持了原有設計的精華以及最小的代碼改動(dòng)量,在業(yè)務(wù)層和持久化層引入了Spring。在您閱讀了本文以及改造后的源代碼后,會(huì )深切的感受到Spring帶來(lái)的種種好處:自然的面向接口的編程,業(yè)務(wù)對象的依賴(lài)注入,一致的數據存取框架和聲明式的事務(wù)處理,統一的配置文件…更重要的是Spring既是全面的又是模塊化的,Spring有分層的體系結構,這意味著(zhù)您能選擇僅僅使用它任何一個(gè)獨立的部分,就像本文,而它的架構又是內部一致。 參考資料 關(guān)于作者 吳高峰,原一直在中興通訊從事運營(yíng)支撐產(chǎn)品的研發(fā)工作,對J2EE以及各種開(kāi)源項目感興趣?,F在常德卷煙廠(chǎng)信息技術(shù)部從事EAI的建設。聯(lián)系方式:shuwgf@21cn.com |
|