版權聲明:任何獲得Matrix授權的網站,轉載時請務必以超鏈接形式標明文章原始出處和作者信息及本聲明
作者:Palash Ghosh;
kelvincheng
原文地址:
http://www.onjava.com/pub/a/onjava/2005/03/23/components.html
中文地址:
http://www.matrix.org.cn/resource/article/43/43899_Java_Component_Developmen.html
關鍵詞: Java Component Development
我先介紹幾個在構建和設計解決方案來適應商業活動中的持續變化時需要注意的商業場景:
·公司需要將其文件倉庫從文檔文件轉成網絡文件。
·公司需要更換其安全提供商。
·因為經濟情況的巨大的改變,保險公司必須擴展其保險流程政策到更大范圍。
一樣東西是很確定的,需求更改就像商業和技術一樣快速改變。但是對于所有改變,無論其大小,我們都需要拋棄原來整個系統重新開始么?這是不必要的—架構和設計解決方案時加入少許考慮,好的策略以及最優方法可以適應現有的體系結構的變更而不需要太多爭辯。
在面向對象編程以及分布式對象技術中,組件是類和接口的集合,通過可重用的外部API來滿足需求(功能性的以及非功能性的)。組件應該可以在分布式網絡環境運行來形成網絡程序?;诮M件的設計和開發對于遵循面向對象分析與設計(OOAD)的方法學的專家并不是新的話題。
本文的目的是根據java的最優方法和最初開始一步一步地達到通用的概念模型來開發java組件。本文面向的讀者需要具有Java,UML以及Java/J2EE設計模式的知識。這篇文章主要描述的范圍是:
·組件的基本性質。
·如何利用Java設計最優方法(設計模式)來實現這些Java組件設計的基本性質,并且形成一個概念上的基本組件開發框架, 這個框架將來可以方便地用于任何組件開發中的。
組件的基本性質
·為了讓其他組件可以與之相互作用,組件必須有服務接口(API)。
·組件必須有合適的生命周期機制(start, stop, initialize等等)
·組件必須可以配置。
·組件只有一個實例在企業程序中運行。
·配置的改變應該是動態的(在運行中)。
·組件必須有合適的第三方軟件融入的機制。
·組件必須有合適的處理錯誤機制
如何實現基本的組件性質
組件必須有服務接口(API)
無論我們是在一個類還是幾個類中寫100行到1000行的代碼,最終勞動成果(類或者類的結合)提供一些基本的高級的服務。返回去想想,我們甚至可以在實現他們之前定義那些我們想要達到的基本的高級的服務。
讓我們舉個來自保險界的例子,保險商在他每天做了以下的工作:
·檢查保險申請。
·收集并評估背景信息。
·根據公司現有的規則計算保險金
·從其他部門收集信息以及各種各樣的報告(醫學等等)。
·準備相關的政策。
現在我們如果想寫一個保險商的商業組件,我們必須有如圖1的服務接口以及其實現:

Figure 1. Underwriter service interfac
當其他組件請求保險商組件的服務時,并不需要考慮組件內部的操作。封裝其商業邏輯讓組件更易維護及擴展。
服務組件將有一個主要的服務實現類(服務接口的實現)以及這個類使用助手類,這個類是組件的一部分,同時也可能使用其他的組件
在產品開發中,我們也須有許多組件提供不同的服務。例如,在保險業,我們有“索取流程組件”,“投保人服務組件”以及其他更多組件。所以我們必須有個機制來在企業解決方案中注冊這些服務組件,使其可以根據企業的特殊需要采用或者中止這些服務。
下面是XML結構的例子,它可以自動處理服務注冊的流程。
<Services>
<Service>
<Serviceid>S001</Serviceid>
<ServiceName>UnderwriterService</ServiceName>
<ServiceImplClass>
com.org.service.UnderWriterServiceImpl
</ServiceImplClass>
</Service>
<Service>
<ServiceId>S002</ServiceId>
<Servicename>PolicyHolderService</ServiceName>
<ServiceImplClass>
com.org.service.PolicyHolderServiceImpl
</ServiceImplClass>
</Service>
</Services>
組件應該具有合適的生命周期機制
組件也需要一個在它的生命周期內置的,可見的,獨立的機制,所以它可以根據需要被開始和中止。ComponentControllerFactory(組件控制工廠)是singleton,因為其只需要一個實例。這個工廠負責根據配置內容為不同的提供商創建類的實例。ComponentControllerFactory扮演雙重角色:首先其通過其init(),reload()等等方法來管理組件的生命周期(這就是為什么它是一個“工廠”),圖2顯示其方法

