一、概述
EJB結構是Java平臺上的服務器端組件模型,設計EJB結構的目的是通過使企業開發人員將注意力只集中于編寫商務邏輯,從而解決上面所提出的問題。這些企業級bean使應用程序編程人員能夠開發與平臺無關、面向事務的分布式應用程序,而且這種應用程序可在眾多廠商的服務器上運行。
SUN定義的EJB規范為Java開發者提供了一個創建分布式業務組件的基礎。EJB是一種實現業務邏輯(Business Logic)的Java組件,而且它遵從EJB規范所定義的約束。EJB組件生存在EJB容器之內,EJB容器提供了一系列的標準服務,包括事務、數據存儲、安全、并發等,它使得應用程序開發者無需從頭開發這些服務。
在一個由J2EE應用服務器支持的企業級分布式應用中,要想盡量發揮EJB的作用,程序員應該: 嚴格遵從EJB規范。使用各種可用的工具輔助Bean開發和兼容性測試。從其他程序員的經驗獲益。
EJB 2.0中有四種類型的EJB組件:
無狀態會話Bean(Stateless Session Bean):提供一種服務,但不保存多個方法調用之間的會話狀態信息。
有狀態會話Bean(Stateful Session Bean):維持會話狀態;每一個實例關聯到一個特定的用戶。
實體Bean(Entity bean):代表著永久性數據的一個對象化描述,常常是數據庫中的行。它們擁有作為唯一標識符的主鍵。實體Bean有兩種運作方式:Container managed persistence entity bean(CMP),Bean managed persistence entity bean(BMP)。
消息驅動的Bean(Messege Drived Bean):這是EJB 2.0新增的類型。消息驅動的Bean實現JMS(Java Message Service)和EJB之間的整合,用來在服務器內執行異步操作。
為了幫助你開發J2EE應用服務器(比如BEA WebLogic Server)支持的企業級分布式應用的EJB,下面是一些筆者EJB開發的經驗和技巧,各個要點的前面以"▲"符號特別注明。我們的討論將從以下幾個方面進行:
事務
EJB安全
創建主鍵類
何時避免使用stateful Session Bean
編寫業務接口
在事務中使用消息驅動的Bean
利用設計模式(Design Pattern)和面向對象的編程思想
二、事務(Transaction)處理技巧
幾乎所有的EJB應用都會在某些時候用到事務(Transaction)。事務確保了正確性和可靠性,對于電子商務、證券業務等應用來說是必不可少的。然而,誤用或者濫用事務會影響性能,甚至可能產生不正確的結果。Session Bean本身不能事務化,理解這一點非常重要。
一個常見的誤解是:當事務被異常中止時,Session Bean的成員變量也會被回退。事實恰好相反,Session Bean只不過是把事務"傳播"給所有它們獲得的資源。例如,假設事務開始時,Session Bean有一個值為"1"的成員變量。在事務處理期間,這個成員變量被設置成了2,同時有一個記錄被插入到了數據庫。如果事務被回退,Bean的成員變量不會恢復0值,但數據庫中的記錄將不再存在。數據庫是一種事務化的資源,它會加入到Session Bean的事務,一旦事務回退,則所有數據庫相關的操作也被回退。
▲ Session Bean本身的狀態不是事務化的。
2.1 用戶交互與事務的性能
如果事務的操作過于復雜、繁多,它們可能導致性能問題。特別地,事務永遠不應該包含用戶輸入操作或其他需要用戶參與才能完成的操作。例如,如果用戶啟動了事務之后,接著又去吃午餐或者去訪問另外一個Web網站,則該事務不會被提交;相反,它將繼續鎖定和占用寶貴的服務器資源。
一般地,所有的事務分界應該出現在服務器上。有許多眾所周知的技術能夠避免長時間事務處于持續打開狀態。當你要求用戶提交、驗證表單時,應該把操作分成兩個事務:Web頁面應該在單一完整的事務之內讀取數據,該事務在表單返回給用戶之前完成提交;然后由用戶根據需要修改數據,表單的更新則在一個新的事務中完成。
▲ 事務處理過程中永遠不應該插入用戶輸入或者其他需要用戶參與的操作。
2.2 Entity Bean:選擇事務管理方式
EJB規范允許Session Bean或者選擇容器(Container)管理的事務,或者選擇Bean用戶自己管理的事務。前者是由EJB容器管理事務,后者是由Bean本身管理事務。對于容器管理的事務,Bean開發者在部署描述器中聲明事務屬性。在這種情況下,EJB容器會在必要時自動啟動和提交事務,Bean開發者無需編寫任何管理事務的代碼。
對于Bean管理的事務,Bean開發者必須利用事務接口顯式地啟動和提交事務。Bean開發者的第一選擇永遠應該是容器管理的事務。如果采用Bean管理的事務,Bean開發者必須確保事務被提交或者回退。雖然象BEA WebLogic Server之類的系統支持事務超時,但Bean開發者不應該依賴系統提供的這種功能,而是應該盡可能早地釋放事務資源。如果采用容器管理的事務,這一切都由EJB容器自動處理,這樣可以提高開發效率。
▲ 盡量選用Container管理的事務,而不是用Bean管理的事務。
三、EJB的安全性
EJB不僅支持一種說明性的安全配置方式,而且還為在Bean代碼內部進行安全檢查提供了一個簡單的可編程接口。在實踐中,EJB的安全配置應該置于應用整體安全模型之下考慮。
在基于Web的應用中,驗證處理在Web層進行是很常見的情形。在這種環境下,EJB層可能只包含很少的安全約束。這種安排簡化了EJB的設計,而且,由于安全檢查在表現層進行,我們可以在不修改EJB層的情況下修改應用的安全策略。然而,帶有獨立可編程客戶端的應用有可能需要直接訪問Session Bean。由于不存在中間層,這時訪問安全必須在EJB層內進行控制(注:筆者不建議在客戶端的應用直接訪問Session Bean,應該把EJB層和表示層(Presentation)分離開,松散耦合)。
說明性安全控制是簡單應用的首選安全控制方法。由于安全約束在部署描述器中聲明,Bean類的業務邏輯之中不需要插入安全檢查代碼,從而使得業務邏輯代碼更加整潔。說明性安全模型以部署描述器中聲明的安全角色為基礎。如果角色的數量固定不變,而且與客戶的數量無關,說明性安全模型最理想。例如,應用可能包含一個用戶角色和一個管理員角色。由于這里只有兩種訪問模式,在部署描述器中聲明這些角色是可行的。
然而,當每一個用戶要求有各不相同的安全約束時,我們不應該再使用說明性的安全模型,這種應用需要在EJB代碼之內用編程方式進行安全檢查。另外,結合運用兩種安全檢查方式的情況也很常見。例如,一個Account(帳戶)Bean可以使用說明性的安全檢查方式確保只有注冊用戶才能訪問任意一個方法,然后,在Bean的代碼之中包含額外的安全約束代碼以確保每一個用戶只能訪問他自己的帳戶信息。
▲ 當應用程序只包含少量的角色時,使用說明性安全檢查模式;當每一個用戶需要單獨進行安全檢查時,使用編程方式進行安全檢查。
四、如何為實體Bean編寫主鍵類
無論在永久性存儲還是EJB容器之內,EJB主鍵類都起著唯一標識符的作用。主鍵類的域常常直接映射到數據庫的主鍵字段。如果主鍵只是一個單一的實體Bean域,而且它屬于Java簡單數據類型(比如java.lang.String),那么,Bean開發者無需編寫定制的主鍵類。 相反,Bean開發者只需在部署描述器中指定類的名字和主鍵域的名字。
如果主鍵映射到一個用戶定義的類型或者多個域,Bean開發者必須編寫定制的主鍵類。這個主鍵類必須實現java.io.Serializable,并包含各個主鍵域。對于CMP Entity Bean,域名字必須匹配Bean類中相應主鍵域的名字,這將使EJB容器能夠把恰當的CMP域賦值給主鍵類中相應的域。例如,我們可以把雇員的主鍵定義為一個復合鍵,這個復合鍵由userName、deptNumber和officeNumber構成,主鍵類的代碼清單如下:
 |
