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