天天看點

運用J2EE建立靈活易擴充的企業應用程式探讨

引言

随着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的構架

運用J2EE建立靈活易擴充的企業應用程式探讨

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概述

運用J2EE建立靈活易擴充的企業應用程式探讨

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

運用J2EE建立靈活易擴充的企業應用程式探讨

MiniSOA包括業務層和資料持久層兩部分元件,業務層的核心元件是ServiceManager, 它是表現層和業務層進行互動的橋梁;資料持久層的核心元件是DaoManager,它是業務層和資料持久層進行互動的橋梁。根據資料模型和業務需求,定義提供的服務接口後,開發人員用自己熟悉的技術開發接口的實作元件,經過單元測試後,把服務接口、實作元件和相應的事務,環境參數等資訊配置到dao-conf.xml,service-conf.xml檔案,服務使用者就可以通過ServiceManager,DaoManager來查找所需的服務接口,調用服務方法。

MiniSOA 業務層核心元件簡介

ServiceFacotry:作為表現層調用業務層的Facade,它讀取service-factory-conf.xml配置檔案,建立用于各個子產品的ServiceManager,便于多個開發小組的分工合作。

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配置,适合多個開發小組共同開發,有效避免了資源沖突。

ServiceManager,ServiceManagerBuilder:ServiceManagerBuilder用于讀取serivice-conf.xml配置資訊,建立每個service對象使用的環境對象ServiceContext,傳回用于服務使用者查找具體服務的ServiceManager接口。

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>
		
			<environment>
       			<name>smtphost</name>
          			<value>mail.mycom.com</value>
       		</environment>
       		<environment>
       			<name>mailfrom</name>
       			<value>[email protected]</value>
       		</environment>	
       		<transaction>
       			<method>sendMail</method>
          		<trans-automanagement>false</trans-automanagement>
         	</transaction>
         	<transaction>
         		<method>*</method>
           		<trans-automanagement>true</trans-automanagement>
         	</transaction>
		</java-bean>
	</service>

	<service singleton = "true"  runas="remote-ejb">
		<interface>demo.Bank</interface>
		<remote-ejb>
          		<jndi-name>bankEJB</jndi-name>
          		<home>demo.ejb.BankHome</home>
          		<remote>demo.ejb.Bank</remote>
	   	</remote-ejb>
	</service>
	<service>
    ......
	</service>
</service-config>
      

供使用者使用的每個service分别配置在 标簽中,cglib-enable屬性用于配置本子產品的services是否要用cglib來增強Proxy功能;dao-config屬性指定services使用的dao配置資訊;runas屬性設定此service的實作方式:java-bean | remote-ejb | webservice,每個service可以通過不同的方式來實作,但Service對外的接口interface隻有一個;singleton屬性用于配置service是否是單一執行個體的,如果singleton為true,則此service在第一次建立和使用後,被ServiceManager緩存起來,其他Client再請求此service時,則使用被緩存的對象,而不會再被建立,singleton為true的service是無狀态的,不能保持每個Client的狀态資訊,相反,singleton為false的service是有狀态的,每個Client都使用自己的service對象,它們之間互不影響;interface标簽指定service的接口,供Client查找和使用,environment标簽指定service使用的環境資訊,transaction标簽指定service方法使用的transaction。

ServiceContext:是ServiceManager和service實作元件之間傳遞資訊的橋梁,ServiceContext裡儲存了每個Service的環境變量的配置,每個方法的事務配置等資訊。

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>
		
			<transaction>
       			<method>transfer</method>
          		<trans-automanagement>true</trans-automanagement>
			</transaction>
		</java-bean>
	</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對業務層都沒有任何影響。

總結

以上初步介紹了我的MiniSOA構架的設計思想、核心元件以及使用MiniSOA開發的簡單示例,使用MiniSOA我們可以很簡單地建立靈活,具有易擴充性且不依賴于具體實作技術的應用程式,能夠很好地內建各種流行的技術,在充分發揮各種技術優勢的同時,最大限度地釋放出開發人員的技術優勢,同時有利于小組間的分工與合作,提高軟體開發效率。

對MiniSOA有興趣的讀者可以向筆者索要免費的MiniSOA運作庫和技術支援,希望與您共同探讨可重用的軟體構架技術。