其實(shí),就算用Java建造一個(gè)不是很煩瑣的web應(yīng)用,也不是件輕松的事情。 在構(gòu)架的一開(kāi)始就有很多事情要考慮。 從高處看,擺在開(kāi)發(fā)者面前有很多問(wèn)題:要考慮是怎樣建立用戶接口?在哪里處理業(yè)務(wù)邏輯? 怎樣持久化的數(shù)據(jù)。 而這三層構(gòu)架中,每一層都有他們要仔細(xì)考慮的。 各個(gè)層該使用什么技術(shù)? 怎樣的設(shè)計(jì)能松散耦合還能靈活改變? 怎樣替換某個(gè)層而不影響整體構(gòu)架?應(yīng)用程序如何做各種級(jí)別的業(yè)務(wù)處理(比如事務(wù)處理)?
構(gòu)架一個(gè)Web應(yīng)用需要弄明白好多問(wèn)題。 幸運(yùn)的是,已經(jīng)有不少開(kāi)發(fā)者已經(jīng)遇到過(guò)這類問(wèn)題,并且建立了處理這類問(wèn)題的框架。 一個(gè)好框架具備以下幾點(diǎn): 減輕開(kāi)發(fā)者處理復(fù)雜的問(wèn)題的負(fù)擔(dān)(“不重復(fù)發(fā)明輪子”); 內(nèi)部有良好的擴(kuò)展; 并且有一個(gè)支持它的強(qiáng)大的用戶團(tuán)體。 好的構(gòu)架一般有針對(duì)性的處理某一類問(wèn)題,并且能將它做好(Do One Thing well)。 然而,你的程序中有幾個(gè)層可能需要使用特定的框架,已經(jīng)完成的UI(用戶接口) 并不代表你也可以把你的業(yè)務(wù)邏輯和持久邏輯偶合到你的UI部分。 舉個(gè)例子, 你不該在一個(gè)Controller(控制器)里面寫(xiě)JDBC代碼作為你的業(yè)務(wù)邏輯, 這不是控制器應(yīng)該提供的。 一個(gè)UI 控制器應(yīng)該委派給其它給在UI范圍之外的輕量級(jí)組件。 好的框架應(yīng)該能指導(dǎo)代碼如何分布。 更重要的是,框架能把開(kāi)發(fā)者從編碼中解放出來(lái),使他們能專心于應(yīng)用程序的邏輯(這對(duì)客戶來(lái)說(shuō)很重要)。
這篇文章將討論怎樣結(jié)合幾種著名的框架來(lái)使得你的應(yīng)用程序做到松弛耦合。
如何建立你的架構(gòu),并且怎樣讓你的各個(gè)應(yīng)用層保持一致。?如何整合框架以便讓每個(gè)層在以一種松散偶合的方式彼此作用而不用管低層的技術(shù)細(xì)節(jié)?這對(duì)我們來(lái)說(shuō)真是一種挑戰(zhàn)。 這里討論一個(gè)整合框架的策略( 使用3 種受歡迎的開(kāi)源框架) :表示層我們用Struts; 業(yè)務(wù)層我們用Spring;而持久層則用Hibernate。 你也可以用其他FrameWork替換只要能得到同樣的效果。 見(jiàn)圖1 (框架組合示意圖)
元\j2ee\【使用open source產(chǎn)品組裝你的web應(yīng)用架構(gòu)】.mht!http://www.uml.org.cn/images/upfile/2004418241.gif)
應(yīng)用程序的分層
大部分的Web應(yīng)用在職責(zé)上至少能被分成4層。 這四層是:presentation(描述),persistence(持久),business(業(yè)務(wù))和domain model(域模塊)。每個(gè)層在處理程序上都應(yīng)該有一項(xiàng)明確的責(zé)任, 而不應(yīng)該在功能上與其它層混合,并且每個(gè)層要與其它層分開(kāi)的,但要給他們之間放一個(gè)通信接口。 我們就從介紹各個(gè)層開(kāi)始,討論一下這些層應(yīng)該提供什么,不應(yīng)該提供什么。
表示層(The Presentation Layer)
一般來(lái)講,一個(gè)典型的Web應(yīng)用的的末端應(yīng)該是表示層。 很多Java發(fā)者也理解Struts所提供的。 象業(yè)務(wù)邏輯之類的被打包到org.apache.struts.Action., 因此,我們很贊成使用Struts這樣的框架。
下面是Struts所負(fù)責(zé)的:
* 管理用戶的請(qǐng)求,做出相應(yīng)的響應(yīng)。
* 提供一個(gè)Controller ,委派調(diào)用業(yè)務(wù)邏輯和其它上層處理。
* 處理異常,拋給Struts Action
* 為顯示提供一個(gè)模型
* UI驗(yàn)證。
以下條款,不該在Struts顯示層的編碼中經(jīng)常出現(xiàn)。 它們與顯示層無(wú)關(guān)的。
* 直接的與數(shù)據(jù)庫(kù)通信,例如JDBC調(diào)用。
* 與你應(yīng)用程序相關(guān)聯(lián)的業(yè)務(wù)邏輯以及校驗(yàn)。
* 事物管理。
在表示層引入這些代碼,則會(huì)帶來(lái)高偶合和麻煩的維護(hù)。
持久層(The Persistence Layer)
典型的Web應(yīng)用的另一個(gè)末端是持久層。這里通常是程序最容易失控的地方。開(kāi)發(fā)者總是低估構(gòu)建他們自己的持久框架的挑戰(zhàn)性。系統(tǒng)內(nèi)部的持續(xù)層不但需要大量調(diào)試時(shí)間,而且還經(jīng)常缺少功能使之變得難以控制,這是持久層的通病。 還好有幾個(gè)ORM開(kāi)源框架很好的解決了這類問(wèn)題。尤其是Hibernate。 Hibernate為java提供了OR持久化機(jī)制和查詢服務(wù), 它還給已經(jīng)熟悉SQL和JDBC API 的Java開(kāi)發(fā)者一個(gè)學(xué)習(xí)橋梁,他們學(xué)習(xí)起來(lái)很方便。 Hibernate的持久對(duì)象是基于POJO和Java collections。此外,使用Hibernate并不妨礙你正在使用的IDE。
請(qǐng)看下面的條目,你在持久層編碼中需要了解的。
* 查詢對(duì)象的相關(guān)信息的語(yǔ)句。 Hibernate通過(guò)一個(gè)OO查詢語(yǔ)言(HQL)或者正則表達(dá)的API來(lái)完成查詢。 HQL非常類似于SQL-- 只是把SQL里的table和columns用Object和它的fields代替。 你需要學(xué)習(xí)一些新的HQL語(yǔ)言; 不管怎樣,他們?nèi)菀桌斫舛臋n也做的很好。 HQL是一種對(duì)象查詢的自然語(yǔ)言,花很小的代價(jià)就能學(xué)習(xí)它。
* 如何存儲(chǔ),更新,刪除數(shù)據(jù)庫(kù)記錄。
* 象Hibernate這類的高級(jí)ORM框架支持大部分主流數(shù)據(jù)庫(kù),并且他們支持 Parent/child關(guān)系,事物處理,繼承和多態(tài)。
業(yè)務(wù)層(The Business Layer)
一個(gè)典型Web應(yīng)用的中間部分是業(yè)務(wù)層或者服務(wù)層。 從編碼的視角來(lái)看,這層是最容易被忽視的一層。 而我們卻往往在UI層或持久層周?chē)吹竭@些業(yè)務(wù)處理的代碼,這其實(shí)是不正確的,因?yàn)樗鼘?dǎo)致了程序代碼的緊密偶合,這樣一來(lái),隨著時(shí)間推移這些代碼很難維護(hù)。幸好,針對(duì)這一問(wèn)題有好幾種Frameworks存在。 最受歡迎的兩個(gè)框架是Spring和PicoContainer。 這些為也被稱為microcontainers,他們能讓你很好的把對(duì)象搭配起來(lái)。 這兩個(gè)框架都著手于‘依賴注射’(dependency injection)(還有我們知道的‘控制反轉(zhuǎn)’Inversion of Control=IoC)這樣的簡(jiǎn)單概念。 這篇文章將關(guān)注于Spring的注射(譯注:通過(guò)一個(gè)給定參數(shù)的Setter方法來(lái)構(gòu)造Bean,有所不同于Factory), Spring還提供了Setter Injection(type2),Constructor Injection(type3)等方式供我們選擇。 Spring把程序中所涉及到包含業(yè)務(wù)邏輯和Dao的Objects——例如transaction management handler(事物管理控制)、Object Factoris(對(duì)象工廠)、service objects(服務(wù)組件)——都通過(guò)XML來(lái)配置聯(lián)系起來(lái)。
后面我們會(huì)舉個(gè)例子來(lái)揭示一下Spring 是怎樣運(yùn)用這些概念。
業(yè)務(wù)層所負(fù)責(zé)的如下:
* 處理應(yīng)用程序的 業(yè)務(wù)邏輯和業(yè)務(wù)校驗(yàn)
* 管理事物
* 允許與其它層相互作用的接口
* 管理業(yè)務(wù)層級(jí)別的對(duì)象的依賴。
* 在顯示層和持久層之間增加了一個(gè)靈活的機(jī)制,使得他們不直接的聯(lián)系在一起。
* 通過(guò)揭示 從顯示層到業(yè)務(wù)層之間的Context來(lái)得到business services。
* 管理程序的執(zhí)行(從業(yè)務(wù)層到持久層)。
域模塊層(The Domain Model Layer )
既然我們致力于的是一個(gè)不是很復(fù)雜的Web的應(yīng)用, 我們需要一個(gè)對(duì)象集合,讓它在不同層之間移動(dòng)的。 域模塊層由實(shí)際需求中的業(yè)務(wù)對(duì)象組成 比如, OrderLineItem , Product等等。 開(kāi)發(fā)者在這層 不用管那些DTOs,僅關(guān)注domain object即可。 例如,Hibernate允許你將數(shù)據(jù)庫(kù)中的信息存放入對(duì)象(domain objects),這樣你可以在連接斷開(kāi)的情況下把這些數(shù)據(jù)顯示到UI層。 而那些對(duì)象也可以返回給持續(xù)層,從而在數(shù)據(jù)庫(kù)里更新。 而且,你不必把對(duì)象轉(zhuǎn)化成DTOs(這可能似的它在不同層之間的在傳輸過(guò)程中丟失),這個(gè)模型使得Java開(kāi)發(fā)者能很自然運(yùn)用OO,而不需要附加的編碼。
一個(gè)簡(jiǎn)單例子
既然我們已經(jīng)從全局上理解這些組件。 現(xiàn)在就讓我們開(kāi)始實(shí)踐吧。 我們還是用 Struts,Spring 和Hibernate。這三個(gè)框架已經(jīng)被描述夠多了,這里就不重復(fù)介紹了。 這篇文章舉例指導(dǎo)你如何使用這三個(gè)框架整合開(kāi)發(fā), 并向你揭示 一個(gè)請(qǐng)求是如何貫穿于各個(gè)層的。(從用戶的加入一個(gè)Order到數(shù)據(jù)庫(kù),顯示;進(jìn)而更新、刪除)。
從這里可以下載到程序程序原代碼(download)
既然每個(gè)層是互相作用的,我們就先來(lái)創(chuàng)建domain objects。首先,我們要在這些Object中要確定那些是需要持久化的,哪些是提供給business logic,那些是顯示接口的設(shè)計(jì)。 下一步,我們將配置我們的持久層并且定義好Hibernate的OR mappings。然后定義好Business Objects。有了這些組成部分之后,我們將 使用Spring把這些連接起來(lái)。 最后,我們提供給Spring一個(gè)持久層,從這個(gè)持久層里我們可以知道它是如何與業(yè)務(wù)邏輯層(business service layer)通信的,以及它是怎樣處理其他層拋出的異常的。。
域?qū)ο髮樱?/SPAN>Domain Object Layer)
這層是編碼的著手點(diǎn),我們的編碼就從這層開(kāi)始。 例子中Order 與OrderItem 是一個(gè)One—To—Many的關(guān)系。 下面就是Domain Object Layer的兩個(gè)對(duì)象:
·
com.meagle.bo.Order.java: 包含了一個(gè)Order的概要信息
· com.meagle.bo.OrderLineItem.java: 包含了Order的詳細(xì)信息
好好考慮怎你的package命名,這反應(yīng)出了你是怎樣分層的。 例如 domain objects在程序中可能打包在com.meagle.bo
內(nèi)。 更詳細(xì)一點(diǎn)將打包在com. meagle.bo
的子目錄下面。business logic應(yīng)該從com.meagle.serice
開(kāi)始打
包,而DAO 對(duì)象應(yīng)該位于com.meagle.service.dao.hibernate
。反應(yīng)
Forms
和
Actions
的
持久對(duì)象(
presentation classes) 應(yīng)該分別放在 com.meagle.action
和
com.meagle.forms
包。 準(zhǔn)確的給包命名使得你的classes很好分割并且易于維護(hù),并且在你添加新的classes時(shí),能使得程序結(jié)構(gòu)上保持上下一致。
持久層的配置(Persistence Layer Configuration)
建立Hibernate的持久層 需要好幾個(gè)步驟。 第一步讓我們把BO持久化。 既然Hibernate是通過(guò)POJO工作的, 因此Order
和 OrderLineItem
對(duì)象需要給所有的fileds 加上getter,setter方法。 Hibernate通過(guò)XML文件來(lái)映射(OR)對(duì)象,以下兩個(gè)xml文件分別映射了Order 和OrderItem對(duì)象。(這里有個(gè)叫XDoclet工具可以自動(dòng)生成你的XML影射文件)
- Order.hbm.xml
- OrderLineItem.hbm.xml
你可以在WebContent/WEB-INF/classes/com/meagle/bo目錄下找到這些xml文件。Hibernate的 SessionFactory 是用來(lái)告訴程序 應(yīng)該與哪個(gè)數(shù)據(jù)庫(kù)通信,該使用哪個(gè)連接池或使用了DataSource
,
應(yīng)該加載哪些持久對(duì)象。而Session接口是用來(lái)完成Selecting,Saving,Delete和Updating這些操作。 后面的我們將講述SessionFactory和Session是怎樣設(shè)置的。
業(yè)務(wù)層的配置(Business Layer Configuration)
既然我們已經(jīng)有了domain objects,接下來(lái)我們就要business service objects了,用他們來(lái)執(zhí)行程序的logic,調(diào)用持久層,得到UI層的requests,處理transactions,并且控制exceptions。 為了將這些連接起來(lái)并且易于管理,我們將使用面向方面的 SpringFramework。 Spring 提供了 控制倒置(inversion of control 0==IoC)和注射依賴設(shè)置(setter dependency injection)這些方式(可供選擇),用XML文件將對(duì)象連接起來(lái)。 IoC是一個(gè)簡(jiǎn)單概念(它允許一個(gè)對(duì)象在上層接受其他對(duì)象的創(chuàng)建),用IoC這種方式讓你的對(duì)象從創(chuàng)建中釋放了出來(lái),降低了偶合度。
這里是一個(gè)沒(méi)有使用IoC的對(duì)象創(chuàng)建的例子,它有很高偶合度。
元\j2ee\【使用open source產(chǎn)品組裝你的web應(yīng)用架構(gòu)】.mht!http://www.uml.org.cn/images/upfile/2004418242.gif)
圖 2.沒(méi)有使用 IoC. A 創(chuàng)建了 B 和 C
而這里是一個(gè)使用IoC的例子,這種方式允許對(duì)象在高層可以創(chuàng)建并進(jìn)入另外一個(gè)對(duì)象,所以這樣可以直接被執(zhí)行。
元\j2ee\【使用open source產(chǎn)品組裝你的web應(yīng)用架構(gòu)】.mht!http://www.uml.org.cn/images/upfile/2004418243.gif)
圖 3. 對(duì)象使用了 IoC。 A 包含了接受B,C的 setter方法 , 這同樣達(dá)到了 由A創(chuàng)建B,C的目的。
建立我們的業(yè)務(wù)服務(wù)對(duì)象(Building Our Business Service Objects)
Business Object中的Setter方法接受的是接口,這樣我們可以很松散的定義對(duì)象實(shí)現(xiàn),然后注入。 在我們的案例中,我們將用一個(gè)business service object接收一個(gè)DAO,用它來(lái)控制domain objects的持久化。 由于在這個(gè)例子中使用了Hibernate,我們可以很方便的用其他持久框架實(shí)現(xiàn) 同時(shí)通知Spring 有新的DAO可以使用了。
在面向接口的編程中,你會(huì)明白 “注射依賴”模式是怎樣松散耦合你的業(yè)務(wù)邏輯和持久機(jī)制的:)。
下面是一個(gè)接口business service object,DAO代碼片段:
public interface IOrderService {
public abstract Order saveNewOrder(Order order)
throws OrderException,
OrderMinimumAmountException;
public abstract List findOrderByUser(
String user)
throws OrderException;
public abstract Order findOrderById(int id)
throws OrderException;
public abstract void setOrderDAO(
IOrderDAO orderDAO);
}
注意到這段代碼里有一個(gè) setOrderDao(),它就是一個(gè)DAO Object設(shè)置方法(注射器)。 但這里并沒(méi)有一個(gè)getOrderDao的方法,這不必要,因?yàn)槟悴⒉粫?huì)在外部訪問(wèn)這個(gè)orderDao。這個(gè)DAO Objecte將被調(diào)用,和我們的persistence layer 通信。我們將用Spring把DAO Object 和 business service object搭配起來(lái)的。因?yàn)槲覀兪敲嫦蚪涌诰幊痰模圆⒉恍枰獙?shí)現(xiàn)類緊密的耦合在一起。
接下去我們開(kāi)始我們的DAO的實(shí)現(xiàn)類進(jìn)行編碼。 既然Spring已經(jīng)有對(duì)Hibernate的支持,那這個(gè)例子就直接繼承HibernateDaoSupport
類了,這個(gè)類很有用,我們可以參考HibernateTemplate
(它主要是針對(duì)HibernateDaoSupport的一個(gè)用法,譯注:具體可以查看Srping 的API)。 下面是這個(gè)DAO接口代碼:
public interface IOrderDAO {
public abstract Order findOrderById(
final int id);
public abstract List findOrdersPlaceByUser(
final String placedBy);
public abstract Order saveOrder(
final Order order);
}
我們?nèi)匀灰o我們持久層組裝很多關(guān)聯(lián)的對(duì)象,這里包含了HibernateSessionFactory
和TransactionManager
。
Spring 提供了一個(gè) HibernateTransactionManager
,他用線程捆綁了一個(gè)Hibernate Session,用它來(lái)支持transactions(請(qǐng)查看ThreadLocal
) 。
下面是HibernateSessionFactory
和 HibernateTransactionManager
:的配置:
<bean id="mySessionFactory"
class="org.springframework.orm.hibernate.
LocalSessionFactoryBean">
<property name="mappingResources">
<list>
<value>
com/meagle/bo/Order.hbm.xml
</value>
<value>
com/meagle/bo/OrderLineItem.hbm.xml
</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
net.sf.hibernate.dialect.MySQLDialect
</prop>
<prop key="hibernate.show_sql">
false
</prop>
<prop key="hibernate.proxool.xml">
C:/MyWebApps/.../WEB-INF/proxool.xml
</prop>
<prop key="hibernate.proxool.pool_alias">
spring
</prop>
</props>
</property>
</bean>
<!-- Transaction manager for a single Hibernate
SessionFactory (alternative to JTA) -->
<bean id="myTransactionManager"
class="org.
springframework.
orm.
hibernate.
HibernateTransactionManager">
<property name="sessionFactory">
<ref local="mySessionFactory"/>
</property>
</bean>
可以看出:每個(gè)對(duì)象都可以在Spring 配置信息中用<bean>標(biāo)簽引用。在這里,mySessionFactory
引用了
HibernateSessionFactory
,而
myTransactionManager
引用了
HibernateTransactionManage
。
注意代碼中
myTransactionManger Bean
有個(gè)
sessionFactory
屬性。
HibernateTransactionManager
有個(gè)sessionFactory
setter 和 getter方法,這是用來(lái)在Spring啟動(dòng)的時(shí)候?qū)崿F(xiàn)“依賴注入” (dependency injection)的。 在sessionFactory
屬性里 引用mySessionFactory
。這兩個(gè)對(duì)象在Spring容器初始化后就被組裝了起來(lái)了。 這樣的搭配讓你從 單例(singleton objects)和工廠(factories)中解放了出來(lái),降低了代碼的維護(hù)代價(jià)。 mySessionFactory
.的兩個(gè)屬性,分別是用來(lái)注入mappingResources
和 hibernatePropertes
的。通常,如果你在
Spring
之外使用
Hibernate,
這樣的設(shè)置應(yīng)該放在
hibernate.cfg.xml中的。 不管怎樣,Spring提供了一個(gè)便捷的方式-----在Spring內(nèi)部配置中并入了Hibernate的配置。 如果要得到更多的信息,可以查閱Spring API。
既然我們已經(jīng)組裝配置好了Service Beans,就需要把Business Service Object和 DAO也組裝起來(lái),并把這些對(duì)象配到一個(gè)事務(wù)管理器(transaction manager)里。
在Spring中的配置信息:
<!-- ORDER SERVICE -->
<bean id="orderService"
class="org.
springframework.
transaction.
interceptor.
TransactionProxyFactoryBean">
<property name="transactionManager">
<ref local="myTransactionManager"/>
</property>
<property name="target">
<ref local="orderTarget"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="find*">
PROPAGATION_REQUIRED,readOnly,-OrderException
</prop>
<prop key="save*">
PROPAGATION_REQUIRED,-OrderException
</prop>
</props>
</property>
</bean>
<!-- ORDER TARGET PRIMARY BUSINESS OBJECT:
Hibernate implementation -->
<bean id="orderTarget"
class="com.
meagle.
service.
spring.
OrderServiceSpringImpl">
<property name="orderDAO">
<ref local="orderDAO"/>
</property>
</bean>
<!-- ORDER DAO OBJECT -->
<bean id="orderDAO"
class="com.
meagle.
service.
dao.
hibernate.
OrderHibernateDAO">
<property name="sessionFactory">
<ref local="mySessionFactory"/>
</property>
</bean>
圖4 是我們對(duì)象搭建的一個(gè)提綱。 從中可以看出,每個(gè)對(duì)象都聯(lián)系著Spring,并且能通過(guò)Spring注入到其他對(duì)象。把它與Spring的配置文件比較,觀察他們之間的關(guān)系
元\j2ee\【使用open source產(chǎn)品組裝你的web應(yīng)用架構(gòu)】.mht!http://www.uml.org.cn/images/upfile/2004418244.gif)
圖 4. Spring就是這樣基于配置文件,將各個(gè)Bean搭建在一起。
這個(gè)例子使用一個(gè)TransactionProxyFactoryBean
,它定義了一個(gè)
setTransactionManager()
。
這對(duì)象很有用,他能很方便的處理你申明的事物還有
Service Object
。
你可以通過(guò)
transactionAttributes
屬性來(lái)定義怎樣處理。
想知道更多還是參考
TransactionAttributeEditor
吧。
TransactionProxyFactoryBean
還有個(gè)setter. 這會(huì)被我們 Business service object(orderTarget
)引用, orderTarget
定義了
業(yè)務(wù)服務(wù)層,并且它還有個(gè)屬性,由
setOrderDAO()
引用。這個(gè)屬性
Spring 和Bean 的還有一點(diǎn)要注意的: bean可以以用兩種方式創(chuàng)造。 這些都在單例模式(Sington)和原型模式(propotype)中定義了。 默認(rèn)的方式是singleton,這意味著共享的實(shí)例將被束縛。 而原形模式是在Spring用到bean的時(shí)候允許新建實(shí)例的。當(dāng)每個(gè)用戶需要得到他們自己Bean的Copy時(shí),你應(yīng)該僅使用prototype模式。(更多的請(qǐng)參考設(shè)計(jì)模式中的單例模式和原形模式)
提供一個(gè)服務(wù)定位器(Providing a Service Locator)
既然我們已經(jīng)將我們的Serices和DAO搭配起來(lái)了。我們需要把我們的Service顯示到其他層。 這個(gè)通常是在Struts或者Swing這層里編碼。一個(gè)簡(jiǎn)單方法就是用 服務(wù)定位器返回給Spring context 。當(dāng)然,可以通過(guò)直接調(diào)用Spring中的Bean來(lái)做。
下面是一個(gè)Struts Actin 中的服務(wù)定位器的一個(gè)例子。
public abstract class BaseAction extends Action {
private IOrderService orderService;
public void setServlet(ActionServlet
actionServlet) {
super.setServlet(actionServlet);
ServletContext servletContext =
actionServlet.getServletContext();
WebApplicationContext wac =
WebApplicationContextUtils.
getRequiredWebApplicationContext(
servletContext);
this.orderService = (IOrderService)
wac.getBean("orderService");
}
protected IOrderService getOrderService() {
return orderService;
}
}
UI 層配置 (UI Layer Configuration)
這個(gè)例子里UI層 使用了Struts framework. 這里我們要講述一下在給程序分層的時(shí)候, 哪些是和Struts部分的。我們就從一個(gè)Struts-config.xml文件中的Action的配置信息開(kāi)始吧。
struts-config.xml
file.
<action path="/SaveNewOrder"
type="com.meagle.action.SaveOrderAction"
name="OrderForm"
scope="request"
validate="true"
input="/NewOrder.jsp">
<display-name>Save New Order</display-name>
<exception key="error.order.save"
path="/NewOrder.jsp"
scope="request"
type="com.meagle.exception.OrderException"/>
<exception key="error.order.not.enough.money"
path="/NewOrder.jsp"
scope="request"
type="com.
meagle.
exception.
OrderMinimumAmountException"/>
<forward name="success" path="/ViewOrder.jsp"/>
<forward name="failure" path="/NewOrder.jsp"/>
</action>
SaveNewOrder 這個(gè)Action是用來(lái)持久化UI層里的表單提交過(guò)來(lái)Order的。這是Struts中一個(gè)很典型的Action; 注意觀察這個(gè)Action中exception配置,這些Exceptions也在Spring 配置文件(applicationContext-hibernate.xml)中配置了(就在 business service object 的transactionAttributes
屬性里)。 當(dāng)異常在業(yè)務(wù)層被被拋出時(shí),我們可以控制他們,并適當(dāng)?shù)娘@示給UI層。
第一個(gè)異常,OrderException,在持久層保存order對(duì)象失敗的時(shí)候被觸發(fā)。這將導(dǎo)致事物回滾并且通過(guò)BO把異常回傳到Struts這一層。
第二個(gè)異常,OrderMinimumAmountException
也同第一個(gè)一樣。
搭配整和的最后一步 通過(guò)是讓你顯示層和業(yè)務(wù)層相結(jié)合。這個(gè)已經(jīng)被服務(wù)定位器(service locator)實(shí)現(xiàn)了(前面討論過(guò)了), 這里服務(wù)層作為一個(gè)接口提供給我們的業(yè)務(wù)邏輯和持久層。
SaveNewOrder Action
在Struts中用一個(gè)服務(wù)定位器(service locator)來(lái)調(diào)用執(zhí)行業(yè)務(wù)方法的。 方法代碼如下:
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
throws java.lang.Exception {
OrderForm oForm = (OrderForm) form;
// Use the form to build an Order object that
// can be saved in the persistence layer.
// See the full source code in the sample app.
// Obtain the wired business service object
// from the service locator configuration
// in BaseAction.
// Delegate the save to the service layer and
// further upstream to save the Order object.
getOrderService().saveNewOrder(order);
oForm.setOrder(order);
ActionMessages messages = new ActionMessages();
messages.add(
ActionMessages.GLOBAL_MESSAGE,
new ActionMessage(
"message.order.saved.successfully"));
saveMessages(request, messages);
return mapping.findForward("success");
}
總結(jié)
這篇文章在技術(shù)和構(gòu)架方面掩蓋了很多低層的基礎(chǔ)信息, 文章的主要的意圖在于讓你意識(shí)到如何給你應(yīng)用程序分層。 分層可以“解耦”你的代碼——允許新的組件被添加進(jìn)來(lái),而且讓你的代碼易于維護(hù)。 這里用到的技術(shù)只是專注于把“解偶”做好。 不管怎樣,使用這樣的構(gòu)架可以讓你用其他技術(shù)代替現(xiàn)在的層。 例如,你可能不使用Hibernate實(shí)現(xiàn)持久化。既然你在DAO中面向接口的編程的,所以你完全可以用iBATIS來(lái)代替。或者,你也可能想用Struts外的其他的技術(shù)或者框架替換現(xiàn)在的UI層(轉(zhuǎn)換久層,實(shí)現(xiàn)層并不應(yīng)該直接影響到你的業(yè)務(wù)邏輯和業(yè)務(wù)服務(wù)層)。 用適當(dāng)?shù)目蚣艽罱愕?/SPAN>Web應(yīng)用,其實(shí)也不是一件煩瑣的工作,更主要的是它“解耦”了你程序中的各個(gè)層.