Figure 2. Component controller factory
組件的生命周期方法是:
·doStart(): 開始組件
():幫助其從XML配置文件中取得配置對象,負責創建適當的類的實例
·doStop():停止組件
·reload():如果當組件已經開始但XML配置文件發生更改,這個方法將重新讀取XML配置文件并重啟逐漸。
·getInstance():返回ComponentControllerFactory類的實例
一個組件應該是可配置的
通常,每個組件有自己的可配置的不經常改變的參數。例如,假設我們需要寫一個緩存組件,它需要每小時從數據庫取得半靜態的數據來刷新本身狀態。更新的時間應該在配置文件中設置,那樣我們可以不更改源代碼來更改參數的值。
下面是關于logger組件的XML配置文件的例子,專用于管理企業程序各個層次的logging。
<LoggingServiceProvider>
<Provider>
<ProviderName>Apache</ProviderName>
<AdapterImpl>com.org.integration.adapter.Log4jAdapter
</AdapterImpl>
<Enable>true</Enable>
</Provider>
<Provider>
<ProviderName>WebLogic</ProviderName>
<AdapterImpl>com.org.integration.adapter.WebLogicAdapter
</AdapterImpl>
<Enable>false</Enable>
</Provider>
</LoggingServiceProvider>
在企業應用中組件只有一個實例在運行
一個組件應該有且只有一個實例在運行,而Singleton設計模式是合適的選擇來保證在JVM中只有一個實例。但是當這種模式在單一JVM情形下可行,但是在多JVM情形下就有問題。但是由于配置信息在組件開始時載入而不需要改變并處理所有靜態信息,用Singleton設計模式依然可行
我們假設組件可以在單JVM情況下可行,ComponentControllerFactory將會如圖3那樣:

Figure 3. Component controller factory in a single JVM
Singleton控制工廠提供的方法是
·getXXXService():方法返回在XML文件中定義的服務提供的實現類
·getXXXAdapter():方法返回在XML文件中定義適配實現類
配置文件的更改應該是動態的
如果組件是不可變的,每串代碼應該有與singleton實例同樣的拷貝,但是如果它是不是不變得,我們需要改變時,配置文件需要動態改變。
有兩種可能的情況但動態配置文件更改:
·單一JVM情況
·多JVM情況
單一JVM情況
如果程序在單一JVM中運行,事情就簡單得多了。我們已經知道,SingletonControllerFactory通常在JVM中有一個實例,所以任何時候配置文件發生任何改變,將需要根據一些通知機制輪流載入java串行的配置對象來重新載入工廠對象。這是基于Observer-Observable模式并做兩件事:
·通過XMLizer(單獨的組件)來讀取和處理XML配置文件并載入Java配置對象。
·監視XML配置文件可能發生的更改。
圖4顯示ConfigManager的方法:

Figure 4. ConfigManager
ConfigManager類當被Observable通知時扮演Observer角色,其更新方法將會被調用。Update()方法將會調用SingletonControllerFactory的reload()方法,所以新創建的java對象將會從其配置信息中重新載入。
ConfigurationChangeNotifier扮演Observable的角色并在XML配置文件發生更改時啟動通知ConfigManger線程,并將指出其內容上的改變。圖5顯示其關系:

