alix_an@sohu.com
內容摘要
在J2EE技術飛速發展的今天,各種技術框架和設計模式層出不窮,每種技術都有各自的優勢與劣勢。目前,沒有一種技術處于絕對的霸主地位,如何使我們的應用程序不過多地依賴某種實現技術,具有強的可擴展性,同時,能更好地集成各種技術框架,使他們發揮最大的優勢,是我們創建企業應用時,應該考慮的問題。本文主要介紹了筆者基于SOA(Service Oriented Architecture)思想而設計的MiniSOA構架的設計原理,核心組件與使用示例,它能很好地幫您集成各種技術,使您輕松地選擇自己的表現層、業務層和數據持久層,“無痛”切換各個層面的技術實現,降低各個層面的耦合性,同時滿足多小組的團隊開發,提高開發效率,最大限度地發揮開發人員的技術優勢。
引言
隨著J2EE的飛速發展,已經有越來越多的企業應用程序以J2EE技術為其構建的基石,J2EE本身并不是產品,它只是制定了一套創建企業應用程序的規范,不同廠商根據J2EE規范,創建了符合J2EE規范的產品,這給予了我們更多的選擇創建企業應用的平臺。
一個典型的J2EE的應用,至少應該包括以下三部分:表現層,業務邏輯層和數據持久層,為了更加容易地創建企業應用程序,許許多多的Framework涌現出來,表現層我們可以選擇Struts, JSF, Tapestry, WebWork, Velocity等, 數據持久層我們可以選擇原始的JDBC, ORMapping tools(Hibernate,toplink等),SQLMapper tools(Ibatis),JDO, EJB(Entity Bean)等,業務邏輯層我們可以用普通的JAVA Beans,也可以用EJB(Session Bean)。每種技術都有它的優點與缺點,各自有各自的適用范疇,例如EJB可以很好地進行分布式處理和Object Cache等,但EJB的運行需要EJB容器,開發調試起來很不方便,特別在需求不確定性很大、模型不穩定的情況下,實在是一種重量級別的開發;而JAVA BEAN則是一種很輕量級的方式,開發調試容易,但又很難實現分布式處理。在各種技術紛爭的今天,暫時還沒有一種技術處于絕對的霸主地位,在這種條件下,我們不能把“賭注“押在任何一種技術上,如何使我們的應用程序有很高的靈活性和易擴展性是我們要仔細研究的課題。
在實際的項目中,關于應用程序開發時所用技術的問題,大致存在兩種情況,一種是構架師或技術經理沒有嚴格限定用什么技術來實現具體的業務邏輯或者只有簡單的開發規范,程序員在開發時,只是依據自己的技術背景,選擇自己熟悉的實現方式,這種情況一般屬于橫向開發,在小的項目中,每個人只做自己負責的一個模塊,從表現層,業務邏輯層,一直到數據層,都由同一個人來負責,這種方式給了技術人員更多的自我發揮能力的空間,但不便于后期維護,特別是人員流動頻繁的情況下,問題更是嚴重。第二種情況是構架師或技術經理在項目初期從開發成本,項目需求等等各個方面做出評估,經過幾番取舍,確定項目各個層面使用什么樣的技術實現方式,按不同層面進行分工,不同的工作人員負責不同層面的技術實現,這種方式比第一種方式要好得多,適合校大項目的開發,但也存在很多問題。在目前各種實現技術紛爭的情況下,沒有一種技術是萬能的,在做取舍時,難免和某一技術或實現方式依賴性過強,同時限定了技術人員個人技術特長的很好發揮,當由于某些原因要更改實現方式時,經常是牽一發而動全身,造成資源的極大浪費和開發成本的提高。
所以,在構建企業應用時,應該有個好的技術框架,這個框架應該考慮到各種主流的實現技術,我們既可以根據實際情況進行取舍,同時在從一種實現方式變更為另一種實現方式時,又可以進行平滑過度,讓多種技術實現并存,發揮技術人員的最大優勢,降低項目成本,提高開發效率。
基于SOA的構架
圖一