主鍵類包含主鍵域,主鍵域必須是public類型;另外,主鍵類還必須包含一個沒有參數的構造函數。主鍵類必須實現hashCode和equals方法。EJB容器內部要用到大量的數據結構,其中許多都通過主鍵類索引。因此,對于主鍵類來說,正確、高效地實現這兩個方法是至關重要的。
hashCode方法利用主鍵域返回一個整數。這個函數的目的是生成一個可用于索引表的整數。主鍵的hashCode值永遠不應該改變。因此,hashCode值應該從不可改變的值構造得到。
實現equals方法可能稍微復雜一點。equals方法的第一行應該參照自身(this)檢查作為參數傳入的引用,這一步驟檢查的是對equals方法的調用是否針對其自身進行。雖然從表面上看起來這有點兒奇怪,但是,當容器檢查某個數據結構中是否存在特定的主鍵對象時,這是一種常見的操作。
接下來,equals方法應該確認作為參數傳入的引用屬于它自己的類型。如果主鍵類是final類型,這里只需簡單地用instanceof操作符進行檢查。如果主鍵類不是final類型,作為參數傳入的引用可能屬于主鍵類的派生類型,此時,equals方法必須使用getClass().equals確保類型的嚴格匹配。與比較類相比,使用instanceof操作符進行比較的開銷更小,因此,最好把主鍵類定義為final類型。
▲ 主鍵類應該是final類型。
五、什么時候避免使用Stateful Session Bean
Stateful Session Bean代表著單一客戶與單個Bean實例之間一個有狀態信息的會話。Stateful Session Bean不能在多個用戶之間共享。你不應該以Stateful Session Bean的形式模擬一個共享的緩沖區或者任何其他共享的資源。如果要讓多個客戶訪問單個EJB實例,請使用Entity Bean。
▲ Stateful Session Bean不能由多個用戶共享。
由于每一個客戶必須有它自己的Stateful Session Bean實例,Bean實例的數量以及對相關資源的需求將很快增長。如果應用程序允許無狀態編程模式,那么,Stateless Session Bean比Stateful Session Bean有著更好的可伸縮性。 h應用程序使用Stateful Session Bean實例完畢之后,應該總是調用remove方法。這使得EJB容器能夠盡快地釋放容器資源。
▲ Stateless Session Bean比Stateful Session Bean有著更好的可伸縮性。
如果要在Web應用中整合Stateful Session Bean,Bean開發者應該謹慎。Stateful Session Bean不允許并發方法調用。正如前面所提到的,多個請求可能導致對Stateful Session Bean進行并發調用。遺憾的是,這種錯誤通常在應用處于正常負荷狀態下出現,所以測試期間它往往被忽視。由于這個原因,建議只在請求范圍之內使用Stateful Session Bean;對于那些需要在多個請求之間保存數據的應用,請使用Entity Bean或者Servlet會話。
六、編寫業務接口
遠程接口(Remote Interface)和EJB類之間的關系常常使許多初學EJB開發的程序員感到困惑不解。遠程接口和EJB類之間的關系對于讓容器截取所有對EJB的方法調用是必要的。容易令人困惑的地方在于:EJB類實現了遠程接口所定義的方法,但EJB類并不實現遠程接口本身。事實上,EJB類永遠不應該實現遠程接口。雖然EJB規范允許EJB類在事實上實現遠程接口,但這種做法會導致嚴重的、難以理解的BUG。讓EJB類實現遠程接口的問題在于:在這種情況下,EJB類可能被作為參數傳遞給任何期望遠程接口類型為參數的方法。
請記住,遠程接口的存在使得容器能夠截取方法調用以便提供事務、安全之類必需的服務。如果使用的是Bean類,方法調用將直接對Bean對象進行--從而導致了一種危險的情形,即容器不能截獲方法調用,或者在出現錯誤的時候容器不能介入。如果(按照推薦地那樣)EJB類不實現遠程接口,這個問題在編譯時就會顯示出來:當我們把Bean類作為遠程接口類型的參數傳遞時,Java編譯器將拒絕編譯。
▲ 永遠不要在EJB類中實現遠程接口。WebLogic Server提供了一個EJB順從性檢查器,它能夠找出所有已經在遠程接口中定義、但EJB類沒有實現的方法。
七、事務與消息驅動的Bean
消息驅動的EJB整合EJB和JMS。和其他EJB類型一樣,消息驅動的EJB生存在EJB容器之內,而且它也從EJB容器的各種服務受益,比如事務、安全以及并發控制等。然而,消息驅動的EJB不直接與客戶交互。相反,消息驅動的EJB是JMS消息監聽器。客戶把消息發布給JMS目的地,然后,JMS提供者和EJB容器協作,把消息發送給消息驅動的EJB。
與其他EJB一樣,消息驅動的EJB可以利用EJB容器的事務服務。但是,由于這些Bean永遠不會直接與客戶交互,它們永遠不會參與到客戶的事務之中。
與Session Bean一樣,在ejb-jar.xml部署描述器中,消息驅動的EJB可以選擇由Bean管理事務還是由容器分界事務。對于后者,EJB可以指定Required或者NotSupported屬性(因為不存在客戶事務,因此支持其他事務屬性也就沒有必要)。如果事務的屬性是NotSupported,消息驅動的EJB將不參與事務。
如果指定了Required屬性,EJB容器自動啟動事務。從JMS Queue或Topic收到的消息被包含到該事務之中,然后,消息驅動Bean的onMessage方法在該事務環境內被調用。當onMessage方法返回時,EJB容器提交事務。如果事務中止,JMS消息遺留在JMS目的地并被再次發送給消息驅動的EJB。
對于帶有Required事務屬性的消息驅動Bean,中止事務時應該小心對待。事務的中止或者是由于它被顯式地標記為回退,或者是由于出現了系統異常。這時可能出現一個稱為"Poison Message"(毒藥消息)的問題。假設消息驅動的EJB從股票交易單隊列接收交易單,而且它可能遇到不存在的股票代號。當消息驅動的EJB接收到錯誤的消息時,底層邏輯會因為股票代號非法而中止事務。一旦JMS在一個新的事務中再次發送該消息,這個過程重復出現。顯然,這是我們不希望出現的情形。
要解決這種可能出現的問題,一種好方案是分離應用程序錯誤和系統錯誤。應用程序的錯誤,比如非法的股票代號,應該用向出現錯誤的JMS目的地發送錯誤消息的方式處理。采用這種方法之后,事務能夠提交,"Poison Message"問題也就遠離了系統。系統錯誤可能是后端數據庫故障。在這種情況下,事務應該回退。這樣,當數據庫恢復運行時,消息仍舊在隊列中。
八、利用設計模式(Design Pattern)和面向對象的編程思想
目前在開發J2EE應用,都有一些設計模式(Design Pattern)供我們利用,有些是針對EJB的,例如:Data Access object、Session Fa?ade、Value Object等J2EE的設計模式,另外開發人員一定要有面向對象的編程思想和思路,把面向對象的思想應用到設計和開發過程中,這樣才能保證系統的可擴展性、易于維護性等。
|
posted on 2005-10-24 21:02
zjw_albert 閱讀(200)
評論(0) 編輯 收藏