Figure 5. ConfigurationChangeNotifier
多JVM情況
在多JVM情況下,事情就不會變得這樣簡單。我們必須有
·需要機制在運行時來動態載入更改的XML配置文件而不關閉整個企業程序。
·需要機制保證在群中只有一個實例在運行。
結合RMI利用JNDI是一種選擇來保證在集群環境中的多個節點中的特定的一個節點自由一個實例在運行。RMI服務需要編寫,同時RMI stub要在RMI服務之外創建。創建的RMI stub需要被綁定在程序服務器的JNDI樹上。這個對象將保持在container中,container可以讓對象在集群中都可以用到。
為了處理這種情況,我們需要引入ConfigManager,它將會做一下任務:
·創建需要可以動態改變的XML配置文件。
·創建來自XML文件的java串行文件。串行和非串行化將會在不同的組件中完成。
·創建RMI服務,注冊從RMI服務中創建的RMI stub,并通過RMI服務載入串行配置對象。
·將RMI stub與集群環境中的JNDI樹的任何節點綁定。
·創建通知系統,其將重新綁定RMI服務并當XML文件似乎發生變化時重新載入對象。
圖6顯示RMI服務的接口以及其實現:

Figure 6. RMI service
在多JVM情況下,ConfigManager如圖7:

Figure 7. ConfigManager in a multiple-JVM scenario
ConfigManagerMultipleJVM 類扮演Observer的角色。當他被Observable通知時,其update方法將會被調用。通過update()方法,rebindRMIService()方法將會被調用,這樣新創建的對象(通過最新的配置信息)將會被重新載入。
SingletonControllFactory將會為RMI服務扮演wrapper角色,返回合適的已配置的對象。
這種方法的會產生問題,因為只有一個實例,所以只可以允許一個點的錯誤。ConfigManager組件需要更強壯來處理錯誤。
但是同樣有其他的方法,通過MDB和JMS在群眾的不同節點同步緩存的配置對象。在這種情況下,并不需要RMI服務。下面是實現這種方法的步驟:
·SingletonControllerFactory通過配置對象初始化并開始組件。
·ConfigManager的Observer-Observable模型通過其通知機制來跟蹤XML配置文件的任何變更。當發現更改時,他將公布消息到JMS topic。
·運行在集群環境中的每個群中的MDB觸發其onMessage()方法,并載入更改的配置Java對象。
組件應該有合適的第三方軟件整合機制
如果組件依賴第三方軟件整合來建立服務,第三方API不應該直接在實現類中使用。最佳的策略是開發適配器并隔離第三方軟件調用和適配器的實現。
圖8顯示調用logger組件的適配器的例子,演示了如何讓其更方便的適應第三方APIs。

Figure 8. Application logger interface
利用adapter模式的優點是更容易的和第三方軟件APIs合并。此外,當這些APIs改變時,適配實現需要改變,而用此適配接口的服務將不需要改變。
通過XML配置文件從不同的適配器中選擇是便利的,就如上面這節介紹的那樣。
組件應該有合適的錯誤處理機制
每個組件應該有自己的異常處理類,它可以幫助捕捉適當的異常。假設我們對于特定的即將使用的商業程序有單獨的組件來處理異常。這個特定組件異常類(Underwriter exception)將會使需要的服務脫離異常處理組件。

Figure 9. Component exception handling
這個異常處理類是特定用于Underwriter服務并擴展基于企業程序的異常類。其工作就是掩蓋在服務類中產生的異常并重新釋放他。
結論
總的來說,以下是整合的基本步驟:
·作為程序開始過程的一部分,ConfigManager鍵通過XMLizer(用于XML-to-java對象轉換的單獨對象)來為不同的組件讀取XML配置文件,并通過程序服務器節點的JNDI tree來綁定Java配置對象。
·作為程序開始過程的一部,配置對象將會被讀取,因此相關的provider/adapter/service需要被說明。
·如果配置文件發生更改,ConfigManager將讀取更改后的XML文件并重新綁定配置對象。
·組件將會重新載入配置對象并根據其最新更改來重新初始化。
回到我們開始的地方,當你計劃開發強壯的系統時組件框架將會有效地適應商業和技術上的改變。概念框架的最佳部分是通過引入不同的即插即用的服務提供商的概念,完全將組件管理/生命周期進程與商業邏輯和不同的第三方APIs隔離。即使發生改變,除了更改/替代服務提供商,你也不需要擔心代碼的其他部分。這樣可以使程序更易維護,更易適應,和更強壯。
作者:
Palash Ghosh是有6年架構,設計和開發Java/J2EE解決方案專家經驗的軟件架構師。