SOA(Service Oriented Architecture),對這一術語我們并不陌生,因為Web service是基于SOA的一種技術,服務的提供者將提供的服務注冊到UDDI,其使用者從UDDI上獲得服務的描述(WSDL),然后根據服務接口使用服務。Web Service用XML進行消息的傳遞,通過SOAP綁定在現有的輕量級協議,如HTTP之上,可以透過防火墻,不依賴服務端和客戶端具體地實現技術,進行分布式遠程調用,它是現有的應用向Internet的延伸。Web service在EAI,B2B,應用到應用的集成等方面體現了巨大的優勢,有很多文獻介紹Web service,由于這不是本文重點,在此不再贅述。
SOA的優勢在于降低了服務的提供者與使用者之間的耦合性,服務的提供者將自己提供的服務注冊在中介那里,服務使用者先通過中介查找自己所需服務,使用者獲得的是服務接口,但并不知道服務的具體實現,它根據調用接口調用服務,這樣即使服務的實現方式發生了變化,只要供使用者調用接口沒有改變,服務使用者就不會受到任何影響,這種思想正是我們應該學習和借鑒的地方。那么既然Web service是基于SOA的,我們的企業應用是不是就可以完全構架在Web service上呢?我們并不建議這么做,Web service對于企業內部的應用并不太適合,在一個應用內部使用Web service,系統大量的資源花費在進行XML消息的解析和進行遠程調用上,造成系統運轉緩慢。當然,從某種意義上講,EJB也是SOA的一種實現,服務的提供者把服務注冊在JNDI上,使用者通過JNDI找到自己想要的服務,通過遠程接口使用服務,但EJB的運行需要EJB 容器,開發調試起來不方便,特別對于需求經常變化的系統,進行EJB調試的時間會更久。我們所需要的是一個輕量級的構架,能兼顧各種主流的技術,但又不依賴具體的某個實現方式,當實現方式從一種技術變更為另一種技術時,對于服務的使用者來說幾乎沒有影響,從而實現客戶端和服務器端的松耦合,這樣,我們的表現層,業務邏輯層和數據持久層都可以依據實際需求情況與偏好隨意選擇各種實現方式。
MiniSOA構架介紹
MiniSOA設計思想
MiniSOA是以SOA思想為指導的一個極其輕量級的構架,其主旨是降低服務使用者和提供者之間的耦合度,使現在流行的多種技術能夠很好的并存,并充分發揮各自的優勢,技術人員能夠發揮自己的特長,用自己熟悉的技術來實現業務邏輯,同時應用程序某些層面能夠方便的在不同的實現技術之間進行切換,而不會對其他層面造成影響。MiniSOA沒有創建一個新的編程模式,沒有Web service,EJB那么工業化的龐大,它只是通過XML配置把應用組件裝配在一起,通過必要的緩存,提高組件的運行效率,自動管理組件運行時的事務、環境參數等信息,使其能更有效的運行,同時,提供多小組的團隊開發的支持,并方便進行單元測試,使開發人員集中精力開發業務邏輯,提高勞動生產率。
圖二

MiniSOA概述
為了降低表現層,業務層和數據持久層等各個層面之間的耦合性,各個層面使用其他層面提供的服務時,不是直接去使用服務對象,而是通過服務中介,查找定位服務接口,通過接口來訪問服務,服務提供者可以通過各種方式來實現服務接口,然后把提供的服務注冊到服務中介,服務中介是服務使用者和服務提供者之間進行通訊的橋梁,可以在服務中介上做一些其他方面的控制,如事務,安全等。
表現層作為業務層的使用者,在使用業務層提供的服務時,通過Business Service Manager這個中介來查找和使用服務接口;業務層是表現層的服務提供者,它把通過各種方式實現的服務對象注冊到Business Service Manager,供表現層對象使用,同時業務層也是數據持久層服務的使用者,它通過Data Access Object Manager這個中介來使用數據持久層提供的服務;同樣數據持久層把對數據庫,文件等資源的存取進行封裝,把以各種方式提供的服務對象以接口的形式注冊到Data Access Object Manager,供業務層對象使用。
圖三

