posts - 40,  comments - 7,  trackbacks - 0
          Avalon的簡要?dú)v史以及創(chuàng)建它所有的設(shè)計原則概述

          事情是從Apache JServ項(xiàng)目開始的。Stefano Mazzocchi和其它協(xié)助開發(fā)Apache JServ的人員認(rèn)識到項(xiàng)目中所用到的一些模式很通用,足以用于創(chuàng)建一個服務(wù)器框架。 在1999年1月27日,星期三(在JServ 1.0b發(fā)布大約一個月后),Stefano拿出一份建議書,建議啟動一個名為Java Apache Server Framework的項(xiàng)目。它的目標(biāo)是成為Apache所有Java服務(wù)器代碼的基礎(chǔ)。想法是通過提供一個框架,將跨項(xiàng)目的一些組件和重用代碼集中在一起。

          Stefano Mazzocchi,F(xiàn)ederico Barbieri和Pierpaolo Fumagalli創(chuàng)建了最初的版本。在2000年末,Berin Loritsch和Peter Donald參加到項(xiàng)目中來。那時,Pierpaolo和Stefano已轉(zhuǎn)向其它項(xiàng)目的開發(fā),Java Apache Server Framework開始被稱為Avalon。這五個開發(fā)者是框架目前版本所使用的設(shè)計和概念的主要負(fù)責(zé)人。當(dāng)前版本與2000年6月發(fā)行的版本非常相似。實(shí)際上,主要的區(qū)別是對包重新組織,以及將項(xiàng)目劃分為子項(xiàng)目。同樣的設(shè)計模式和接口今天依然存在。
          分解一個系統(tǒng)

          您是如何決定由哪些東西組成一個組件的? 關(guān)鍵是定義您的解決方案所需的設(shè)施,以便能有效率地進(jìn)行操作。

          我們將使用一個假想的業(yè)務(wù)服務(wù)器來展示如何識別和確定服務(wù)與組件。在我們定義了一些系統(tǒng)用到的服務(wù)之后,我們將以這些服務(wù)中的一個為例,定義該服務(wù)所需的不同組件。我的目標(biāo)是傳遞給您一些概念,這些概念有助于您把您的系統(tǒng)定義成可管理的部分。


          系統(tǒng)分析——識別組件

          盡管提供一個完整全面的方法學(xué)不是本篇的范圍,我還是愿意討論幾點(diǎn)問題。我們將從組件和服務(wù)的面向?qū)崿F(xiàn)的定義開始,然后提供一個實(shí)踐的定義。

          組件

          一個組件是一個工作接口和該工作接口的實(shí)現(xiàn)的組合。使用組件提供了對象間的松耦合,允許改變實(shí)現(xiàn)而不影響使用它的代碼。

          服務(wù)

          一個服務(wù)由一個或更多的組件組成,提供了一種完整的解決方案。服務(wù)的例子包括協(xié)議處理器、任務(wù)調(diào)度器、認(rèn)證和授權(quán)服務(wù)等等。

          盡管這些定義提供了一個起點(diǎn),但它并沒有提供一個完整圖景。為了把一個系統(tǒng)(定義為一組設(shè)施,組成一個項(xiàng)目)分解為必要的組成部分,我建使用自頂向下的方式。采用這種方式可以在您了解到底有哪些設(shè)施之前,避免陷入到細(xì)節(jié)的泥潭中。

          確定您的項(xiàng)目范圍

          您的項(xiàng)目預(yù)期完成什么功能,一開始您總要有個大致的想法。在商業(yè)世界里,初始工作說明(initial statement of work )就是完成這項(xiàng)工作的。在開放源代碼的世界里,這通常是由一個想法或一個腦力激蕩的過程完成的。我想不論如何強(qiáng)調(diào)具備一個項(xiàng)目的高層視圖的重要性都是不過份的。

          很明顯,一個大項(xiàng)目將由許多不同的服務(wù)組成,而小項(xiàng)目只有一兩個服務(wù)。如果您開始感到有點(diǎn)不知所措,只需提醒自己大項(xiàng)目實(shí)際上是一把大傘下的許多小項(xiàng)目。最終,您將能理解整個系統(tǒng)大的圖景。



          工作說明:業(yè)務(wù)服務(wù)器

          業(yè)務(wù)服務(wù)器(Business Server)是一個假想的項(xiàng)目。出于我們討論問題的目的,它的功能是處理銷售訂單,自動向客戶發(fā)出賬單,并管理存貨控制。銷售訂單到達(dá)時必須得到處理,通過某種類型的事務(wù)系統(tǒng)。在銷售訂單填寫30天后,服務(wù)器自動向客戶發(fā)出賬單。庫存由服務(wù)器和工廠或倉庫的當(dāng)前庫存量同時來管理。該業(yè)務(wù)服務(wù)器將是一個分布式系統(tǒng),每個服務(wù)器將通個一個消息服務(wù)來與其它服務(wù)器通信。


          發(fā)現(xiàn)服務(wù)

          我們將使用這個Business Server項(xiàng)目來發(fā)現(xiàn)服務(wù)。考慮上面的過于概括性的工作說明,我們立即可以看到項(xiàng)目描述中定義的一些服務(wù)。服務(wù)的清單可被分成兩大類:顯式的服務(wù)(可以從工作說明中直接導(dǎo)出的服務(wù))和隱式的服務(wù)(根據(jù)相似的工作發(fā)現(xiàn)的服務(wù),或?qū)︼@示服務(wù)起支持作用的服務(wù))。請注意,實(shí)現(xiàn)系統(tǒng)的公司不必親自開發(fā)所有的服務(wù)——有一些可作為商業(yè)解決方案購買。在那些情況下,我們可能會開發(fā)一個封裝層(wrapper),以便我們能夠以確定的方式與商業(yè)產(chǎn)品實(shí)現(xiàn)互操作。實(shí)現(xiàn)系統(tǒng)的公司將構(gòu)建大部分的服務(wù)。

          顯式的服務(wù)

          從工作說明中,我們可以快速導(dǎo)出一些服務(wù)。但這種初始分析并不意味我們的工作已完成,因?yàn)槟承┓?wù)的定義需要其它服務(wù)的存在才能保證。

          事務(wù)處理服務(wù)

          工作說明明確指出“銷售訂單到達(dá)時必須得到處理 ”。這表明我們需要有一種機(jī)制能夠接受銷售請求并自動地處理它們。這與web服務(wù)器工作的方式相似。它們接收到對資源的請求,進(jìn)行處理并返回一個結(jié)果(如HTML頁面)。這被稱作是事務(wù)處理。

          完整地說起來,存在不同類型的事務(wù)處理。這種一般性的事務(wù)處理服務(wù)極有可能必須分解為一些更特殊的東西,如“銷售訂單處理器“。具體方法取決于您的服務(wù)的通用性。在可用性和可重用性之間存在一種平衡。服務(wù)越是通用,它就越可重用。通常它也就越難于理解。


          調(diào)度服務(wù)

          在某些情況下,當(dāng)事務(wù)完成,過了一段特定的時間之后,必須調(diào)度某個事件。而且,庫存控制過程必需能周期性地開出采購訂單。因?yàn)楣ぷ髡f明中指出“ 在銷售訂單填寫30天后,服務(wù)器自動向客戶發(fā)出賬單” ,所以我們需要一個調(diào)度服務(wù)。所幸的是Avalon Cornerstone為我們提供了一個,這樣我們就不必再自己寫一個了。


          消息服務(wù)

          工作說明中指出,在我們的分布式系統(tǒng)中“每個服務(wù)器將通個一個消息服務(wù)來與其它服務(wù)器通信“。讓我們來考慮這個問題,有時用戶需要一個特定的產(chǎn)品或一種他們想用的方法。消息服務(wù)是利用其它公司產(chǎn)品的一個首選例子。極有可能,我們將采用Java Messaging Service (JMS) 來作為Messaging Service的接口。因?yàn)镴MS是一個標(biāo)準(zhǔn),它的接口不大可能短期內(nèi)發(fā)生變化。

          從實(shí)踐經(jīng)驗(yàn)上來說,一個定義良好的面向消息的系統(tǒng)在可擴(kuò)展性方面要強(qiáng)于面向?qū)ο蟮南到y(tǒng)(如EJB)。可擴(kuò)展性更好的一個原因是消息通常并發(fā)內(nèi)存開銷較小。另一個原因是它更容易把消息處理的負(fù)載分散到所有服務(wù)器上去,而不是把所有的處理集中在少量的服務(wù)器集群上(甚至是在一臺服務(wù)器上)。


          庫存控制服務(wù)

          盡管這不是一個教科書上的經(jīng)典服務(wù),但它是這個系統(tǒng)的一項(xiàng)需求。庫存控制服務(wù)固定地監(jiān)視工廠或倉庫存貨的記錄,當(dāng)存貨開始不足時觸發(fā)一些事件。



          隱式的服務(wù)

          運(yùn)用在過去系統(tǒng)中獲得的經(jīng)驗(yàn),將系統(tǒng)更進(jìn)一步分解出其它服務(wù),將得到?jīng)]有明確指出但又是系統(tǒng)需要的一些服務(wù)。因?yàn)槠P(guān)系,我們將不做全面地分解。

          認(rèn)證和授權(quán)服務(wù)

          認(rèn)證和授權(quán)服務(wù)沒有在工作說明中明確地提到,但是所有的業(yè)務(wù)系統(tǒng)必須認(rèn)真考慮安全性。這意味著系統(tǒng)所有的客戶端都必須經(jīng)過認(rèn)證,用戶的所有行為都必須經(jīng)過授權(quán)。


          工作流自動化服務(wù)

          工作流自動化是企業(yè)系統(tǒng)中的一個熱門開發(fā)領(lǐng)域。如果您不使用第三方的工作流管理服務(wù)器,您就需要自己寫一個。通常工作流自動化所做的是使用軟件系統(tǒng)來安排貫穿公司業(yè)務(wù)過程的任務(wù)。更多的信息請參考Workflow Management Council的網(wǎng)站http://www.wfmc.org/


          文檔中心服務(wù)

          作為一個任務(wù)的當(dāng)前狀態(tài)信息,"文檔中心"這個詞的定義很不精確。換言之,當(dāng)公司接到一份購買訂單時,我們的系統(tǒng)需要能夠存儲并重新調(diào)出購買訂單信息。出賬單和系統(tǒng)中其它任何過程,從庫存到新的用戶請求都有同樣的需求。



          小結(jié)

          我希望Business Server項(xiàng)目的服務(wù)的例子可以幫助您發(fā)現(xiàn)更多。您會發(fā)現(xiàn),當(dāng)您從較高的抽象層逐漸轉(zhuǎn)向較低的抽象層時,會發(fā)現(xiàn)需要更多類型的服務(wù),如用于在一個打開端口上處理請求的連接服務(wù)。我們定義的某些服務(wù)將通過第三方的系統(tǒng)來實(shí)現(xiàn),如消息服務(wù)和工作流管理服務(wù)。對這些服務(wù)來說,使用一個標(biāo)準(zhǔn)接口是最符合您的利益的,這樣您可以在以后更換供應(yīng)商。有些服務(wù)實(shí)際上是由多個服務(wù)組成的大服務(wù)。有些服務(wù)Avalon Excalibur或Avalon Cornerstone中已經(jīng)提供了。

          在發(fā)現(xiàn)一個系統(tǒng)中的服務(wù)時,應(yīng)該牢記的一件事是:一個服務(wù)應(yīng)該是一個高層子系統(tǒng)。這將有助于您通過分析師團(tuán)隊(duì)來定義組件。因?yàn)槲覀円炎R別出了主要的服務(wù),您可以讓多個個人(或團(tuán)隊(duì))并行地分解每個服務(wù)。子系統(tǒng)邊界也定義良好,發(fā)生重疊的可能性很小。如果您決定進(jìn)行并行分析,您應(yīng)該回過頭來識別通用的組件,以便能夠盡可能地重用。

          UML Diagram for the Business Server
          Berin Loritsch, 2001
          • Berin Loritsch, 2001


          發(fā)現(xiàn)組件

          我們將以前面提到的文檔中心服務(wù)為例來說明識別合適的組件的過程。為討論方便起見,我們現(xiàn)在來列出文檔中心服務(wù)的需求。文檔中心將采用一個數(shù)據(jù)庫來作為持久存儲,對客戶端授權(quán),在內(nèi)存中緩存文檔。

          組件的實(shí)踐性定義

          當(dāng)我們談?wù)摻M件時,您考慮問題的角度應(yīng)該是:我的服務(wù)需要操作哪些設(shè)施?Avalon相信將系統(tǒng)投影(cast)的概念。系統(tǒng)的開發(fā)者會面對一個組件的職責(zé)列表,組件則被稱為它的角色(role)。

          什么是角色?

          角色的概念來自劇院。一部戲劇、音樂劇或電影片都會有一定數(shù)量的角色,由演員來扮演。盡管演員似乎從不短缺,角色的數(shù)量卻是有限的。演出的腳本 定義了角色的功能或行為。如同劇院里發(fā)生的一樣,腳本決定了您如何與組件交互。考慮系統(tǒng)中的不同角色,您會將組件的投影為角色,并與之對話。

          一個角色是一類組件的契約。例如,我們的文檔中心服務(wù)需要操作數(shù)據(jù)庫。Avalon Excalibur定義了一個組件,符合"Data Source"腳色的需要。在Excalibur中有兩個不同的組件,都能滿足該角色的需要。 具體使用哪一個取決于服務(wù)所處的環(huán)境,但是它們都滿足相同的契約。大量基于Avalon的系統(tǒng)對每個角色將只用到一個活動的組件。腳本就是工作接口:其它組件與之交互的接口。

          在確定組件的接口時,必須有確定的契約并牢記在心。契約規(guī)定了組件的使用者必須提供什么,以及組件生產(chǎn)出什么。有時在契約中必須包括使用語義。一個例子是臨時存儲組件和持久存儲組件之間的區(qū)別。當(dāng)接口和協(xié)議定義好之后,您就可以致力于實(shí)現(xiàn)。



          怎樣算是一個好的候選組件?

          在我們的文檔中心服務(wù)中,我們已識別了四個可能的組件:DataSourceComponent (來自Excalibur)、 Cache、Repository、Guardian。您應(yīng)該尋求那些很可能有多種實(shí)現(xiàn)的角色,與這些實(shí)現(xiàn)的交互可以無縫地進(jìn)行。

          通個這個例子,您會發(fā)現(xiàn)一些情況下您需要使用可替換的設(shè)施。大多數(shù)情況下,您只會用到該設(shè)施的一種實(shí)現(xiàn),但您需要能獨(dú)立地升級它而不影響到系統(tǒng)的其它部分。其它情況下,您需要根據(jù)環(huán)境的不同使用不同的實(shí)現(xiàn)。例如,Excaliber定義的"Data Source"通常會自己處理所有的JDBC連接池,但有時你可能希望利用Java 2 Enterprise Edition(J2EE)中提供的設(shè)施。Excalibur解決這個問題的辦法是,一個"Data Source"組件直接管理JDBC連接和池,另一個組件使用Java's Naming and Directory Interface (JNDI) 來得到特定的連接。


          怎樣不算是一個好的組件?

          習(xí)慣于使用JavaBeans的人喜歡把所有的東西都實(shí)現(xiàn)為一個JavaBean。這意味著從數(shù)據(jù)模型到事務(wù)處理的一切東西。如果您用這種方式來處理組件,您可能會得到一個過于復(fù)雜的系統(tǒng)。把組件視為一個服務(wù)或設(shè)施的模型,而不是數(shù)據(jù)的模型。您可以有從其它資源拉數(shù)據(jù)的組件,但數(shù)據(jù)還是應(yīng)該保持為數(shù)據(jù)。在Avalon Excalibur中這種哲學(xué)的一個例子是連接(Connection)不是一個組件。

          另一個例子是我們前面提到的Guardian組件。可能存在的爭議是,Guardian所包含的邏輯與文檔中心服務(wù)太相關(guān),不能做為一個組件用在完全不同的服務(wù)中。盡管管理復(fù)雜性有多種方式,也有多種方式讓它變得靈活,但有時為它付出額外的工作并不值得。在這種情況下,您必仔細(xì)權(quán)衡您的決定。如果一個潛在組件的邏輯將被一致地應(yīng)用,那么將它作為一個組件可能是有意義的。在一個系統(tǒng)中可以有一個組件的多個實(shí)例,它們可以在運(yùn)行時進(jìn)行選擇。如果潛在組件的邏輯只是根據(jù)另外一個組件來確定的,也許可以把這些邏輯放到另外的那個組件中去。通過Guardian組件和Repository組件的例子,我們可以辯稱Guardian太專注于Repository,不是作為一個組件來實(shí)現(xiàn)的。


          分解文檔中心服務(wù)

          我們將列出將要實(shí)現(xiàn)的組件,以及它們的角色、根本原因和來源(如果組件已經(jīng)存在的話)。

          DocumentRepository

          DocumentRepository是整個服務(wù)的父組件。在Avalon中,服務(wù)實(shí)現(xiàn)為Block,Block是一個特定類型的組件。Block必須有一個工作接口,擴(kuò)展了Service marker接口。Block接口也擴(kuò)展了Avalon的Component接口。請注意,Block和Service是包含在Avalon Phoenix中的接口。最后,Service從技術(shù)上說仍是一種特定類型的Component。

          DocumentRepository是我們從持久存儲中取得Document對象的方法。它與服務(wù)中的其它組件交互,以提供安全性、功能性和速度。這個特定的DocumentRepository會與數(shù)據(jù)庫連接,在內(nèi)部使用數(shù)據(jù)庫的邏輯來建造Document對象。


          DataSourceComponent

          DataSourceComponent由Avalon Excalibur提供。它是我們獲得有效的JDBC連接對象的方式。


          Cache

          Cache是一個短期內(nèi)存中的存儲設(shè)施。DocumentRepository將用它來保存Document對象,并通過一個散列算法來引用。為了提高Cache組件的可重用性,存儲的對象必須實(shí)現(xiàn)一個Cacheable接口。


          Guardian

          Guardian組件的作用是基于參與者管理許可。Guardian將從數(shù)據(jù)庫中裝入許可規(guī)則集。Guardian將使用標(biāo)準(zhǔn)Java安全模型,以保證對特定Document的訪問。



          小結(jié)

          到目前為止,您應(yīng)該對怎樣才算是一個好組件有一些認(rèn)識了。例子描述了在文檔中心服務(wù)中的所有組件,簡要介紹了它們將完成的工作。快速瀏覽這個列表,它體現(xiàn)了將設(shè)施實(shí)現(xiàn)為組件而不是數(shù)據(jù)的方法。到目前為止,你應(yīng)該能夠確定您的服務(wù)需要操作什么組件。

          框架和基礎(chǔ)

          我們將描述Avalon的契約和接口,為我們實(shí)際編寫組件打下基礎(chǔ)。

          Avalon Framework是整個Avalon項(xiàng)目的中心部分。如果您理解了框架所定義的契約和結(jié)構(gòu),您就能理解任何利用該框架的代碼。請記住我們已討論過的原理和模式。在本部分中,我們將詳細(xì)解釋角色的概念在實(shí)踐中是怎樣起作用的,組件的生命周期以及接口是如何工作的。


          定義組件的角色

          在Avalon中,所有的組件都扮演一個角色。原因是您通過角色來獲取您的組件。在這個舞臺上,我們唯一要考慮的是角色的簽名。回顧一下第二部分,我們把組件定義為"一個工作接口和該工作接口的實(shí)現(xiàn)的組合"。工作接口就是角色。

          創(chuàng)建角色的接口

          下面您將看到一個接口的例子,以及一些最佳的實(shí)踐和原因。

          package org.apache.bizserver.docs;
          
          public interface DocumentRepository extends Component
          {
              String ROLE = DocumentRepository.class.getName();
          
              Document getDocument(Principal requestor, int refId);
          }
          
                
          最佳實(shí)踐

          • 包含一個名為"ROLE"的字符串,這是角色的正式名字。該名字與該工作接口的完全限定名稱是一樣的。這在今后我們需要得到一個組件的實(shí)例時會有幫助。

          • 如果有可能,請擴(kuò)展組件接口。這會使您在發(fā)布組件時變得更容易。如果您不負(fù)責(zé)控制該工作接口,那么這點(diǎn)對你無用。問題也不會太大,因?yàn)槟诎l(fā)布時總可以將其強(qiáng)制轉(zhuǎn)換為Component 的實(shí)例。

          • 做一件事并把它做好。組件的接口應(yīng)該盡可能地簡單。如果您的工作接口擴(kuò)展了其它一些接口,就會把組件的契約給搞得難以理解。一個老的美國首字母縮寫對這一點(diǎn)表述得很好:Keep It Simple, Stupid (KISS)。比自己更聰明(犯傻)并不難,我自己就干過幾次。

          • 只確定您需要的方法。客戶程序應(yīng)該不知道任何實(shí)現(xiàn)細(xì)節(jié),太多的可替換方法只會帶來不必要的復(fù)雜性。換言之,選擇一種方式并堅持不變。

          • 別讓您的角色接口擴(kuò)展任何生命周期或生存方式的接口。如果實(shí)現(xiàn)任何一個這樣的類或接口,您就是在試圖實(shí)現(xiàn)規(guī)范。這不是一個好模式,只會在將來帶來調(diào)試和實(shí)現(xiàn)問題。


          選擇角色名稱

          在Avalon中,每個角色都有一個名稱。它是您取得系統(tǒng)中其它組件引用的方式。Avalon開發(fā)團(tuán)隊(duì)已經(jīng)概括了一些對角色命名的習(xí)慣方式。

          命名習(xí)慣方式

          • 工作接口的完整限定名通常就是角色名。例外情況列在本通用規(guī)則的下面。在這個例子里,我們理論上的組件名稱應(yīng)該是"org.apache.bizserver.docs.DocumentRepository"。這就是應(yīng)該包含在您的接口的"ROLE"屬性里的名字。

          • 如果我們通過一個組件選擇器得到了該組件的引用,我們通常使用從第一條規(guī)則推導(dǎo)出的角色名稱,在末尾加上單詞"Selector"。這條命名規(guī)則的結(jié)果將是"org.apache.bizserver.docs.DocumentRepositorySelector"。您可以通過DocumentRepository.ROLE + "Selector"來得到這個名稱。

          • 如果我們有多個組件實(shí)現(xiàn)相同的工作接口,但用于不同目的,我們將分離角色。一個角色是組件在一個系統(tǒng)中的目的。每個角色名都將以最初的角色名開頭,但表示角色目的的名字會以/${purpose}的形式附在后面。例如,對DocumentRePository我們可以有如下的目的: PurchaseOrder(購買訂單)和Bill(賬單)。這兩個角色可被分別表述為DocumentRepository.ROLE + "/PurchaseOrder"DocuementRepository.ROLE + "/Bill"





          Framework接口概述

          整個Avalon Framework可以被分成七個主要類別(根據(jù)API): Activity, Component, Configuration, Context, Logger, Parameters, Thread, and Miscellany。每一類(Miscellany除外)表示了一個考慮方向(concern area)。一個組件通常實(shí)現(xiàn)幾個接口來標(biāo)明它所關(guān)心的考慮方向。這使組件的容器能以一致的方式來管理每個組件。

          Avalon接口的生命周期

          當(dāng)一個框架實(shí)現(xiàn)了多個接口以分開考慮組件的各個方面時,存在搞不清方法調(diào)用次序的潛在可能性。Avalon Framework意識到了這一點(diǎn),因此我們開發(fā)了事件生命周期次序的協(xié)定。如果您的組件不實(shí)現(xiàn)相關(guān)的接口,就簡單跳到下一個要處理的事件。因?yàn)榇嬖谝粋€正確的創(chuàng)建和準(zhǔn)備組件的方法,您可以在接收到事件時設(shè)置好組件。

          組件的生命周期被分成三個階段:初始化階段、活動服務(wù)階段和銷毀階段。因?yàn)檫@些階段是依次發(fā)生的,我們將依次討論這些事件。另個,因?yàn)镴ava語言的原因,Construction和Finalization的行為是隱含進(jìn)行的,所以跳過不談。我們將列出方法名和所需的接口。在每個階段中,會有一些由方法名標(biāo)識的步驟。如果組件擴(kuò)展了括號中指定的接口,這些步驟會依次執(zhí)行。

          初始化階段

          以下的步驟依次發(fā)生,在組件生存期中只發(fā)生一次。

          1. enableLogging() [LogEnabled]

          2. contextualize() [Contextualizable]

          3. compose() [Composable]

          4. configure() [Configurable] orparameterize() [Parameterizable]

          5. initialize() [Initializable]

          6. start() [Startable]


          活動服務(wù)階段

          以下的步驟依次發(fā)生,但在組件的生存期中可能發(fā)生多次。請注意,如果您選擇不實(shí)現(xiàn)Suspendable接口,那么您的組件有責(zé)任在執(zhí)行任何re開頭的步驟時保證正確的功能。

          1. suspend() [Suspendable]

          2. recontextualize() [Recontextualizable]

          3. recompose() [Recomposable]

          4. reconfigure() [Reconfigurable]

          5. resume() [Suspendable]


          銷毀階段

          以下的步驟依次發(fā)生,在組件生存期中只發(fā)生一次。

          1. stop() [Startable]

          2. dispose() [Disposable]



          Avalon Framework契約

          在本部分中,我們將按字母次序介紹所有內(nèi)容,除了最重要的部分:Component,我們把它放在最前面。

          當(dāng)我使用"容器"或"容納"來描述組件時,我是有特殊含義的。我是指那些已經(jīng)由父組件實(shí)例化并控制的子組件。我不是指通過ComponentManager或ComponentSelector得到的組件。更進(jìn)一步,容器組件所接收到的一些Avalon步驟執(zhí)行命令必須傳播到它的所有子組件,只要這些子組件實(shí)現(xiàn)了相應(yīng)的接口。特定的接口是指Initializable、Startable、Suspendable和Disposable。 這樣安排契約的原因是這些接口有特別的執(zhí)行約定。

          Component

          這是Avalon Framework的核心。這個考慮方向所定義的接口會拋出ComponentException異常。

          Component

          每個Avalon組件必須 實(shí)現(xiàn)Component接口。Component Manager和Component Selector只處理Component。這個接口沒有定義方法。它只是作為一個標(biāo)記性接口。

          任何組件必須使用不帶參數(shù)的缺省構(gòu)造方法。所有配置都是通過ConfigurableParameterizable接口來完成的。


          Composable

          一個用到其它組件的組件需要實(shí)現(xiàn)這個接口。這個接口只有一個方法compose(),該方法帶唯一一個ComponentManager 類型的參數(shù)。

          圍繞該接口的契約是:在組件的生存期中,compose()被調(diào)用一次且僅被調(diào)用一次。

          這個接口與其它任何定義了方法的接口一樣,都使用的是反向控制模式。它由組件的容器調(diào)用,只有該組件需要的那些組件會出現(xiàn)在ComponentManager中。


          Recomposable

          在少數(shù)的情況下,組件會需要一個新的ComponentManager和新的組件-角色映射關(guān)系。在這些情況下,需要實(shí)現(xiàn)recomposable接口。它的方法也與Composable的方法名稱不一樣,是recompose()

          圍繞該接口的契約是:recompose() 方法可以被調(diào)用任意多次,但不能在組件完全初始化好之前調(diào)用。當(dāng)該方法被調(diào)用時,組件必須以一種安全和一致的方式更新它自己。通常這意味著組件進(jìn)行的所有操作必需在更新之間停下來,在更新之后再繼續(xù)。



          Activity

          這組接口與組件生命周期的契約相關(guān)。如果在這組接口調(diào)用過程中出現(xiàn)了錯誤,您可以拋出一個通用的Exception。

          Disposable

          如果一個組件需要以一種結(jié)構(gòu)化的方式知道自己不在需要了,它可以使用Disposable接口。一旦一個組件被釋放掉,它就不能再用了。實(shí)際上,它就等待著被垃圾收集。該接口只有一個方法dispose(),該方法沒有參數(shù)。

          圍繞該接口的契約是:dispose()方法被調(diào)用一次,也是組件生存期中調(diào)用的最后一個方法。同時也表明該組件將不再使用,必須釋放該組件所占用的資源。


          Initializable

          任何組件如果需要創(chuàng)建其它組件,或需要執(zhí)行初始化操作從其它的初始化步驟中獲取信息,就要用到Initializable接口。該接口只有一個initialize()方法,該方法沒有參數(shù)。

          圍繞該接口的契約是:initialize()方法被調(diào)用一次,它是初始化過程中最后被調(diào)用的方法。同時也表明,該組件已處于活動狀態(tài),可以被系統(tǒng)中其它組件使用。


          Startable

          任何組件如果在其生存期中持續(xù)運(yùn)行,就要用到Startable 接口。該接口定義了兩個方法:start()stop()。這兩個方法都沒有參數(shù)。

          圍繞該接口的契約是:start()方法在組件完全初始化之后被調(diào)用一次。stop() 方法在組件被銷毀之前被調(diào)用一次。它們都不會被調(diào)用兩次。start() 總在stop()之前被調(diào)用。對該接口的實(shí)現(xiàn)要求安全地執(zhí)行start()stop() 方法 (不像Thread.stop()方法) 并且不會導(dǎo)致系統(tǒng)不穩(wěn)定。


          Suspendable

          任何組件如果在其生命期中允許自己被掛起,就要用到Suspendable 接口。雖然它通常總是和Startable 接口在一起使用,但這不是必需的。該接口有兩個方法:suspend()resume()。這兩個方法都沒有參數(shù)。

          圍繞該接口的契約是:suspend() and resume() 可以被調(diào)用任意多次,但在組件初始化并啟動之前,或在組件停止并銷毀之后不能調(diào)用。 對已掛起的組件調(diào)用suspend() 方法,或?qū)σ言谶\(yùn)行的組件調(diào)用resume() 將沒有任何效果。



          Configuration

          這一組接口描述了配置方面的考慮。如果發(fā)生任何問題,例如沒有所需的Configuration 元素,那么可以拋出ConfigurationException異常。

          Configurable

          那些需要根據(jù)配置確定其行為的組件必須實(shí)現(xiàn)這個接口以得到Configuration 對象的實(shí)例。該接口有一個configure() 方法,只有一個Configuration 類型的參數(shù)。

          圍繞該接口的契約是:configure() 方法在組件的生存期中被調(diào)用一次。傳入的Configuration 對象一定不能為null


          Configuration

          Configuration對象是一由配置元素組成的樹,配置元素?fù)碛幸恍傩浴T谀撤N程度上,您可以將配置對象看作一個大大簡化的DOM。Configuration類的方法太多,不便在本文中介紹,請看JavaDoc文檔。您可以從Configuration 對象中取得String, int, long, float, or boolean類型的值,如果配置沒有將提供缺省值。對屬性也是一樣的。您也可以取得子Configuration 對象。

          契約規(guī)定,具有值的Configuration 對象不應(yīng)有任何子對象,對子對象也是這樣的。

          你會注意到你無法得到父Configuration 對象。設(shè)計就是這樣做的。為了減少配置系統(tǒng)的復(fù)雜性,大多數(shù)情況下容器會將子配置對象傳給子組件。子組件不應(yīng)該有權(quán)訪問父配置的值。這種方式可能帶來一些不便,但是Avalon團(tuán)隊(duì)在需要做折衷的情況下總是選擇把安全性放在第一。


          Reconfigurable

          實(shí)現(xiàn)該接口的組件行為與Recomposable 組件非常相似。它只有一個reconfigure()方法。這樣的設(shè)計決策是為了降低Re開頭的那些接口的學(xué)習(xí)難度。ReconfigurableConfigurable 來說就象RecomposableComposable一樣。



          Context

          Avalon中Context 的概念起源于需要一種機(jī)制來實(shí)現(xiàn)從容器向組件傳遞簡單對象。確切的協(xié)議和名字綁定有意沒有定義,以便給開者提供最大的靈活性。圍繞Context 對象的使用的契約由您在您的系統(tǒng)中定義,盡管機(jī)制是一樣的。

          Context

          Context接口只定義了一個get()方法。它有一個Object 類型的參數(shù),返回以參數(shù)對象為鍵值的一個對象。Context 對象由容器組裝,然后傳遞給子組件,子組件對Context只有讀權(quán)限。

          除了Context 對子組件總是只讀的之外,沒有別的契約。如果您擴(kuò)展了Avalon的Context,請注意遵守該契約。它是反向控制模式的一部分,也是安全性設(shè)計的一部分。另外,在Contex中傳一個引用 給容器的做法也是不好的,原因和Context應(yīng)該是只讀的相同。


          Contextualizable

          希望接收來自于容器的Context對象的組件應(yīng)該實(shí)現(xiàn)該接口。它有一個名為contextualize() 的方法,參數(shù)是容器組裝的Context 對象。

          圍繞這個接口的契約是:contextualize() 在組件的生存期中被調(diào)用一次,在LogEnabled 之后,但在其它初始化方法之前。


          Recontextualizable

          實(shí)現(xiàn)該接口的組件行為與Recomposable 組件非常相似。它只有一個名為recontextualize()的方法。這樣的設(shè)計決策是為了降低Re開頭的接口的學(xué)習(xí)難度。RecontextualizableContextualizable 就如同RecomposableComposable


          Resolvable

          Resolvable接口用于標(biāo)識一些對象,這些對象在某些特定上下文中需要分離(need to be resolved)。一個例子是:某對象被多個Context 對象共享,并根據(jù)特定的Context改變其自身的行為。在對象被返回之前Context會調(diào)用 resolve() 方法。



          Logger

          每個系統(tǒng)都需要具備對事件記錄日志的能力。Avalon內(nèi)部使用了它的LogKit項(xiàng)目。盡管LogKit有一些方法可以靜態(tài)地訪問一個Logger實(shí)例,但Framework希望使用反向控制模式。

          LogEnabled

          每個需要Logger實(shí)例的組件都要實(shí)現(xiàn)該接口。該接口有一個名為enableLogging() 的方法,將Avalon Framework的Logger 實(shí)例傳遞給組件。

          圍繞該接口的契約是:在組件的生存期中只調(diào)用一次,在任何其它初始化步驟之前。


          Logger

          Logger接口用于對不同的日志庫進(jìn)行抽象。它提供一個唯一的客戶端API。Avalon Framework提供了三個實(shí)現(xiàn)該接口的封裝類:針對LogKit的LogKitLogger 、針對Log4J的Log4jLogger 、和針對JDK1.4日志機(jī)制的Jdk14Logger



          Parameters

          Avalon認(rèn)識到Configuration對象層次結(jié)構(gòu)在許多場合下太重量級了。因此,我們提出了一個Parameters對象來提供Configuration 對象的替代,使用名稱-值對的方法。

          Parameterizable

          任何希望用Parameters 來取代Configuration 對象的組件將實(shí)現(xiàn)該接口。Parameterizable 只有一個名為parameterize()的方法,with the parameter being the Parameters object.

          圍繞該接口的契約是:它在組件的生存期中被調(diào)用一次。該接口與Configurable接口不兼容。


          Parameters

          Parameters對象提供了一種通過一個String 類型的名字取得值的機(jī)制。如果值不存在,有方便的方法允許你使用缺省值,也可以得到Configurable 接口中任何相同格式的值。

          盡管Parameters 對象與java.util.Property 對象之間存在相似性,但它們還是存在重要的語義差別。首先,Parameters只讀的。 其次,Parameters 總是很容易從Configuration 對象中導(dǎo)出。最后,Parameters 對象是從XML 片斷中導(dǎo)出的,看上去是這樣的:

          <parameter name="param-name" value="param-value"/>
          
                    


          Thread

          線程標(biāo)記器(marker)用于根據(jù)組件的使用發(fā)出容器的基本語義信息信號。它們考慮到線程安全,對組件的實(shí)現(xiàn)提供標(biāo)記。最佳實(shí)踐是把這些接口的實(shí)現(xiàn)推遲到最終實(shí)現(xiàn)該組件的類。這樣做避免了一些復(fù)雜情況,有時某個組件被標(biāo)記為ThreadSafe,但從它派生出來的組件實(shí)現(xiàn)去不是線程安全的。這個包中定義的接口組成了我們稱之為LifeStyle系列接口的一部分。 另一個LifeStyle接口是Excalibur包的一部分(所以它是這個核心接口集的擴(kuò)展),Poolable定義在Excalibur的池實(shí)現(xiàn)中。

          SingleThreaded

          圍繞SingleThreaded組件的契約是實(shí)現(xiàn)該接口的組件不允許被多個線程同時訪問。每個線程需要有該組件的自己的實(shí)例。另一種做法是使用一個組件池,而不是每次請求該組件時都創(chuàng)建一個新的實(shí)例。為了使用池,您需要實(shí)現(xiàn)Avalon Excalibur的Poolable接口,而不是這個接口。


          ThreadSafe

          圍繞ThreadSafe組件的契約是:不管有多少線程同時訪問該組件,它們的接口和實(shí)現(xiàn)都能夠正常工作。盡管這是一個有彈性的設(shè)計目標(biāo),但有時候由您所使用的技術(shù),它就是不能實(shí)現(xiàn)。實(shí)現(xiàn)了這個接口的組件通常在系統(tǒng)中只有一個實(shí)例,其它的組件將使用該實(shí)例。



          其它

          在Avalon Framework的根包(root package)中的這些類和接口包括了Exception層次和一些通用工具類。但是有一個類值得一提。

          Version

          JavaTM 版本技術(shù)是在jar包中的manifest文件中有一項(xiàng)指定。問題是,當(dāng)jar被解包后您就失去了版本信息,并且版本信息放在易于修改的文本文件中。當(dāng)您把這些問題與一個較陡的學(xué)習(xí)曲線放在一起考慮時,檢查組件和接口的版本就比較困難。

          Avalon開發(fā)小組設(shè)計了Version對象,讓您可以容易地檢查版本和比較版本。您可以在您的組件中實(shí)現(xiàn)Version對象,測試合適的組件或最低版本號也會更容易。

          實(shí)現(xiàn)夢想

          我們將向你展示怎樣使用Avalon Framework和Avalon Excalibur來實(shí)現(xiàn)您的服務(wù)應(yīng)用程序。我們將向您展示Avalon有多么易于使用。

          在您完成分析以后,您需要創(chuàng)建組成您的系統(tǒng)的組件與服務(wù)。如果Avalon只是描述了一些您可以使用的編程習(xí)慣,那么它的用處就不大。但即使是這樣,運(yùn)用這些編程習(xí)慣和模式也會對理解整個系統(tǒng)有所幫助。Avalon Excalibur提供了一些有用的組件和工具,您可以在您自己的系統(tǒng)中使用它們,這可以讓您的日子過得更輕松些。作為我們的演示,我們把定義一個組件從一個repository中取出一個文檔實(shí)現(xiàn)的全過程走一遍。如果您還記得我們關(guān)于理論上的業(yè)務(wù)服務(wù)器的討論,我們曾確定將這個組件作為一個服務(wù)。在實(shí)際情況中,一個組件就是一個服務(wù)的情況是很多的。


          實(shí)現(xiàn)該組件

          這里,我們定義如何實(shí)現(xiàn)我們的組件。我們會把實(shí)現(xiàn)前面提到的DocumentRepository組件的過程整個走一遍。我們需要弄清楚的第一件事就是我們的組件所關(guān)注的領(lǐng)域。然后我們需要弄清楚怎樣創(chuàng)建和管理我們的組件。

          選擇關(guān)注的領(lǐng)域

          我們在前面已經(jīng)為DocumentRepository組件定義了角色和接口,我們已準(zhǔn)備好來創(chuàng)建實(shí)現(xiàn)。因?yàn)镈ocumentRepository的接口只定義了一個方法,我們有機(jī)會創(chuàng)建一個線程安全的組件。這是最受歡迎的一類組件,因?yàn)樗试S只消耗最少的資源。為了讓我們的實(shí)現(xiàn)是線程安全的,我們確實(shí)需要仔細(xì)考慮如何實(shí)現(xiàn)該組件。既然我們所有的文檔都存放在數(shù)據(jù)庫中,而且我們希望使用一個外部的Guardian 組件,我們將需要訪問其它組件。作為一個負(fù)責(zé)任的開發(fā)者,我們希望對有助于調(diào)試組件的信息記錄日志,追蹤內(nèi)部發(fā)生了什么。Avalon框架的優(yōu)美之處在于,您只需實(shí)現(xiàn)您需要的接口,可以忽略不需要的那些。這就是Separation of Concerns帶來的好處。當(dāng)您發(fā)現(xiàn)需要考慮一個新的方面時,您只需實(shí)現(xiàn)相關(guān)的接口就為組件加入了新的功能。 對于使用您的組件的部分來說,不需要作任何改動。

          既然線程安全是一個設(shè)計目標(biāo),我們就已經(jīng)知道了需要實(shí)現(xiàn)ThreadSafe接口。DocumentRepository接口只有一個方法,所以對于該組件的工作界面的使用是滿足該需求的。而且我們知道,Component在完全初始化之前是不會被使用的,在它被銷毀之后也不會被使用。

          為了完成設(shè)計,我們需要實(shí)現(xiàn)一些隱含的接口。我們希望解決方案足夠安全,讓我們可能顯式地得知組件是否已經(jīng)完全初始化。為了達(dá)到這個目標(biāo),我們將實(shí)現(xiàn)Initializable和Disposable接口。由于關(guān)于環(huán)境方面的信息可能發(fā)生改變,或者可能需要能定制,我們需要讓DocumentRepository實(shí)現(xiàn)Configurable接口,Avalon提供的取得所需組件的實(shí)例的方法是利用一個ComponentManager。我們需要實(shí)現(xiàn)Composable 接口來從ComponentManager取得組件實(shí)例。

          因?yàn)镈ocumentRepository訪問數(shù)據(jù)庫中的文檔,我們需要做一個決定。我們是要利用Avalon Excalibur DataSourceComponent呢,還是希望自己實(shí)現(xiàn)數(shù)據(jù)庫連接管理的代碼。在本文中,我們將利用DataSourceComponent。

          此時,我們的類骨架看起來是這樣的:

          public class DatabaseDocumentRepository
          extends AbstractLogEnabled
          implements DocumentRepository , Configurable, Composable, Initializable,
                     Disposable, Component, ThreadSafe
          {
              private boolean initialized = false;
              private boolean disposed = false;
              private ComponentManager manager = null;
              private String dbResource = null;
          
              /**
               * Constructor.  All Components need a public no argument constructor
               * to be a legal Component.
               */
              public DatabaseDocumentRepository() {}
          
              /**
               * Configuration.  Notice that I check to see if the Component has
               * already been configured?  This is done to enforce the policy of
               * only calling Configure once.
               */
              public final void configure(Configuration conf)
                  throws ConfigurationException
              {
                  if (initialized || disposed)
                  {
                      throw new IllegalStateException ("Illegal call");
                  }
          
                  if (null == this.dbResource)
                  {
                      this.dbResource = conf.getChild("dbpool").getValue();
                      getLogger().debug("Using database pool: " + this.dbResource);
                      // Notice the getLogger()?  This is from AbstractLogEnabled
                      // which I extend for just about all my components.
                  }
              }
          
              /**
               * Composition.  Notice that I check to see if the Component has
               * already been initialized or disposed?  This is done to enforce
               * the policy of proper lifecycle management.
               */
              public final void compose(ComponentManager cmanager)
                  throws ComponentException
              {
                  if (initialized || disposed)
                  {
                      throw new IllegalStateException ("Illegal call");
                  }
          
                  if (null == this.manager)
                  {
                      this.manager = cmanager;
                  }
              }
          
              public final void initialize()
                  throws Exception
              {
                  if (null == this.manager)
                  {
                      throw new IllegalStateException("Not Composed");
                  }
          
                  if (null == this.dbResource)
                  {
                      throw new IllegalStateException("Not Configured");
                  }
          
                  if (disposed)
                  {
                      throw new IllegalStateException("Already disposed");
                  }
          
                  this.initialized = true;
              }
          
              public final void dispose()
              {
                  this.disposed = true;
                  this.manager = null;
                  this.dbResource = null;
              }
          
              public final Document getDocument(Principal requestor, int refId)
              {
                  if (!initialized || disposed)
                  {
                      throw new IllegalStateException("Illegal call");
                  }
          
                  // TODO: FILL IN LOGIC
              }
          }
          
                

          您在以上代碼中可以發(fā)現(xiàn)一些結(jié)構(gòu)模式。當(dāng)您在設(shè)計時考慮到安全性時,您應(yīng)該在組件中顯式地強(qiáng)制實(shí)現(xiàn)每個契約。安全強(qiáng)度總是取決于最弱的那一環(huán)。只有當(dāng)您肯定一個組件已經(jīng)完全初始化以后才能使用它,在它被銷毀后,就再也不要用它了。我在這里放上這些邏輯是因?yàn)槟诰帉懽约旱念悤r也會采用相同的方式。


          組件實(shí)例化和管理組件

          為了讓您能理解容器/組件的關(guān)系是如何工作的,我們將先討論管理組件的手工方式。接下來我們將討論Avalon's Excalibur組件體系結(jié)構(gòu)是如何為您隱藏復(fù)雜性的。您仍會發(fā)現(xiàn)有些時候?qū)幵缸约汗芾斫M件。但在大多數(shù)時候,Excalibur的強(qiáng)大能力和靈活性就能滿足您的需要。

          The Manual Method

          所有Avalon的組件都是在某處創(chuàng)建的。創(chuàng)建該組件的代碼就是該組件的容器。容器負(fù)責(zé)管理組件從構(gòu)造到析構(gòu)的生命周期。容器可以有一個靜態(tài)的"main"方法,讓它能從命令行調(diào)用,或者它也可以是另一個容器。在您設(shè)計容器時,請記得反向控制的模式。信息和方法調(diào)用將只從容器流向組件。

          顛覆控制(Subversion of Control)

          顛覆控制是反向控制的反模式。當(dāng)您把容器的引用傳遞給組件時,就實(shí)現(xiàn)了顛覆控制。當(dāng)您讓一個組件管理它自己的生命周期時,也屬于這種情況。以這種方式操作的代碼應(yīng)該被視為是有缺陷的。當(dāng)您將容器/組件關(guān)系混在一起時,它們的交互將使系統(tǒng)難于調(diào)試,并難以審計安全性。

          為了管理子組件,您需要在它們整個生命同期都保存對它們的引用。在容器和其它組件可以使用該子組件之前,它必須完成初始化。對我們的DocumentRepository來說,代碼看起來可能象下面的樣子:

          class ContainerComponent implements Component, Initializable, Disposable
          {
              DocumentRepository docs = new DatabaseDocumentRepository();
              GuardianComponent guard = new DocumentGuardianComponent();
              DefaultComponentManager manager = new DefaultComponentManager();
          
              public void initialize()
                  throws Exception
              {
                  Logger docLogger = new LogKitLogger( Hierarchy.defaultHierarchy()
                                                       .getLoggerFor( "document" ) );
          
                  this.docs.enableLogging( docLogger.childLogger( "repository" ) );
                  this.guard.enableLogging( docLogger.childLogger( "security" ) );
          
                  DefaultConfiguration pool = new DefaultConfiguration("dbpool");
                  pool.setValue("main-pool");
                  DefaultConfiguration conf = new DefaultConfiguration("");
                  conf.addChild(pool);
          
                  this.manager.addComponent( DocumentRepository.ROLE, this.docs );
                  this.manager.addComponent( GuardianComponent.ROLE, this.guard );
                  this.docs.compose( this.manager );
                  this.guard.compose( this.manager );
          
                  this.docs.configure(conf);
          
                  this.guard.initialize();
                  this.docs.initialize();
              }
          
              public void dispose()
              {
                  this.docs.dispose();
                  this.guard.dispose();
              }
          }
          
                  

          為了簡潔,我把顯式地檢查從以上代碼中移去了。您可以看到手工地創(chuàng)建和管理組件是很細(xì)節(jié)化的工作。如果您忘記做了組件生命周期中的某一步,您就會發(fā)現(xiàn)bug。這也需要對您正在實(shí)例化的組件有一些深入的了解。另一種做法是給上面的ContainerComponent增加一些方法,來動態(tài)地處理組件的初始化。


          Automated Autonomy

          開發(fā)者的本性是懶惰的,所以他們會花時間寫一個特別的ComponentManager 作為系統(tǒng)中所有組件的容器。通過這種方式,他們就不必深入地了解系統(tǒng)中所有組件的接口。這可能是個令人沮喪的任務(wù)。Avalon的開發(fā)者已經(jīng)創(chuàng)建了這樣一個怪獸。Avalon Excalibur的組件體系結(jié)構(gòu)中包括了一個ComponentManager,通過XML的配置文件來控制。

          當(dāng)您把管理組件的責(zé)任交給Excalibur的ComponentManager時,存在一個折衷。您放棄了對CompomentManager中包含哪些組件的精細(xì)控制。但是,如果您的系統(tǒng)相當(dāng)大,您會發(fā)現(xiàn)手工控制是一項(xiàng)令人沮喪的工作。在這種情況下,出于對系統(tǒng)穩(wěn)定性的考慮,最好由一個地方集中式地管理系統(tǒng)中所有的組件。

          既然可以與Excalibur的組件體系結(jié)構(gòu)有不同中層次的集成程度,我們將從最低層次開始。Excalibur有一組ComponentHandler對象,它們作為每類組件獨(dú)立的容器。它們管理您的組件的整個生命周期。讓我們引入生存方式(lifestyle)接口的概念。一個生存方式接口描述了系統(tǒng)是怎樣對待一個組件的。既然組件的生存方式對系統(tǒng)運(yùn)行會產(chǎn)生影響,我們就需要討論當(dāng)前的一些生存方式所暗含的意義:

          • org.apache.avalon.framework.thread.SingleThreaded

            • 不是線程安全的或可重用的。

            • 如果沒有指定其它生存方式方式接口,系統(tǒng)就認(rèn)為是這個。

            • 在每次請求組件時,都會創(chuàng)建一個全新的實(shí)例。

            • 實(shí)例的創(chuàng)建和初始化被推遲到請求組件時進(jìn)行。

          • org.apache.avalon.framework.thread.Threadsafe

            • 組件是完全可重入的,并符合所有的線程安全的原則。

            • 系統(tǒng)創(chuàng)建一個實(shí)例,所有Composable組件對它的訪問是共享的。

            • 實(shí)例的創(chuàng)建和初始化是在ComponentHandler創(chuàng)建時完成的。

          • org.apache.avalon.excalibur.pool.Poolable

            • 不是線程安全的,但是是完全可重用的。

            • 創(chuàng)建一組實(shí)例放在池中,當(dāng)Composable組件請求時,系統(tǒng)提供一個可用的。

            • 實(shí)例的創(chuàng)建和初始化是在ComponentHandler創(chuàng)建時完成的。

          ComponentHandler接口處理起來是很簡單的。你通過Java類、Configuration對象、ComponentManager對象、Context對象和RoleManager對象來初始化構(gòu)造方法。 如果您知道您的組件將不需要上述的某一項(xiàng),您可以在它的位置上傳一個null。 在這之后,當(dāng)您需要對該組件的引用時,您就調(diào)用"get"方法。當(dāng)您用完之后,您調(diào)用"put"方法將組件歸還給ComponentHandler。 以下的代碼便于我們理解這一點(diǎn)。

          class ContainerComponent implements Component, Initializable, Disposable
          {
              ComponentHandler docs = null;
              ComponentHandler guard = null;
              DefaultComponentManager manager = new DefaultComponentManager();
          
              public void initialize()
                  throws Exception
              {
                  DefaultConfiguration pool = new DefaultConfiguration("dbpool");
                  pool.setValue("main-pool");
                  DefaultConfiguration conf = new DefaultConfiguration("");
                  conf.addChild(pool);
                  this.docs.configure(conf);
          
                  this.docs = ComponentHandler.getComponentHandler(
                                                  DatabaseDocumentRepository.class,
                                                  conf, this.manager, null, null);
                  this.guard = ComponentHandler.getComponentHandler(
                                                  DocumentGuardianComponent.class,
                                                  null, this.manager, null, null);
          
                  Logger docLogger = new LogKitLogger( Hierarchy.defaultHierarchy()
                                                       .getLoggerFor( "document" ) );
          
                  this.docs.enableLogging( docLogger.childLogger( "repository" ) );
                  this.guard.enableLogging( docLogger.childLogger( "security" ) );
          
                  this.manager.addComponent(DocumentRepository.ROLE, this.docs);
                  this.manager.addComponent(GuardianComponent.ROLE, this.guard);
          
                  this.guard.initialize();
                  this.docs.initialize();
              }
          
              public void dispose()
              {
                  this.docs.dispose();
                  this.guard.dispose();
              }
          }
          
                  

          這里,我們只少寫了幾行代碼。我們還是手工地創(chuàng)建了Configuration對象,還是設(shè)置了Logger,還是不得不對ComponentHandler對象進(jìn)行初始化和銷毀。 這里我們所做的只是防止受到接口變化的影響。您會發(fā)現(xiàn)用這種方式對代碼有好處。Excalibur所做的更進(jìn)了一步。大多數(shù)復(fù)雜的系統(tǒng)都有一些配置文件。它們允許管理員調(diào)整關(guān)鍵的配置信息。 Excalibur可以用以下的格式讀取配置文件,并從中創(chuàng)建系統(tǒng)的組件。

          <my-system>
            <component
              role="org.apache.avalon.excalibur.datasource.DataSourceComponentSelector"
              class="org.apache.avalon.excalibur.component.ExcaliburComponentSelector">
               <component-instance name="documents"
                 class="org.apache.avalon.excalibur.datasource.JdbcDataSource">
                   <pool-controller min="5" max="10"/>
                   <auto-commit>false</auto-commit>
                   <driver>org.gjt.mm.mysql.Driver</driver>
                   <dburl>jdbc:mysql:localhost/mydb</dburl>
                   <user>test</user>
                   <password>test</password>
                </component-instance>
                <component-instance name="security"
                  class="org.apache.avalon.excalibur.datasource.JdbcDataSource">
                   <pool-controller min="5" max="10"/>
                   <auto-commit>false</auto-commit>
                   <driver>org.gjt.mm.mysql.Driver</driver>
                   <dburl>jdbc:mysql:localhost/myotherdb</dburl>
                   <user>test</user>
                   <password>test</password>
                </component-instance>
            </component>
            <component
              role="org.apache.bizserver.docs.DocumentRepository"
              class="org.apache.bizserver.docs.DatabaseDocumentRepository">
                <dbpool>documents</dbpool>
            </component>
            <component
              role="org.apache.bizserver.docs.GuardianComponent"
              class="org.apache.bizserver.docs.DocumentGuardianComponent">
                <dbpool>security</dbpool>
                <policy file="/home/system/document.policy"/>
            </component>
          </my-system>
          
                  

          根元素可以由您任意指定。 您會注意到我們已經(jīng)定義了一些組件。 我們有了熟悉的DocumentRepository類和GuardianComponent類,以及一些Excalibur DataSourceComponent類。 而且,現(xiàn)在我們對Guardian組件有了一些特定的配置信息。 為了把這些系統(tǒng)讀入您的系統(tǒng),Avalon框架為您提供了一些方便:

          DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
          Configuration systemConf = builder.buildFromFile("/path/to/file.xconf");
          
                  

          這確實(shí)對我們前面手工構(gòu)建配置元素的代碼起到了簡化作用,而且它限制了我們在編程時需要明確了解的信息。 讓我們再看一眼Container類,看看是否真的省了一些事。記住我們指定了5個組件( ComponentSelector算作是一個組件), 以及每個組件的配置信息。

          class ContainerComponent implements Component, Initializable, Disposable {
              ExcaliburComponentManager manager = new ExcaliburComponentManager();
          
              public void initialize()
                  throws Exception
              {
                  DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
                  Configuration sysConfig = builder.buildFromFile("./conf/system.xconf");
          
                  this.manager.setLogger(  Hierarchy.getDefaultHierarchy()
                                                    .getLoggerFor("document") );
                  this.manager.contextualize( new DefaultContext() );
                  this.manager.configure( sysConfig );
                  this.manager.initialize();
              }
          
              public void dispose()
              {
                  this.manager.dispose();
              }
          }
          
                  

          難道不令人驚奇?我們對數(shù)量超過兩倍的組件進(jìn)行了初始化,而代碼量減少了一倍多(6行代碼,而不是13行)。 這個配置文件有一個缺點(diǎn),看起來有點(diǎn)瘋狂,但它將需要寫的代碼數(shù)量降到了最低。

          在ExcaliburComponentManager的背后發(fā)生了很多的活動。 對配置文件中的每個"component"元素,Excalibur為每個類的條目(entry)創(chuàng)建了一個ComponentHandler,并建立起與角色(role)的對應(yīng)關(guān)系。 "component"元素和它的所有子元素是對組件的配置。當(dāng)組件是一個ExcaliburComponentSelector時, Excalibur會讀入每個"component-instance"元素并執(zhí)行和前面同類型的操作,這次是與hint entry建立對應(yīng)關(guān)系。

          讓配置文件好看一些

          我們可以使用別名來改變配置文件的外觀。Excalibur使用一個RoleManager為配置系統(tǒng)提供別名。RoleManager可以是您專門創(chuàng)建的一個類,也可以使用DefaultRoleManager并傳入一個Configuration對象。 如果我使用DefaultRoleManager,我將把角色配置文件和系統(tǒng)的其它部分藏在jar文件中。這是因?yàn)榻巧渲梦募⒅挥砷_發(fā)者改動。 下面是RoleManager接口:

          interface RoleManager
          {
              String getRoleForName( String shorthandName );
              String getDefaultClassNameForRole( String role );
              String getDefaultClassNameForHint( String hint, String shorthand );
          }
          
                    

          讓我們來看一下Excalibur是如何使用我們的框架中的RoleManager的。首先,Excalibur循環(huán)讀入根元素的所有子元素。 這包括了所有的"component"元素,但這次Excalibur并不識別元素的名稱,它詢問RoleManager 對這個組件我們將使用什么角色。如果RoleManager返回null, 那么該元素和它所有的子元素都被忽略。接下來, Excalibur 從角色名稱中導(dǎo)出類名。最后的方法是動態(tài)地將類名與ComponentSelector的子類型對應(yīng)起來。

          Excalibur提供了一個RoleManager的缺省實(shí)現(xiàn),它使用一個XML配置文件。標(biāo)記相當(dāng)簡單,它把所有您不希望管理員看到的附加信息都隱藏起來的。

          <role-list>
            <role
              name="org.apache.avalon.excalibur.datasource.DataSourceComponentSelector"
              shorthand="datasources"
              default-class="org.apache.avalon.excalibur.component.ExcaliburComponentSelector">
               <hint shorthand="jdbc"
                 class="org.apache.avalon.excalibur.datasource.JdbcDataSourceComponent"/>
               <hint shorthand="j2ee"
                 class="org.apache.avalon.excalibur.datasource.J2eeDataSourceComponent"/>
            </role>
            <role
              name="org.apache.bizserver.docs.DocumentRepository"
              shorthand="repository"
              default-class="org.apache.bizserver.docs.DatabaseDocumentRepository"/>
            <role
              name="org.apache.bizserver.docs.GuardianComponent"
              shorthand="guardian"
              default-class="org.apache.bizserver.docs.DocumentGuardianComponent"/>
          </role-list>
          
                    

          為了使用RoleManager,您需要改變?nèi)萜黝愔械?初始化"方法。您將使用配置構(gòu)造器(configuration builder)通過這個文件來構(gòu)造一個Configuration樹。 請記住,如果您打算使用一個RoleManager,您必須在調(diào)用"configure"方法之前調(diào)用"setRoleManager"方法。 為了展示您如何從類裝入器中取得這個XML文件,我將在下面展示該技巧:

          DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
          Configuration sysConfig = builder.buildFromFile("./conf/system.xconf");
          Configuration roleConfig = builder.build(
                      this.getClass().getClassLoader()
                      .getResourceAsStream("/org/apache/bizserver/docs/document.roles"));
          
          DefaultRoleManager roles = new DefaultRoleManager();
          roles.enableLogging(Hierarchy.getDefaultHierarchy().getLoggerFor("document.roles"));
          roles.configure(roleConfig);
          
          this.manager.setLogger( Hierarchy.getDefaultHierarchy()
                                     .getLoggerFor("document") );
          this.manager.contextualize( new DefaultContext() );
          this.manager.setRoleManager( roles );
          this.manager.configure( sysConfig );
          this.manager.initialize();
          
                    

          既然我們增加了6行代碼,就要看一下它帶來了什么好處。 我們最終的配置文件可以這樣寫:

          <my-system>
            <datasources>
               <jdbc name="documents">
                   <pool-controller min="5" max="10"/>
                   <auto-commit>false</auto-commit>
                   <driver>org.gjt.mm.mysql.Driver</driver>
                   <dburl>jdbc:mysql:localhost/mydb</dburl>
                   <user>test</user>
                   <password>test</password>
                </jdbc>
                <jdbc name="security">
                   <pool-controller min="5" max="10"/>
                   <auto-commit>false</auto-commit>
                   <driver>org.gjt.mm.mysql.Driver</driver>
                   <dburl>jdbc:mysql:localhost/myotherdb</dburl>
                   <user>test</user>
                   <password>test</password>
                </jdbc>
            </datasources>
            <repository>
                <dbpool>documents</dbpool>
            </repository>
            <guardian>
                <dbpool>security</dbpool>
                <policy file="/home/system/document.policy"/>
            </guardian>
          </my-system>
          
                    

          正如您所看到的那樣,與前面的文件相比,這個文件的可讀性要強(qiáng)很多。 現(xiàn)在我們可以為系統(tǒng)增加任意數(shù)目的組件,而不需要寫更多的代碼來支持它們。





          使用該組件

          現(xiàn)在我們已經(jīng)創(chuàng)建了我們的組件,我們將使用它們。不管組件是如何被初始化和管理的,您訪問組件的方法都是一樣的。 您必須實(shí)現(xiàn)Composable接口,這樣才能從ComponentManager得到一個引用。 ComponentManager保存著所有您需要的組件的引用。為了討論方便起見,我們將假定我們得到的ComponentManager 是按照前一節(jié)的最終的配置文件來配置的。 這就是說我們有一個Repository, 一個Guardian, 和兩個DataSources。

          使用組件管理基礎(chǔ)結(jié)構(gòu)的原則

          組件管理基礎(chǔ)結(jié)構(gòu)要求您釋放您得到引用的組件。這個限制的原因是為了能正確地管理組件資源。ComponentManager的設(shè)計考慮到您對特定的角色有不同類型的組件。 ComponentSelector的另一個獨(dú)特的方面是它也被設(shè)計為一個組件。這使我們可以從一個ComponentManager取得一個ComponentSelector。

          有兩種合法的方式來處理對外部組件的引用。您可以在初始化過程中得到引用,在銷毀時釋放它們。 您也可以把處理組件的代碼放在try/catch/finally語句塊中。兩種方法各有優(yōu)缺點(diǎn)。

          Initialization and Disposal Approach

          class MyClass implements Component, Composable, Disposable
          {
              ComponentManager manager;
              Guardian myGuard;
          
              /**
               * Obtain a reference to a guard and keep the reference to
               * the ComponentManager.
               */
              public void compose(ComponentManager manager)
                  throws ComponentException
              {
                  if (this.manager == null)
                  {
                      this.manager = manager;
                      myGuard = (Guardian) this.manager.lookup(Guardian.ROLE);
                  }
              }
          
              /**
               * This is the method that uses the Guardian.
               */
              public void myMethod()
                  throws SecurityException
              {
                  this.myGuard.checkPermission(new BasicPermission("test"));
              }
          
              /**
               * Get rid of our references
               */
              public void dispose()
              {
                  this.manager.release(this.myGuard);
                  this.myGuard = null;
                  this.manager = null;
              }
          }
          
                  

          從示例代碼中您可以看到,照這樣做很容易。當(dāng)該對象第一次接收到ComponentManager時,它取得了一個Guardian組件的引用。 如果您可以保證Guardian組件是線程安全的(實(shí)現(xiàn)了ThreadSafe接口),那么就只需要做這些事。 不幸的是,從長遠(yuǎn)來看,您不能保證這一點(diǎn)。為了能正確地管理資源,在用完組件之后,我們必須釋放對組件的引用。 這就是為什么我們保持一個對ComponentManager的引用的原因。

          這種方式的主要不利之處在于處理組件池中的組件時。對組件的引用維系著該組件的生命。 如果該對象的生存期很短,這可能不是個問題;但是如果該對象是一個由Excalibur組件管理體系結(jié)構(gòu)所管理的組件,只要有對它的引用,它的生存期就會繼續(xù)。這意味著我們實(shí)際上把組件池變成了一個組件工廠。

          這種方式的主要好處是,得到和釋放組件的代碼很清楚。 您不必去理解異常處理的代碼。

          另一個細(xì)微差別是,您把Guardian的存在與初始化這個對象的能力捆綁在了一起。 一旦在一個對象的初始化階段拋出一個異常, 你就只好認(rèn)為該對象不是一個有效的對象。有時您希望當(dāng)要求的組件不存在時就讓程序失敗掉,那么這就不是問題。 在設(shè)計組件時,您確實(shí)需要注意到這一層隱含的意思。


          Exception Handling Approach

          class MyClass implements Composable, Disposable
          {
              ComponentManager manager;
          
              /**
               * Obtain a reference to a guard and keep the reference to
               * the ComponentManager.
               */
              public void compose(ComponentManager manager)
                  throws ComponentException
              {
                  if (this.manager == null)
                  {
                      this.manager = manager;
                  }
              }
          
              /**
               * This is the method that gets the Guardian.
               */
              public void myMethod()
                  throws SecurityException
              {
                  Guardian myGuard = null;
                  try
                  {
                      myGuard = (Guardian) this.manager.lookup(Guardian.ROLE);
                      this.criticalSection(myGuard);
                  }
                  catch (ComponentException ce)
                  {
                      throw new SecurityException(ce.getMessage());
                  }
                  catch (SecurityException se)
                  {
                      throw se;
                  }
                  finally
                  {
                      if (myGuard != null)
                      {
                          this.manager.release(myGuard);
                      }
                  }
              }
          
              /**
               * Perform critical part of code.
               */
              public void criticalSection(Guardian myGuard)
                  throws SecurityException
              {
                  myGuard.checkPermission(new BasicPermission("test"));
              }
          }
          
                  

          如您所見,這段代碼有些復(fù)雜。為了能理解它,您需要理解異常處理。 這可能不是問題,因?yàn)榻^大多數(shù)Java開發(fā)者都知道如何處理異常。 用這種方式,您不需要太擔(dān)心組件的生存方式問題, 因?yàn)橐坏┪覀儾恍枰鼤r,就釋放了它。

          這種方式的主要不利之處是增加了異常處理代碼,因?yàn)檩^為復(fù)雜。 為了能將復(fù)雜度降到最低,讓代碼更易于維護(hù),我們把工作代碼提取出來,放在另一個方法中。 請記住在try語句塊中,我們想得到多少組件的引用,就可以得到多少。

          這種方式的主要好處是您可以更有效率地管理組件引用。 同樣,如果您使用的是ThreadSafe組件,就沒有實(shí)質(zhì)差別,但如果您使用的是組件池里的組件時,就有差別了。 在每次您使用一個組件,取得一個新的引用時,有一點(diǎn)輕微的開銷,但是被迫創(chuàng)建一個新的組件實(shí)例的可能性大大降低。

          像初始化和銷毀的方式一樣,您也必須了解一個微妙的差別。 如果管理器找不到組件,異常處理的方式不會讓程序在初始化時失敗掉。 如前所述,這并不是完全沒好處的。 很多時候,您希望某個組件存在,但是如果期望的組件不存在,程序也不需要失敗掉。



          從ComponentSelector取得一個組件

          對于大多數(shù)操作,您只需要使用ComponentManager。 既然我們決定我們需要DataSourceComponent的多個實(shí)例, 我們就需要知道如何得到我們想要的那個實(shí)例。 ComponentSelector比ComponentManagers要稍復(fù)雜一些, 因?yàn)樘幚頃r有暗示要取得想要的引用。 一個組件屬于一個特定的角色,這我們已經(jīng)說得很清楚了。 但是,有時候我們需要從一個角色的多個組件中選擇一個。 ComponentSelector使用一個任意的對象來作為暗示。 大多數(shù)時候,這個對象是一個String,盡管您可能會希望使用一個Locale對象來取得一個正確國際化的組件。

          在我們已建立起來的系統(tǒng)中, 我們選擇使用字符串來選擇DataSourceComponent的正確實(shí)例。 我們甚至給了自己一個Configuration元素,來指明為了得到正確的組件,需要的字符串是什么。 這是一個好的實(shí)踐,可以照著做,因?yàn)樗瓜到y(tǒng)管理更容易。 比起要系統(tǒng)管理員記住這些神奇的配置值來,這便他們更容易看到對其它組件的引用。

          從概念上來看,從ComponentSelector取得一個組件與從ComponentManager取得組件并無差別。 你只多了一個步驟。 請記住ComponentSelector也是一個組件。 當(dāng)您查找ComponentSelect的角色時,ComponentManager 將準(zhǔn)備好ComponentSelector組件并返回給您。 然后您需要通過它來選擇組件。 為了說明這一點(diǎn),我將擴(kuò)展前面討論的異常處理方式的代碼。

          public void myMethod()
              throws Exception
          {
              ComponentSelector dbSelector = null;
              DataSourceComponent datasource = null;
              try
              {
                  dbSelector = (ComponentSelector)
                          this.manager.lookup(DataSourceComponent.ROLE + "Selector");
                  datasource = (DataSourceComponent)
                          dbSelector.select(this.useDb);
          
                  this.process(datasource.getConnection());
              }
              catch (Exception e)
              {
                  throw e;
              }
              finally
              {
                  if (datasource != null)
                  {
                      dbSelector.release(datasource);
                  }
          
                  if (dbSelector != null)
                  {
                      this.manager.release(dbSelector);
                  }
              }
          }
          
                

          您可以看到,我們通過使用指定組件的角色得到了ComponentSelector的引用。 我們遵守了前面提到的角色命名規(guī)范,在角色名的后名加上"Selector"作為后綴。 您可以使用一個靜態(tài)的接口來處理系統(tǒng)小所有的角色名,以減少代碼中字符串連接的次數(shù)。這樣做也是完全可以接受的。

          接下來,我們得從ComponentSelector到了DataSource組件的引用。 我們的示例代碼假定我們已經(jīng)從Configuration對象取得了所需的信息并把它放在一個名為"useDb"的類變量中。



          Excalibur的工具類

          最后這一節(jié)是向您介紹Apache Avalon Excalibur帶的幾類組件和工具類。 這些工具類是健壯的, 完全可以在實(shí)際生產(chǎn)系統(tǒng)中使用。 我們有一個非正式的分級式項(xiàng)目稱為"Scratchpad",在這個項(xiàng)目中,我們解決潛在新工具類的實(shí)現(xiàn)細(xì)節(jié)問題。 Scratchpad中的工具類的品質(zhì)各有不同,它們的使用方式也不能保證不變,盡管您可能覺得用起來不錯。

          命令行接口(Command Line Interface,CLI)

          CLI工具類在一些項(xiàng)目中使用,包括Avalon Phoenix和Apache Cocoon,用于處理命令行參數(shù)。它提供了打印幫助信息的機(jī)制,并能夠以短名字或長名字的方式來處理參數(shù)選項(xiàng)。


          集合工具類

          集合工具類對JavaTM Collections API進(jìn)行了一些增強(qiáng)。 這些增強(qiáng)包括在兩個list中找出交叉處的功能和一個PriorityQueue ,它是對Stack 的增強(qiáng),允許對象的優(yōu)先級改變簡單的先進(jìn)后出的Stack實(shí)現(xiàn)。


          組件管理

          我們已經(jīng)在前面討論了這方面的用法。這是Excalibur中最復(fù)雜的怪獸,但是它在很少的幾個類中提供了很多的功能。在簡單的SingleThreadedThreadSafe兩種管理類型之外,還有一種Poolable類型。如果一個組件實(shí)現(xiàn)了Excalibur的Poolable接口,而不是SingleThreaded接口, 那么它將維護(hù)一個組件池并重用實(shí)例。大多數(shù)情況下這工作得很好。 在少數(shù)組件不能重用的情況下,就使用SingleThreaded接口。


          LogKit管理

          Avalon開發(fā)團(tuán)隊(duì)意識到許多人需要一種簡單的機(jī)制來創(chuàng)建復(fù)雜的日志目標(biāo)層次結(jié)構(gòu)。 出于RoleManager類似的想法,團(tuán)隊(duì)開發(fā)了LogKitManager,可以被前面提到的Excalibur Component Management系統(tǒng)使用。基于"logger"屬性,它將為不同的組件給出相應(yīng)的 Logger 對象。


          線程工具類

          concurrent包提供了一些輔助多線程編程的類:Lock (mutex的實(shí)現(xiàn)), DjikstraSemaphore, ConditionalEvent, 和 ThreadBarrier.


          Datasources

          這是根據(jù)javax.sql.DataSource類設(shè)計的,但是作了簡化。 DataSourceComponent有兩個實(shí)現(xiàn):一個顯式地使用JDBC連接池,另一個使用J2EE 應(yīng)用服務(wù)器的javax.sql.DataSource 類。


          輸入/輸出 (IO) 工具類

          IO工具類提供了一些FileFilter類以及File和IO相關(guān)的工具類。


          池實(shí)現(xiàn)

          Pool實(shí)現(xiàn)在各種情況下都能使用的池。其中有一個實(shí)現(xiàn)非常快, 但只能在一個線程中使用,用來實(shí)現(xiàn)FlyWeight模式是不錯的。還有一個DefaultPool,它不管理池中對象的數(shù)目。SoftResourceManagingPool在對象被歸還時判斷是否超過一個閾值,如果超過,就讓對象“退役”。最后,HardResourceManagingPool在您達(dá)到對象的最大數(shù)目時會拋出一個異常。后面的三個池都是ThreadSafe的。


          Property工具類

          Property工具類與Context對象一起使用。它們使您可以擴(kuò)展Resolvable對象中的"variables"。它是這樣工作的:"${resource}" 將去尋找一個名為"resource"的Context的值,并用這個值來代替這個符號。


          結(jié)論

          Avalon經(jīng)受了時間的考驗(yàn),可以供您使用。本部分提供的證據(jù)可以幫助說服您自己和其它人,使用一個成熟的框架比自己創(chuàng)建一個要好。

          您可能已經(jīng)被說服了,但需要一些幫助來說服您的同事,讓他們相信Avalon是正確的選擇。 也許您也需要說服您自己。 不管怎樣,本章將幫助您整理思路,并提供一些有說服力的證據(jù)。 我們需要與對開放源代碼模式的(Fear, Uncertainty, and Doubt ,F(xiàn)UD)作斗爭。 關(guān)于開放源代碼的有效性的證據(jù),我推薦您閱讀Eric S. Raymond對這一主題的優(yōu)秀論述N400017. 不論您對他所持觀點(diǎn)的看法如何,他寫的文章匯編成的書The Cathedral and the Bazaar 將提供使人從總體上接受開放源代碼的信息。


          Avalon能工作

          我們的底線是Avalon完成了它最初設(shè)計時要達(dá)到的目標(biāo)。 Avalon沒有引入新的概念和思想,而是使用了一些經(jīng)受時間考驗(yàn)的概念,并將它們規(guī)范化。 影響Avalon設(shè)計的最新的概念是分離考慮(Separation of Concerns)模式,它大約是在1995提出的。即使在那時候, 分離考慮也是一種系統(tǒng)分析技術(shù)的規(guī)范化方法。

          Avalon的用戶群數(shù)以百計。 一些項(xiàng)目如Apache Cocoon, Apache JAMES, 和Jesktop是建立在Avalon之上的。這些項(xiàng)目的開發(fā)者是Avalon Framework的用戶。 因?yàn)锳valon有如此眾多的用戶,它得到了很好的測試。

          由最優(yōu)秀的人設(shè)計

          Avalon的作者認(rèn)識到,我們不是服務(wù)器端計算的唯一一群專家。 我們使用了來自其它人的研究的概念和想法。 我們響應(yīng)來自用戶的反饋。Avalon不僅僅是由前面介紹的5個開發(fā)者設(shè)計的,他們帶來的是反向控制、分離考慮和面向組件編程的思想,并設(shè)計了它。

          開放源代碼的優(yōu)秀之處在于,得到的結(jié)果是最佳的思想和最佳的代碼的融合。 Avalon經(jīng)過一了些測試想法的階段并拒絕了一些想法,因?yàn)檫€有更好的解決方案。 您可以利用Avalon開發(fā)小組所獲得的知識,并在您自己的系統(tǒng)中運(yùn)用。 您可以在自己的項(xiàng)目中使用Excalibur中預(yù)先定義好的組件,它們經(jīng)過了測試,可以在重負(fù)載下運(yùn)行無誤。


          兼容性的許可證

          Apache Software License (ASL)可以兼容其它任何已知的許可證。 已知的最大例外是GNU Public License (GPL) 和Lesser GNU Public License (LGPL). 重要的是ASL對合作開發(fā)相當(dāng)友好,如果您不想的話,也不會強(qiáng)迫您發(fā)布源代碼。 Apache Software Foundation名下的德高望眾的HTTP服務(wù)器用的是相同的許可證。


          集群式的研發(fā)

          大多數(shù)的Avalon用戶以某種方式做出他們的貢獻(xiàn)。這把開發(fā),調(diào)試和文檔的工作量分散到了一些用戶身上。 這也表明Avalon的代碼經(jīng)過了更廣泛的對等復(fù)查,這在公司開發(fā)中是不可能做到的。 而且,Avalon的用戶支持Avalon。 盡管開放源代碼的項(xiàng)目通常沒有一個幫助平臺或電話支持熱錢,但是我們有一個郵件列表。 您的許多問題可以通過郵件列表得到很快的回答,比一些支持熱線還要快。


          簡化的分析和設(shè)計

          基于Avalon開發(fā)有助于開發(fā)者達(dá)到一種精神狀態(tài)。 在這種精神狀態(tài)下,開發(fā)者的工作集中在如何發(fā)現(xiàn)組件和服務(wù)上。 既然關(guān)于組件和服務(wù)的生存期的細(xì)節(jié)問題都已得到分析和設(shè)計,開發(fā)者只需選擇他們需要的就行了。

          需要重點(diǎn)指出的是,Avalon的開始并不是取代了傳統(tǒng)的面向?qū)ο蠓治龊驮O(shè)計,而是對它進(jìn)行了增強(qiáng)。 您還是使用以前所用的技術(shù),只不過現(xiàn)在您有了一組工具,能更快地實(shí)現(xiàn)您的設(shè)計。



          Avalon已經(jīng)就緒

          Avalon Framework, Avalon Excalibur, 和 Avalon LogKit已經(jīng)準(zhǔn)備好讓您使用了。它們已經(jīng)成熟,只會變得越來越好。 盡管Avalon Phoenix和Avalon Cornerstone正在緊鑼密鼓地開發(fā),但您在它們基礎(chǔ)上寫的代碼將來只需做些不重要的改動就能工作。


          posted on 2006-08-18 20:14 Lansing 閱讀(2389) 評論(0)  編輯  收藏 所屬分類: Java
          <2006年8月>
          303112345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          歡迎探討,努力學(xué)習(xí)Java哈

          常用鏈接

          留言簿(3)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          Lansing's Download

          Lansing's Link

          我的博客

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 旺苍县| 南澳县| 唐山市| 墨玉县| 兰西县| 日土县| 和顺县| 武城县| 普格县| 顺昌县| 池州市| 沁源县| 措勤县| 望城县| 叶城县| 柳江县| 泰兴市| 山阴县| 达州市| 滁州市| 沾益县| 潜山县| 伊宁市| 磐安县| 曲水县| 油尖旺区| 钟祥市| 平罗县| 石柱| 交口县| 延寿县| 嫩江县| 海阳市| 乐业县| 西充县| 宁都县| 松滋市| 万载县| 缙云县| 于田县| 德昌县|