MiniSOA包括業務層和數據持久層兩部分組件,業務層的核心組件是ServiceManager, 它是表現層和業務層進行交互的橋梁;數據持久層的核心組件是DaoManager,它是業務層和數據持久層進行交互的橋梁。根據數據模型和業務需求,定義提供的服務接口后,開發人員用自己熟悉的技術開發接口的實現組件,經過單元測試后,把服務接口、實現組件和相應的事務,環境參數等信息配置到dao-conf.xml,service-conf.xml文件,服務使用者就可以通過ServiceManager,DaoManager來查找所需的服務接口,調用服務方法。
MiniSOA 業務層核心組件簡介:
l ServiceFacotry:作為表現層調用業務層的Facade,它讀取service-factory-conf.xml配置文件,創建用于各個模塊的ServiceManager,便于多個開發小組的分工合作。
l Service-factory-conf.xml 實例:
<servie-factory>
<factory>
<service-id>module1</service-id>
<service-file>module1-service-conf.xml</service-file>
</factory>
<factory>
<service-id>module2</service-id>
<service-file>module2-service-conf.xml</service-file>
</factory>
</servie-factory>
通過service-factory-conf.xml配置文件,每個模塊可以使用和本模塊相關的service配置,適合多個開發小組共同開發,有效避免了資源沖突。
l ServiceManager, ServiceManagerBuilder:ServiceManagerBuilder用于讀取serivice-conf.xml配置信息,創建每個service對象使用的環境對象ServiceContext,返回用于服務使用者查找具體服務的ServiceManager接口。
l Service-conf.xml實例:
<service-config>
<settings
cglib-enabled="true"
dao-config="dao-hibernate-conf.xml"
/>
<service singleton="true" runas="java-bean">
<interface>demo.Order</interface>
<java-bean>
<implementation>demo.OrderImpl</implementation>
</java-bean>
<environment>
<name>smtphost</name>
<value>mail.mycom.com</value>
</environment>
<environment>
<name>mailfrom</name>
<value>from@mycom.com</value>
</environment>
<transaction>
<method>sendMail</method>
<trans-automanagement>false</trans-automanagement>
</transaction>
<transaction>
<method>*</method>
<trans-automanagement>true</trans-automanagement>
</transaction>
</service>
<service singleton = "false" >
...
</service>
</service-config>
供使用者使用的每個service分別配置在<service>標簽中,cglib-enable屬性用于配置本模塊的services是否要用cglib來增強Proxy功能,dao-config屬性指定service使用的dao配置信息,runas屬性設置service的實現方式:(java-bean,remote-ejb,corba,webservice),singleton屬性用于配置service是否是單一實例的,如果singleton為true,則此service在第一次創建和使用后,被ServiceManager緩存起來,其他Client再請求此service時,則使用被緩存的對象,而不會再被創建,singleton為true的service是無狀態的,不能保持每個Client的狀態信息,相反,singleton為false的service是有狀態的,每個Client都使用自己的service對象,它們之間互不影響。
l ServiceContext:是ServiceManager和service實現組件之間傳遞信息的橋梁,ServiceContext里保存了每個Service的環境變量的配置,每個方法的事務配置等信息。
l ServiceProxy:是ServiceManager創建service對象的代理對象,用于增強對service的控制。
關于數據持久層的各個組件的功能和業務層的各個組件功能類似,在此不再贅述。
使用MiniSOA進行應用程序開發示例
下面我們模擬銀行轉帳業務,來示例如何使用MiniSOA開發應用程序。
1. 定義用戶接口組件Bank:
public interface Bank {
void transfer(String srcAccountNo,String destAccountNo, float amount );
}
2. 實現用戶接口組件(用Java Bean + DAO)
public class BankImpl extends BaseService implements Bank { //BaseService是MiniSOA提供的服務接口實現組件的父類
public void transfer(String srcAccountNo,String destAccountNo, float amount ) {
try{
//通過ServiceContext得到DaoManager
DaoManager daoManager =
serviceContext.getServiceManager().getDaoManager();
//通過DaoManager得到數據訪問層接口AccountDao
AccountDao accountDao =
(AccountDao) daoManager.getDao(AccountDao.class);
//通過AccountDao接口得到每個賬號對應的Account信息
Account srcAccount = accountDao.findAccountByPK(srcAccountNo);
Account destAccount = accountDao.findAccountByPK(destAccountNo);
//對Account進行加、減帳戶金額的操作
srcAccount.setAmount( srcAccount.getAccount() ? amount);
destAccount.setAmount( destAccount.getAccount() + amount);
//將更新的帳戶信息寫回到數據庫
accountDao.updateAccount(srcAccount);
accountDao.updateAccount(destAccount);
}catch(Exception e) { … …}
}
}
注意對銀行轉帳業務邏輯的實現,必須在同一個事務中進行,否則,將導致數據的不一致,而上面對Bank的transfer方法并沒有進行手工的Transaction的管理,下面在Service的配置中,可以讓MiniSOA自動管理Transaction。
3. 配置service-factory-conf.xml,和本模塊的service-config.xml文件,由MiniSOA自動管理Transaction
service-factory-conf.xml示例:
<servie-factory>
<factory>
<service-id> bankModule </service-id>
<service-file> bankModule-service-conf.xml</service-file>
</factory>
…
</service-factory>
bankModule-service-conf.xml示例:
<service-config>
<settings
cglib-enabled="true"
dao-config="dao-hibernate-conf.xml"
/>
<service singleton="true" runas=”java-bean”>
<interface>demo.Bank</interface>
<java-bean>
<implementation>demo.BankImpl</implementation>
</java-bean>
<transaction>
<method>transfer</method>
<trans-automanagement>true</trans-automanagement>
</transaction>
</service>
</service-config>
4. 寫Client程序,調用Bank service
//先得到srcAccountNo, destAccountNo,amount
……
//得到ServiceFactory
ServiceFactory serviceFactory = ServiceFactory.getInstance();
//通過ServiceFactory得到本模塊的ServiceManager
ServiceManager sm = serviceFactory.getServiceManager(“bankModule”);
//通過ServiceManager得到服務的操作接口Bank
Bank bank = (Bank)sm.getService(Bank.class);
//使用服務
if ( bank != null ) {
bank.transfer(srcAccountNo, destAccountNo, amount);
}
……
對于表現層,只要我們的Bank這個Interface不發生變化,我們可以選擇任何框架技術,無論是基于MVC的Struts,JSF,還是Java Application, Java Applet對業務層都沒有任何影響。
延伸閱讀
文章來源于領測軟件測試網 http://www.kjueaiud.com/