總 攬
一、Server方組件結(jié)構(gòu)
EJB是一種Server方的組件結(jié)構(gòu),它可以非常簡單的開發(fā)基于java的企業(yè)級的分布式對象應(yīng)用。使用EJB可以開發(fā)出易升級的、可靠的、安全的應(yīng)用程序,而不用獨立開發(fā)復(fù)雜的分布式對象框架;EJB可以迅速開發(fā)服務(wù)方應(yīng)用程序,快速建立基于java的服務(wù)方組件。EJB被設(shè)計用來實現(xiàn)企業(yè)中間件服務(wù)的可移植和可重用性。
如果你對企業(yè)計算很陌生,這些概念對你很有用,EJB是個很復(fù)雜的主題,應(yīng)該被充分的解釋。在這一章我們討論有關(guān)EJB的主要概念。首先,我們先討論開發(fā)企業(yè)級軟件是為了干什么?為什么說像EJB的預(yù)包裝的分布式對象體系可以簡化你的生活?在討論中,你將會對服務(wù)端的組件結(jié)構(gòu)有宏觀的了解。
服務(wù)端組件結(jié)構(gòu)的需要
我們必須首先了解開發(fā)者在建立和配置服務(wù)端環(huán)境下的組件時通常需要什么?同時,我們將解決圍繞服務(wù)端開發(fā)所出現(xiàn)的問題,將看到建立一套像EJB標準體系結(jié)構(gòu)的必要性。
軟件組件??一個軟件組件是一段代碼,它用來實現(xiàn)一系列定義好的接口。組件不是完整的
應(yīng)用程序??它們不能被獨立運行。更貼切的說,它們是看作是許多大型問題分割成的小問題。
軟件組件的思想非常有用。公司可以買來定義好的可用來解決某一問題的模塊,將它和其他組件一起編譯用以解決大型問題。
組件結(jié)構(gòu)??為了使組件開發(fā)過程更加容易,需要為建立、管理、維持組件建立規(guī)范。
開發(fā)組件的開發(fā)工具??建立組件時,應(yīng)該允許開發(fā)者集中精力在組件的背后開發(fā)核心邏輯,使開發(fā)者不需要考慮太多的標準細節(jié)問題,從而快速開發(fā)應(yīng)用程序。例如IDE:Symantec的Visual
Cafe,IBM的VisualAge for Java,Inprise的Jbuilder 2,這些工具可以幫助你快速的建立和調(diào)試組件。
管理配置好的組件容器??組件容器為你的組件運行提供了一個運行時環(huán)境。同時也提供了一套供大多數(shù)組件使用的通用服務(wù)。
配置和維持組件工具??從組件提供商購買了組件后,還需要有一套工具來幫助配置和維持這些組件。
Java:完美實現(xiàn)組件結(jié)構(gòu)
對于成功運用在解決商業(yè)問題的一個組件,無論是組件開發(fā)商還是使用組件的客戶都必須遵守調(diào)用組件方法的語法和語義。開發(fā)商出版有關(guān)調(diào)用組件的規(guī)范,客戶代碼必須遵守它們。
為了防止當(dāng)開發(fā)商提供了一個新版本的組件,或更改了組件規(guī)范,客戶方就必須重新編寫或編譯它們的代碼,因此面向?qū)ο笤O(shè)計提出了一種新的設(shè)計方法:通過從組件的實現(xiàn)中分離出接口。
組件接口
組件邏輯
為了使接口/實現(xiàn)分離這種方式變得更有效,開發(fā)者必須為組件接口寫客戶代碼(這被稱為基于接口的程序設(shè)計),通過這種分離,可以改變服務(wù)方邏輯而不用更改客戶方代碼。
Java中的組件結(jié)構(gòu)
現(xiàn)在讓我們了解什么是組件結(jié)構(gòu),看看在Java世界里存在那些組件結(jié)構(gòu)。首先,你應(yīng)該了解什么是JavaBeans,JavaBeans組件是小的應(yīng)用程序塊,可以使用JavaBean去集合成大型的組件,從而編譯完整的應(yīng)用程序。然而,你不能配置一個JavaBean,因為一個JavaBean不是一個完全的應(yīng)用程序。JavaBean可以幫助你構(gòu)建更大的可配置的軟件。因為不需要配置,JavaBean不需要運行時環(huán)境,也不需要容器來對它進行實例化、破壞、提供其他服務(wù)的操作。應(yīng)用程序本身是由許多JavaBean構(gòu)成的。
相比較,EJB標準定義了一個組件結(jié)構(gòu)來配置組件,被稱為企業(yè)級的Beans。企業(yè)級的Beans是比較大的、粗糙的被用來配置的應(yīng)用程序組件。他們能被破壞,也能被用來和其他組件組合成更大的應(yīng)用程序系統(tǒng)。可配置組件在容器內(nèi)被配置,容器提供了對組件的運行時服務(wù)。例如實例化。
企業(yè)級Beans和兩種其他的Java組件十分相似:applets和servlets。Applets可以在Web頁中配置,瀏覽器的Appletviewer為其提供了運行時的容器。Servlets可以在Web
Server中被配置,Webserver的servlet
engine為提供運行時的容器。企業(yè)級Beans可以在應(yīng)用程序服務(wù)器中被配置,應(yīng)用服務(wù)器為其提供了運行時的容器。
它們?nèi)咧g真正的不同是每個組件類型可以延伸的域大小。
Applets是輕便Java程序,它能夠被任意的下載和運行。例如它可以從Web Server中下載到瀏覽器。
Servlets是可以被用來延伸Web
server功能的網(wǎng)絡(luò)組件。它是面向請求/回答的,從許多客戶端獲得請求,再給它們發(fā)應(yīng)答。這樣使得它被廣泛用于執(zhí)行Web任務(wù)。
Applets和servlets適用于客戶方操作。而企業(yè)級Bean不擴展客戶端操作,它是服務(wù)端組件,執(zhí)行服務(wù)端操作;例如執(zhí)行復(fù)雜運算、執(zhí)行大量的商業(yè)傳輸。
服務(wù)端所需
一個完整的組件結(jié)構(gòu)遵循以下方式:
開發(fā)者寫可重用組件
提供商寫組件容器:用以給組件提供運行時環(huán)境和服務(wù)
提供商提供開發(fā)、配置和維持工具。
這些方式保證了可重用性。
多層結(jié)構(gòu)
服務(wù)方配置是用來支持用戶同時執(zhí)行并發(fā)、安全、可靠、有效的操作的軟件。
服務(wù)方配置一般被分成多層。每層實現(xiàn)不同的功能,每層有一個或多個組件。注意:層是個抽象的概念,在物理上它并不存在。下面有個分層的例子:
代表層:這些容器組件處理用戶接口和用戶交互。例如,代表層是一個單獨的應(yīng)用程序,可以用VB來寫。基于Web配置的代表層可以使用java
servlets,Java server pages或java applets。
業(yè)務(wù)邏輯層:是用來解決業(yè)務(wù)問題的容器組件的集合。這些組件常被用來處理高執(zhí)行度的工作,它們常用安全性的語言來寫,例如:Java、C。
數(shù)據(jù)層:被業(yè)務(wù)邏輯層用來保持狀態(tài)的持久性。數(shù)據(jù)層的中心是一個或多個數(shù)據(jù)庫。
分層的優(yōu)點是盡量隔離各層。
兩層結(jié)構(gòu)
通常,大多數(shù)配置是兩層結(jié)構(gòu)。將業(yè)務(wù)邏輯層和另外兩層的一個合并:可能和代表層合并。可能和數(shù)據(jù)層合并。
代表層和業(yè)務(wù)邏輯層合并
如把代表層和業(yè)務(wù)邏輯層這一層作為客戶端,數(shù)據(jù)層作為服務(wù)端,則形成fat客戶端,而thin服務(wù)端的情況。在兩層結(jié)構(gòu)中,客戶端應(yīng)用程序通過數(shù)據(jù)庫橋API與數(shù)據(jù)層相連。例如:ODBC、JDBC。
這樣的兩層結(jié)構(gòu)有以下特征:
配置代價非常高。數(shù)據(jù)庫驅(qū)動必須在每一個客戶層上安裝和配置。
數(shù)據(jù)庫驅(qū)動交換代價高。轉(zhuǎn)接一個數(shù)據(jù)庫驅(qū)動,需要在各個客戶端重新安裝客戶端驅(qū)動。
數(shù)據(jù)庫類型轉(zhuǎn)型代價高。
業(yè)務(wù)邏輯移植代價高。
數(shù)據(jù)庫連接代價高。
網(wǎng)絡(luò)性能發(fā)揮低。
將業(yè)務(wù)邏輯層部分并入數(shù)據(jù)層
形成客戶端thin,而服務(wù)端fat的情況。
N層結(jié)構(gòu)
其中將代表層、業(yè)務(wù)邏輯層、數(shù)據(jù)層各自為一層。
特點:
配置代價低。
數(shù)據(jù)庫交換代價低。
業(yè)務(wù)邏輯移植代價低。
可以和防火墻結(jié)合配置安全部分。
資源可以被重用。
每層都有較大的靈活性
J2EE技術(shù):
J2EE是中間件服務(wù)套件,對于服務(wù)端應(yīng)用的開發(fā)者來說,使開發(fā)變得更容易。它包含:
EJB:它定義了怎樣去寫服務(wù)方組件、提供了一個在組件和管理它們的應(yīng)用服務(wù)器之間的標準。EJB是J2EE的核心。
RMI和RMI-IIOP:RMI??遠程過程調(diào)用;RMI-IIOP是RMI的擴展,它可以使用IIOP協(xié)議,可以被CORBA整合使用。
JNDI:JNDI用來在網(wǎng)絡(luò)上區(qū)分組件和其他資源的位置。
JDBC:是聯(lián)系數(shù)據(jù)庫的橋梁
推出EJB1.0后的幾個月,第一個基于EJB的應(yīng)用服務(wù)BEA’s WebLogic就誕生了。
二、EJB總攬
EJB采用divide-and-conquer的方式來實現(xiàn)服務(wù)方的計算。事實上,EJB標準定義了6個不同的部分協(xié)同工作。每個部分都被作為EJB配置成功的關(guān)鍵。在這兒,我們分別討論它們的作用。
Who’s Who in EJB
在EJB的世界里,業(yè)務(wù)解決方案被分為四個階段發(fā)展:
1、
業(yè)務(wù)邏輯模塊化。ERP、金融、電子商務(wù)提供商將產(chǎn)品模塊化,集成在可重用EJB組件中,這樣就意味著任何有EJB和J2EE知識的人都可以使用這些組件。
2、 業(yè)務(wù)組件需要運行在分布式的、企業(yè)級配置的多層環(huán)境中。這樣就需要不同的中間件,推動了中間件的發(fā)展。
3、 應(yīng)用服務(wù)器和可重用組件捆綁。
4、 完整的系統(tǒng)配置。
EJB有好的可移植性,它被分為完全不同的6個部分:
6個部分之間的關(guān)系
The relationship between EJB servers and EJB containers.
EJB規(guī)范定義了完成一個基于EJB組件的分布式應(yīng)用所需要的六個角色,這六個角色可以由不同的廠商來擔(dān)當(dāng),也可以某個廠商擔(dān)當(dāng)多個角色。這六個角色是:
Enterprise Bean Provider EJB組件開發(fā)者,負責(zé)編寫EJB組件,EJB組件開發(fā)者是應(yīng)用領(lǐng)域的專家。
Application Assembler 應(yīng)用組合者,負責(zé)將各種EJB組合成一個完整的應(yīng)用系統(tǒng)。
Deployer 部署者,負責(zé)將包含EJB組件的ejb-jar文件部署到應(yīng)用服務(wù)器中。
EJB Server Provider EJB服務(wù)器提供者,負責(zé)實現(xiàn)一些底層的系統(tǒng)服務(wù),如交易管理等。EJB服務(wù)器提供者是系統(tǒng)領(lǐng)域的專家。
EJB Container Provider
EJB容器提供者,負責(zé)提供EJB組件的運行環(huán)境,EJB容器提供者和EJB服務(wù)器提供者一般是由相同的廠商擔(dān)當(dāng),提供的產(chǎn)品叫應(yīng)用服務(wù)器。
System Administrator 系統(tǒng)管理員負責(zé)為EJB服務(wù)器和容器提供一個企業(yè)級的計算和網(wǎng)絡(luò)環(huán)境。
EJB配置的物理部分分為以下幾部分:
EJB Container
是裝載Enterprise Beans及Enterprise
Beans生存的空間,是Beans在運行時相互聯(lián)接的接口,Container必須實現(xiàn)與Beans之間的約定,Continer提供者也應(yīng)該提供配置工具以便能方便地配置Beans,使其適合各種運行環(huán)境。
EJB Server
主要處理復(fù)雜的底層任務(wù),如分布式對象、分布式事務(wù)處理的管理、系統(tǒng)OS級的訪問、網(wǎng)絡(luò)、數(shù)據(jù)庫訪問等。EJB Server與EJB
Container之間的合約在EJB 1.0 規(guī)范中有詳細說明。大多EJB Server提供者也是EJB Container提供者。
開發(fā)Beans
一、會話Beans介紹
按功能可把EJB分為兩類:Session Beans 與 Entity Beans。
企業(yè)級Bean類
為了使bean可以在任一容器中工作,bean必須被附在接口中。在EJB中,在enterprise bean
class中提供了企業(yè)級bean組件的實現(xiàn)。這是個簡單的遵循接口的java類。
一個enterprise bean class 包含對組件的實現(xiàn)細節(jié)。會話bean的實現(xiàn)不同于實體bean的實現(xiàn),
一個Session Beans針對單一的客戶完成一次連接或會話,其生存直到客戶完成連接與會話,或系統(tǒng)意外中止。當(dāng)一個新的客戶從EJB
Server訪到一個Session Beans時,那么EJB Container創(chuàng)建一個新的Session
Beans實例,其運行直到會話結(jié)束,Session Beans必須實現(xiàn)接口javax.ejb.SessionBean。
Entity Beans實現(xiàn)接口javax.ejb.EntityBean,其描述了特定數(shù)據(jù)源中的數(shù)據(jù),能長時間存在于EJB
Container中,不會隨系統(tǒng)的意外中止而消失,并且可以讓多個客戶同時訪問。
EJB規(guī)范定義了許多bean類能夠?qū)崿F(xiàn)的標準接口。定義了所有的bean類必須有的方法。容器調(diào)用這些方法用來管理bean。
所有bean類(無論是會話bean還是實體bean)必須實現(xiàn)的最基本的接口是javax.ejb.EnterpriseBean接口。
public interface javax.ejb.EnterpriseBean extends java.io.Serializable
{
}
值得注意的是:它繼承了java.io.Serializable。
所有的會話bean必須實現(xiàn)javax.ejb.SessionBean,
所有的實體bean必須實現(xiàn)javax.ejb.EntityBean.
EJB對象
當(dāng)客戶想使用enterprise bean
class的一個實例時,客戶不必直接在實際的實例上調(diào)用方法,調(diào)用過程被EJB容器截取,bean實例被容器中對象所代表。
1、Enterprise bean
class不能通過網(wǎng)絡(luò)直接被調(diào)用,我們知道EJB容器可以操縱網(wǎng)絡(luò),因此它通過容器將bean包裝成可在網(wǎng)絡(luò)上使用的對象。
2、通過截取請求,容器可以自動執(zhí)行許多必要的管理工作。
3、EJB容器可以跟蹤哪個方法被調(diào)用,在系統(tǒng)管理者的用戶接口上顯示其用法等等。
因此,EJB容器可以看作間接的存在于客戶代碼和bean之間的層。這個間接的層使用單獨的網(wǎng)絡(luò)對象來表示自己,這個對象稱為EJB對象。
EJB對象作為容器物理的部分;所有的EJB對象都有針對容器特殊要求的代碼。因此,容器提供商提供專門工具,用來自動為EJB對象產(chǎn)生類文件。
遠程接口
我們前面了解到,bean客戶調(diào)用EJB對象上的方法來代替調(diào)用bean,為了執(zhí)行它,EJB對象必須復(fù)制bean類中的每個業(yè)務(wù)方法。但是,怎樣才能使自動產(chǎn)生的EJB對象知道復(fù)制了哪個方法呢?這就用到了bean提供者寫的一個特殊的接口,這個接口復(fù)制所有的與bean類相關(guān)聯(lián)的業(yè)務(wù)邏輯方法。這個接口被稱為遠程接口。
這個接口必須遵循EJB規(guī)范的定義,所有的遠程接口必須從sun公司提供的通用接口繼承而來,即javax.ejb.EJBObject。
EJB對象
public interface javax.ejb.EJBObject
extends java.rmi.Remote
{
public abstract javax.ejb.EJBHome getEJBHome()
throws java.rmi.RemoteException;
public abstract java.lang.Object getPrimaryKey()
throws java.rmi.RemoteException;
public abstract void remove()
throws java.rmi.RemoteException,
javax.ejb.RemoveException;
public abstract javax.ejb.Handle getHandle()
throws java.rmi.RemoteException;
public abstract boolean isIdentical(javax.ejb.EJBObject)
throws java.rmi.RemoteException;
}
以上是對于所有EJB對象必須擁有的方法,你不需實現(xiàn)這些方法,這些方法的實現(xiàn),生成EJB對象時由容器自動生成。
客戶端代碼通過調(diào)用javax.ejb.EJBObject的方法來和bean協(xié)同工作。
Java RMI和EJB對象
你應(yīng)當(dāng)注意到:java.ejb.EJBObject繼承了Java.rmi.Remote。Java.rmi.Remote接口是java遠程方法調(diào)用(RMI)的一部分,任一個實現(xiàn)java.rmi.Remote的對象都是rmote對象,它可以被另外的java虛擬機所調(diào)用。
被容器提供的EJB對象實現(xiàn)了遠程接口,同時也間接實現(xiàn)了java.rmi.Remote,這樣也就意味著你的EJB對象是完全符合網(wǎng)絡(luò)需要的,可以被網(wǎng)絡(luò)上的其他java虛擬機調(diào)用。當(dāng)然,EJB接口也必須遵守EJB規(guī)范。
EJB遠程接口必須遵守java的RMI遠程接口規(guī)范。例如:錯誤處理,二者相同。
遠程接口同樣也必須遵守java RMI參數(shù)傳遞規(guī)范。不是什么都可以通過VM方法調(diào)用來在網(wǎng)絡(luò)上傳遞,傳遞的參數(shù)必須符合RMI類型。
EJB也繼承了RMI的優(yōu)點,對于RMI,你正在調(diào)用的遠程對象的物理地址是不可見的。這個特點同樣也適用于EJB。客戶代碼不必關(guān)心正使用的EJB對象是在鄰近的計算機上還是從internat傳遞來的。這樣,EJB對象可以和客戶端處在同一個java
VM中。
EJB保證了本地分布式組件的透明度。這種透明對于多層配置來說是非常必要的。客戶端代碼是非常容易移植的,不受限于特殊的多層配置。EJB容器可以以最佳化方式在本地執(zhí)行。
Home對象
我們看到,客戶端代碼處理EJB對象,而從不直接操作beans。那么,客戶端如何得到EJB對象的參考呢?
客戶端不直接將EJB對象實例化。因為EJB對象可以存在于不同的機器中。同樣的,EJB使本地透明化,因此客戶端不知道它的確切所在。
客戶端代碼通過EJB對象工廠得到EJB對象的參考。EJB規(guī)范里稱這種工廠為home對象。它主要起一下作用:
建立EJB對象。
找到已經(jīng)存在的EJB對象。
刪除EJB對象。
在一些細節(jié)方面,EJB對象工廠同EJB對象的特征相同。
Home接口
Home接口簡單的定義了建立、刪除和尋找EJB對象的方法。容器的home對象實現(xiàn)了home接口。
通常,EJB定義了所有home接口必須支持的許多方法,這些必須的方法被定義在javax.ejb.EJBHome接口上,home接口必須繼承Java.ejb.EJBHome接口。
public interface javax.ejb.EJBHome
extends java.rmi.Remote
{
public abstract EJBMetaData getEJBMetaData()
throws java.rmi.RemoteException;
public abstract void remove(Handle handle)
throws java.rmi.RemoteException
javax.ejb.RemoveException;
public abstract void remove(Object primaryKey)
throws java.rmi.RemoteException,
javax.ejb.RemoveException;
}
javax.ejb.EJBHome接口
注意javax.ejb.EJBHome繼承了java.rmi.Remote,這意味著home接口同樣也支持RMI遠程對象,傳遞的參數(shù)和RMI也相同。
Home對象
所有home對象所需的方法
配置描述符
配置描述符允許EJB容器向企業(yè)級的bean組件提供隱含的中間件服務(wù)。隱含的中間件服務(wù)是bean可以獲得不必將任何中間件API解碼,可以自動獲得服務(wù)的一種服務(wù)。
Bean的特殊屬性
最后,你還需要有一個基于java的bean的屬性文件。Bean在運行時讀這些屬性,這些屬性在使用bean函數(shù)時會被用到。
Ejb-jar文件
一旦生成bean的類、home接口、遠程接口、配置描述符和bean的屬性,我們就可以把它們打包成一個實體。這個實體稱作Ejb-jar文件。這是個壓縮文件。
建立Ejb-jar文件
什么是會話bean
一個Session Beans針對單一的客戶完成一次連接或會話,其生存直到客戶完成連接與會話,或系統(tǒng)意外中止。Session
Beans必須實現(xiàn)接口javax.ejb.SessionBean。
會話bean的生存期
會話bean和實體bean的主要不同是它們的生存期的長短。會話bean的生存期短。與客戶的會話時間相當(dāng)。在與客戶連接端開時,EJB容器會破壞會話bean。
相反,實體bean可以存活相當(dāng)長的時間,實體bean是永久存取的一部分,例如:數(shù)據(jù)庫。
會話bean不能保存永久的存儲數(shù)據(jù),但是,它可以進行數(shù)據(jù)庫操作。
所有的會話bean都需要管理callback方法,容器定時的調(diào)用它,用來對bean的重要事件發(fā)出警告。這個方法僅能被容器調(diào)用。
Conversational versus Nonconversational Session Beans
如何寫會話Bean
寫會話bean的類,必須實現(xiàn)javax.ejb.SessionBean接口
public interface javax.ejb.SessionBean
extends javax.ejb.EnterpriseBean
{
public abstract void setSessionContext(SessionContext ctx)
throws java.rmi.RemoteException;
public abstract void ejbPassivate()
throws java.rmi.RemoteException;
public abstract void ejbActivate()
throws java.rmi.RemoteException;
public abstract void ejbRemove()
throws java.rmi.RemoteException;
}
會話bean和實體bean都繼承了javax.ejb.EnterpriseBean接口
讓我們詳細看看接口中的各種方法:
setSessionContext(SessionContext ctx)
容器調(diào)用這個方法來通過會話上下文與bean連接。Bean可以通過會話上下文向容器查詢當(dāng)前事物的狀態(tài)和當(dāng)前的安全狀態(tài)等。
import javax.ejb.*;
public class MyBean implements SessionBean {
private SessionContext ctx;
public void setSessionContext(SessionContext ctx) {
this.ctx = ctx;
}
...
}
ejbCreate(…)
用來初始化你的會話bean,可以定義多個不同參數(shù)的ejbCreate()方法來用不同的方法初始化bean。
import javax.ejb.*;
public class MyBean implements SessionBean {
private int memberVariable;
public void ejbCreate(int initialValue) {
this.memberVariable = initialValue;
}
...
}
ejbCreate()方法是容器可以調(diào)用的callback方法,客戶端代碼不能調(diào)用它,因為客戶端不能直接處理beans??他們必須通過容器,但是客戶端必須采用某種方法向ejbCreate方法傳遞參數(shù),客戶端提供初始化參數(shù)。Home接口是客戶端用來初始化調(diào)用的接口工廠。你必須在home接口中復(fù)制每一個ejbCreate()方法,例如:如果在bean類中你有下面的ejbCreate方法
public void ejbCreate(int i) throws ...
那么你必須在你的home接口里有下面的create()方法
public void create(int i) throws ...
客戶端調(diào)用home接口中的create()方法,將參數(shù)傳遞給ejbCreate()。
EjbPassivate()
如果出現(xiàn)太多的實例bean,EJB容器可以將它們中的一些鈍化,將它們寫到臨時的存出空間例如數(shù)據(jù)庫或文件系統(tǒng)。容器釋放它們所申請的空間。
import javax.ejb.*;
public class MyBean implements SessionBean {
public void ejbPassivate() {
<close socket connections, etc...>
}
...
}
ejbActivate()
當(dāng)客戶需要使用被鈍化的bean時,容器將被鈍化的bean重新導(dǎo)入內(nèi)存,激活它們。
Bean又被導(dǎo)致內(nèi)存,這時需要重新得到bean所需要的資源。
import javax.ejb.*;
public class MyBean implements SessionBean {
public void ejbActivate() {
<open socket connections, etc...>
}
...
}
ejbRemove()
當(dāng)容器將會話bean實例remove掉時,調(diào)用此方法。所有的bean都有這種方法,它沒有參數(shù),它將釋放所有以分配的資源。
import javax.ejb.*;
public class MyBean implements SessionBean {
public void ejbRemove() {
<prepare for destruction>
}
...
}
容器可以在任何時候調(diào)用ejbRemove()方法,但如果遇到異常,則有可能禁止容器調(diào)用此方法。
業(yè)務(wù)方法
應(yīng)該定義一些解決業(yè)務(wù)問題的方法:例如:
import javax.ejb.*;
public class MyBean implements SessionBean {
public int add(int i, int j) {
return (i + j);
}
...
}
因為客戶端要調(diào)用這些方法,因此,你必須在bean的遠程接口中列出這些方法。
如何調(diào)用會話beans
我們現(xiàn)在來看客戶方,客戶方要通過使用一個或多個bean解決一些現(xiàn)實的問題。
有兩種不同的客戶方:
Java RMI-based clients:這種客戶方通過使用JNDI定位對象,使用JTA控制事務(wù)。
CORBA客戶方:客戶方也可以使用CORBA標準來寫。CORBA客戶方使用CORBA名字服務(wù)(COS
Naming)在網(wǎng)絡(luò)上定位對象,使用CORBA的OTS控制事務(wù)。
無論是用CORBA還是RMI,典型的客戶端代碼都必須實現(xiàn):
1、 定位Home接口
2、 使用Home接口建立EJB對象
3、在EJB對象上調(diào)用業(yè)務(wù)方法
4、清除EJB對象
定位Home接口
客戶端使用JNDL定位Home對象。
J2EE中JNDL的作用
J2EE的目標之一是使應(yīng)用程序?qū)崿F(xiàn)“write once,run
anywhere”。任何的運行在企業(yè)級配置的java代碼在多層結(jié)構(gòu)中應(yīng)該是不受約束的。因此必須實現(xiàn)定位的透明化。
J2EE通過使用JNDL來實現(xiàn)定位的透明化。已有目錄服務(wù)的產(chǎn)品如Netscape的Directory Server,微軟的Active
Directory,IBM的Lotus Notes。
通常我們使用目錄服務(wù)存儲用戶名、密碼、機器位置、打印機位置等等。J2EE擴展目錄服務(wù)存儲資源的本地信息,這些資源也可以是Home對象、企業(yè)級bean的環(huán)境屬性、數(shù)據(jù)庫驅(qū)動、信息服務(wù)驅(qū)動和其他資源。使用目錄服務(wù),在些應(yīng)用程序代碼時可以不必關(guān)心確切的機器名字和機器地址,這樣保證了代碼的可移植性。無論資源在何處,都不需要重新編譯代碼。
JNDL通過為本地用戶、機器、網(wǎng)絡(luò)、對象和服務(wù)提供一個標準接口向企業(yè)級配置中增加值。
為了在J2EE中定位資源,必須實現(xiàn)以下兩步:
1、 用配置描述符中的“綽號”關(guān)聯(lián)資源,J2EE將向資源綁定綽號。
2、 資源的客戶端使用JNDL中的綽號定位資源。
怎樣使用JNDL定位Home對象
客戶端不必關(guān)心Home對象在網(wǎng)絡(luò)的什么地方。JNDL為Home對象提供綽號來定位Home對象。通過綽號可以得到Home對象的參考。
具體點,客戶端代碼必須執(zhí)行以下幾步來通過JNDL得到參考。
1、 建立環(huán)境。必須配置將要使用目錄服務(wù),包括為驗證身份所需的用戶名、密碼。
2、 建立初始的上下文。初始上下文是連接目錄結(jié)構(gòu)的本地出發(fā)點。通過初始上下文得到設(shè)定的環(huán)境屬性。
3、 得到Home對象。執(zhí)行l(wèi)ookup()操作,返回一個RMI遠程對象。
得到Home對象的參考
/*
* Get System properties for JNDI initialization
*/
Properties props = System.getProperties();
/*
* Form an initial context
*/
Context ctx = new InitialContext(props);
/*
* Get a reference to the home object - the
* factory for EJB objects
*/
MyHome home = (MyHome) ctx.lookup("MyHome");
建立EJB對象
得到Home對象以后,可以將Home對象作為建立EJB對象的工廠。調(diào)用create()方法建立EJB對象。
MyRemoteInterface ejbObject = home.create();
無參數(shù)是因為無狀態(tài)beans不需要初始參數(shù)。
調(diào)用方法
客戶端有了EJB對象以后就可以通過它來調(diào)用方法。當(dāng)客戶端在EJB對象上調(diào)用方法時,EJB對象必須選擇一個bean實例來應(yīng)答。EJB對象可以建立一個新的實例或是重用已經(jīng)存在的實例。
ejbObject.add();
破壞EJB對象
調(diào)用EJB或Home對象上的remove()方法來破壞EJB對象。
ejbObject.remove();
無狀態(tài)會話bean基礎(chǔ)
無狀態(tài)會話bean是可以模仿業(yè)務(wù)過程的組件,它可以在單獨的方法調(diào)用中被執(zhí)行。Stateless Session
Bean不能夠維持一個調(diào)用客戶的狀態(tài),在一個方法調(diào)用中,Stateless Session Bean
可以維持調(diào)用客戶的狀態(tài),當(dāng)方法執(zhí)行完,狀態(tài)不會被保持。在調(diào)用完成后,Stateless Session
Bean被立即釋放到緩沖池中,所以Stateless Session Bean具有很好的伸縮性,可以支持大量用戶的調(diào)用。
無狀態(tài)會話beans的特點
沒有對話狀態(tài)
無狀態(tài)會話bean可以擁有內(nèi)部狀態(tài),它們的狀態(tài)不能為特殊的客戶端定制。這意味著所有的無狀態(tài)bean對于客戶端是無差別的,客戶端也不能分離它們。客戶端必須將所有的必需的客戶端數(shù)據(jù)作為業(yè)務(wù)邏輯方法的參數(shù)傳給無狀態(tài)bean,無狀態(tài)bean可以從外部資源(例如數(shù)據(jù)庫)獲得所需的數(shù)據(jù)。
初始化無狀態(tài)bean只有一種方法
我們知道會話bean的初始化調(diào)用ejbCreate()方法,因為無狀態(tài)會話bean不能夠在方法調(diào)用之間保留狀態(tài),因此它也不能在客戶端給ejbCreate()調(diào)用傳遞數(shù)據(jù)以后保留狀態(tài)。調(diào)用不帶參數(shù)的ejbCreate()或create()。
容器可以聚集和重用無狀態(tài)會話Bean
構(gòu)建“Hello,World!”遠程接口
package com.wiley.compBooks.roman.session.helloworld;
import javax.ejb.*;
import java.rmi.RemoteException;
import java.rmi.Remote;
/**
* This is the HelloBean remote interface.
*
* This interface is what clients operate on when
* they interact with EJB objects. The container
* vendor will implement this interface; the
* implemented object is the EJB object, which
* delegates invocations to the actual bean.
*/
public interface Hello extends EJBObject {
/**
* The one method - hello - returns a greeting to the client.
*/
public String hello() throws java.rmi.RemoteException;
}
Source 4.1 Hello.java.
Hello接口繼承了EJBObject接口,EJBObject繼承Remote接口,因此hello可以拋出rmi異常。
下面建立bean,實現(xiàn)業(yè)務(wù)方法:hello()。
他實現(xiàn)了javax.ejb.SessionBean接口
package com.wiley.compBooks.roman.session.helloworld;
import javax.ejb.*;
/**
* Demonstration stateless session bean.
*/
public class HelloBean implements SessionBean {
//
// EJB-required methods
//
public void ejbCreate() {
System.out.println("ejbCreate()");
}
public void ejbRemove() {
System.out.println("ejbRemove()");
}
public void ejbActivate() {
System.out.println("ejbActivate()");
}
public void ejbPassivate() {
System.out.println("ejbPassivate()");
}
public void setSessionContext(SessionContext ctx) {
System.out.println("setSessionContext()");
}
//
// Business methods
//
public String hello() {
System.out.println("hello()");
return "Hello, World!";
}
}
Source 4.2 HelloBean.java
注意:不需要實現(xiàn)自己的遠程接口,初始化方法不帶參數(shù)。破壞bean時,使用比較簡單的ejbRemove()方法。ejbActivate()
和ejbPassivate()方法不需應(yīng)用在無狀態(tài)會話bean,因此,這兩個方法為空。
建立“Hello,World!”Home接口
Home接口繼承了javax.ejb.EJBHome。Home接口為EJB對象擴展了一個不帶參數(shù)的方法??create()方法。
package com.wiley.compBooks.roman.session.helloworld;
import javax.ejb.*;
import java.rmi.RemoteException;
/**
* This is the home interface for HelloBean. This interface
* is implemented by the EJB Server´s glue-code tools - the
* implemented object is called the Home Object and serves
* as a factory for EJB Objects.
*
* One create() method is in this Home Interface, which
* corresponds to the ejbCreate() method in HelloBean.
*/
public interface HelloHome extends EJBHome {
/*
* This method creates the EJB Object.
*
* @return The newly created EJB Object.
*/
Hello create() throws RemoteException, CreateException;
}
creat方法拋出了a java.rmi.RemoteException和aavax.ejb.CreateException.異常。
寫配置描述符
在EJB1.0中,配置描述符是作為文件存儲在磁盤上的java對象。在EJB1.1種,配置描述符是一個XML文檔。EJB容器或IDE環(huán)境應(yīng)該提供生成配置描述符的工具。
配置描述符的設(shè)置
bean home的名字
企業(yè)級bean類名
home接口類名
遠程接口類名
Re-entrant
狀態(tài)或無狀態(tài)
會話時間
HelloBean的配置描述符
環(huán)境屬性
bean通過使用此信息來適應(yīng)不同的特殊環(huán)境。
Ejb-jar文件
我們需要將我們所需要的文件打包成Ejb-jar文件。
企業(yè)級的bean
遠程接口
home接口
配置描述符,包括屬性
以上這些必須被包含進Ejb-jar文件。在EJB1.0中,jar文件理有一個文本文件的列表。它表示jar的詳細信息。它用來鑒別哪個企業(yè)bean在Ejb-jar文件。在EJB1.1中,XML文件包含了所有的必要信息。
生成Ejb-jar文件
jar cmf ..\manifest HelloWorld.jar *
配置bean
最后,我們還需要在Ejb容器中配置bean。常常執(zhí)行一下步驟:
Ejb-jar文件的檢驗
容器工具來產(chǎn)生EJB對象和home對象
容器工具來生成RMI所需的stubs和skeletons
寫無狀態(tài)bean的客戶代碼
package com.wiley.compBooks.roman.session.helloworld;
import javax.ejb.*;
import javax.naming.*;
import java.rmi.*;
import java.util.Properties;
/**
* This class is an example of client code that invokes
* methods on a simple stateless session bean.
*/
public class HelloClient {
public static void main(String[] args) {
try {
/*
* Get System properties for JNDI initialization
*/
Properties props = System.getProperties();
/*
* Form an initial context
*/
Context ctx = new InitialContext(props);
/*
* Get a reference to the home object
* (the factory for EJB objects)
*/
HelloHome home = (HelloHome) ctx.lookup("HelloHome");
/*
* Use the factory to create the EJB Object
*/
Hello hello = home.create();
/*
* Call the hello() method, and print it
*/
System.out.println(hello.hello());
/*
* Done with EJB Object, so remove it
*/
hello.remove();
} catch (Exception e) {
e.printStackTrace();
}
}
}
客戶端代碼執(zhí)行了一下任務(wù):
定位home接口
使用home接口建立EJB對象
調(diào)用EJB對象上的hello()
移走EJB對象
運行
首先運行應(yīng)用服務(wù)器。對于BEA的WebLogic,執(zhí)行
t3server
客戶端執(zhí)行:
java -Djava.naming.factory.initial=
weblogic.jndi.TengahInitialContextFactory
-Djava.naming.provider.url=
t3://localhost:7001
com.wiley.compBooks.roman.session.helloworld.HelloClient
服務(wù)端輸出:
setSessionContext()
ejbCreate()
hello()
ejbRemove()
客戶端輸出:
Hello, World!
狀態(tài)會話Bean基礎(chǔ)
Stateful Session Bean可以一對一的維持某個調(diào)用客戶的狀態(tài),并且在不同的方法調(diào)用中維持這個狀態(tài),
由于對于每一個并發(fā)用戶,必須有一個對應(yīng)的Stateful Session Bean,為了提高系統(tǒng)的效率,Stateful Session
Bean可以在一定的客戶空閑時間后被寫入二級存儲設(shè)備(如硬盤),在客戶發(fā)出新的調(diào)用請求后,再從二級存儲
設(shè)備恢復(fù)到內(nèi)存中。但是在多用戶下,Stateless Session Bean運行效率高于Stateful Session Bean。
javax.ejb.EnterpriseBean接口繼承了java.io.Serializable,用以實現(xiàn)寫入讀出操作。
當(dāng)EJB容器調(diào)用ejbPassivate()方法鈍化了bean之后,就可以把它寫入二級存儲設(shè)備,然后容器調(diào)用ejbActivate()方法激活bean,把它從二級存儲設(shè)備中讀出。
狀態(tài)bean的鈍化過程
計數(shù)bean的遠程接口
遠程接口定義了一個業(yè)務(wù)方法count(),它將在企業(yè)bean類中實現(xiàn)。
激活狀態(tài)bean
package com.wiley.compBooks.roman.session.count;
import javax.ejb.*;
import java.rmi.RemoteException;
/**
* These are CountBean´s business logic methods.
*
* This interface is what clients operate on when they
* interact with EJB objects. The container vendor will
implement this interface; the implemented object is
* the EJB Object, which delegates invocations to the
* actual bean.
*/
public interface Count extends EJBObject {
/**
* Increments the int stored as conversational state
*/
public int count() throws RemoteException;
}
Source Count.java
package com.wiley.compBooks.roman.session.count;
import javax.ejb.*;
/**
* Demonstration Stateful Session Bean. This bean is
* initialized to some integer value and has a business
* method that increments the value.
*
* This example shows the basics of how to write a stateful
* session bean and how passivation/activation works.
*/
public class CountBean implements SessionBean {
private SessionContext ctx;
// The current counter is our conversational state.
public int val;
//
// Business methods
//
/**
* Counts up
*/
public int count() {
System.out.println("count()");
return ++val;
}
//
// EJB-required methods
//
public void ejbCreate(int val) throws CreateException {
this.val = val;
System.out.println("ejbCreate()");
}
public void ejbRemove() {
System.out.println("ejbRemove()");
}
public void ejbActivate() {
System.out.println("ejbActivate()");
}
public void ejbPassivate() {
System.out.println("ejbPassivate()");
}
public void setSessionContext(SessionContext ctx) {
}
}
Source CountBean.java
Bean實現(xiàn)了javax.ejb.SessionBean。所以,它必須定義所有SessionBean定義的方法。
OjbCreate()初始化帶了val的參數(shù)。它將作為counter的初始狀態(tài)。在鈍化和激活bean的過程中,val變量被保護。
計數(shù)bean的home接口
package com.wiley.compBooks.roman.session.count;
import javax.ejb.*;
import java.rmi.RemoteException;
/**
* This is the home interface for CountBean. This interface
* is implemented by the EJB Server´s glue-code tools - the
* implemented object is called the Home Object and serves
* as a factory for EJB Objects.
*
* One create() method is in this Home Interface, which
* corresponds to the ejbCreate() method in the CountBean file.
*/
public interface CountHome extends EJBHome {
/*
* This method creates the EJB Object.
*
* @param val Value to initialize counter to
*
* @return The newly created EJB Object.
*/
Count create(int val) throws RemoteException, CreateException;
}
Source CountHome.java.
計數(shù)bean的配置描述符
計數(shù)bean的配置描述符
計數(shù)bean的環(huán)境屬性
生成計數(shù)bean的Ejb-jar文件
計數(shù)bean的客戶端代碼
package com.wiley.compBooks.roman.session.count;
import javax.ejb.*;
import javax.naming.*;
import java.util.Properties;
/**
* This class is a simple example of client code that invokes
* methods on a simple Stateless Enterprise Bean.
*
* We create 3 EJB Objects in this example, but we allow
* the container to have only 2 in memory. This illustrates how
* beans are passivated to storage.
*/
public class CountClient {
public static void main(String[] args) {
try {
/*
* Get System properties for JNDI initialization
*/
Properties props = System.getProperties();
/*
* Get a reference to the Home Object - the
* factory for EJB Objects
*/
Source CountClient.java
1、需要JNDL初始化上下文
2、使用JNDL定位home接口
3、使用home對象建立3個不同的計數(shù)EJB對象,因此也就和三個不同的客戶端建立了會話
4、配置描述符限制了同時只能有兩個bean工作,因此3個bean中一定有鈍化的。在調(diào)用ejbPassivate()時,打印一條信息。
5、在每個EJB對象上調(diào)用count()方法,調(diào)用ejbActivate()方法激活bean,該方法打印一條信息。
6、最后所有的EJB對象被刪除。
package com.wiley.compBooks.roman.session.count;
import javax.ejb.*;
import javax.naming.*;
import java.util.Properties;
/**
* This class is a simple example of client code that invokes
* methods on a simple Stateless Enterprise Bean.
*
* We create 3 EJB Objects in this example, but we allow
* the container to have only 2 in memory. This illustrates how
* beans are passivated to storage.
*/
public class CountClient {
public static void main(String[] args) {
try {
/*
* Get System properties for JNDI initialization
*/
Properties props = System.getProperties();
/*
* Get a reference to the Home Object - the
* factory for EJB Objects
*/
Context ctx = new InitialContext(props);
CountHome home = (CountHome) ctx.lookup("CountHome");
/*
* An array to hold 3 Count EJB Objects
*/
Count count[] = new Count[3];
int countVal = 0;
/*
* Create and count() on each member of array
*/
System.out.println("Instantiating beans...");
for (int i=0; i < 3; i++) {
/*
* Create an EJB Object and initialize
* it to the current count value.
*/
count[i] = home.create(countVal);
/*
* Add 1 and print
*/
countVal = count[i].count();
System.out.println(countVal);
/*
* Sleep for 1/2 second
*/
Thread.sleep(500);
}
/*
* Let´s call count() on each EJB Object to
* make sure the beans were passivated and
* activated properly.
*/
System.out.println("Calling count() on beans...");
for (int i=0; i < 3; i++) {
/*
* Add 1 and print
*/
countVal = count[i].count();
System.out.println(countVal);
/*
* Sleep for 1/2 second
*/
Thread.sleep(500);
}
/*
* Done with EJB Objects, so remove them
*/
for (int i=0; i < 3; i++) {
count[i].remove();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Source CountClient.java
運行客戶端:
對于BEA的WebLogic,執(zhí)行:
java -Djava.naming.factory.initial=
weblogic.jndi.TengahInitialContextFactory
-Djava.naming.provider.url=
t3://localhost:7001
com.wiley.compBooks.roman.session.count.CountClient
客戶端輸出:
Instantiating beans...
1
2
3
Calling count() on beans...
2
3
4
服務(wù)端輸出:
ejbCreate()
count()
ejbCreate()
count()
ejbCreate()
ejbPassivate()
count()
ejbPassivate()
ejbActivate()
count()
ejbPassivate()
ejbActivate()
count()
ejbPassivate()
ejbActivate()
count()
ejbPassivate()
ejbActivate()
ejbRemove()
ejbActivate()
ejbRemove()
ejbRemove()
為Beans增加功能
EJB 上下文:通往容器的門戶
存在如下信息:
1、關(guān)于bean的home對象和EJB對象的信息
2、bean的當(dāng)前事務(wù)信息。
3、 對于客戶授權(quán)的安全信息。Bean可以通過查詢環(huán)境決定客戶執(zhí)行操作所需要的安全層次。
4、 bean的環(huán)境屬性。
容器將所有這些信息保存在一個稱為EJB context
object的對象里。EJB上下文作為容器的物理部分,可以被bean訪問。這些訪問可以讓bean得到當(dāng)前的狀態(tài)和改變當(dāng)前的狀態(tài)。
上下文可以在bean的生命期中被更改。
EJB1.0 javax.ejb.EJBContext接口:
public interface javax.ejb.EJBContext
{
public javax.ejb.EJBHome getEJBHome();
public java.util.Properties getEnvironment();
public java.security.Identity getCallerIdentity();
public boolean isCallerInRole(java.security.Identity);
public javax.jts.UserTransaction getUserTransaction();
public void setRollbackOnly();
public boolean getRollbackOnly();
}
會話bean的上下文
上下文根據(jù)bean的不同分為:會話上下文和實體上下文。它們分別被用于會話bean和實體bean
javax.ejb.EJBContext
public interface javax.ejb.SessionContext
extends javax.ejb.EJBContext
{
public javax.ejb.EJBObject getEJBObject();
}
注意:SessionContext接口繼承了EJBContext接口,在EJBContext中定義的方法提供了對會話bean的存取路徑。
對于會話bean,調(diào)用setSessionContext,這個方法在javax.ejb.SessionBean接口中被定義。對于實體bean,調(diào)用setEntityContext。
SessionContext.getEJBObject()
在EJB中,beans可以作為其他bean的客戶端。如果一個bean需要調(diào)用另外的bean,getEJBObject()方法則是必需的。在java中,對象可以使用this關(guān)鍵字保存自身的參考。在EJB中,bean不能使用this關(guān)鍵字給其他bean傳遞對象,這是因為所有的客戶調(diào)用bean上的方法都是間接調(diào)用bean的EJB對象。Bean可以使用this關(guān)鍵字將自己傳給EJB對象。
了解EJB的安全性
首先,客戶端必須是可被鑒別的。
其次,客戶端必須是已經(jīng)授權(quán)的。
第一步:鑒別
不同的EJB容器擁有不同的鑒別客戶端的方法。例如:BEA的WebLogic中,當(dāng)不同客戶端代碼使用JNDL定位Home對象時,提供不同的用戶名和密碼。
Properties props = System.getProperties();
props.put(Context.SECURITY_PRINCIPAL, "EmployeeA");
props.put(Context.SECURITY_CREDENTIALS, "myPassword1");
Context ctx = new InitialContext(props);
// Use the initial context to lookup home objects...
EJB沒有制定如何鑒別的規(guī)范,因此這樣就影響了可移植性。要了解這方面,查看各類容器的文檔。
當(dāng)運行這段代碼時,應(yīng)用服務(wù)器將驗證你的用戶名和密碼,這是應(yīng)用服務(wù)器規(guī)范。許多應(yīng)用服務(wù)器允許在屬性文件里設(shè)置用戶名和密碼。這個文件將在運行時由應(yīng)用服務(wù)器讀。
高級點的服務(wù)器支持已經(jīng)存在的驗證系統(tǒng)的整合。例如將用戶名和密碼列表存儲在LDAP服務(wù)器中。
第二步:授權(quán)
只有經(jīng)過授權(quán)的客戶端才可以調(diào)用bean中的方法。EJB中有兩種驗證授權(quán)的方法:declaratively和programmatically。即:由容器執(zhí)行所有的授權(quán)檢驗、在程序中進行授權(quán)檢查。
Declarative授權(quán)檢查時,要在配置描述符中聲明bean的授權(quán)需要。例如使用BEA的WebLogic服務(wù)器的配置描述符的例子:
(accessControlEntries
submitPurchaseOrder [employees]
approvePurchaseOrder [managers]
DEFAULT [administrators]
); end accessControlEntries
容器將在運行時自動的執(zhí)行安全檢查。拋會出java.lang.SecurityException異常。
Programmatic授權(quán)檢查,必須查詢EJB上下文得到當(dāng)前客戶端的授權(quán)信息。由兩種方法調(diào)用CallerInRole(Identity
role)和getCallerIdentity()。
isCallerInRole()
import java.security.Identity;
...
public class MyBean implements SessionBean {
private SessionContext ctx;
...
public void foo() {
Identity id = new MyIdentity("administrators");
if (ctx.isCallerInRole(id)) {
System.out.println("An admin called me");
return;
}
System.out.println("A non-admin called me");
}
}
import java.security.Identity;
public class MyIdentity extends Identity {
public MyIdentity(String id) {
super(id);
}
}
getCallerIdentity()
import java.security.Identity;
...
public class MyBean implements SessionBean {
private SessionContext ctx;
...
public void bar() {
Identity id = ctx.getCallerIdentity();
String name = id.getName();
System.out.println("The caller´s name is " + name);
}
}
了解EJB對象的操作
許多EJB應(yīng)用程序需要客戶端有與bean斷開的能力,還要有與bean重建連接的能力。EJB提供了EJB object
handles。EJB對象操作對于EJB對象是一個長生命期的代理。可以用它來重建與EJB對象的連接,并保證會話狀態(tài)不被丟失。下面是EJB對象操作的代碼
// First, get the EJB object handle from the EJB object.
javax.ejb.Handle myHandle = myEJBObject.getHandle();
// Next, serialize myHandle, and then save it in
// permanent storage.
ObjectOutputStream stream = ...;
stream.writeObject(myHandle);
// time passes...
// When we want to use the EJB object again,
// deserialize the EJB object handle
ObjectInputStream stream = ...;
Handle myHandle = (Handle) stream.readObject();
// Convert the EJB object handle back into an EJB object
MyRemoteInterface myEJBObject =
(MyRemoteInterface) myHandle.getEJBObject();
// Resume calling methods again
myEJBObject.callMethod();
例子:The Puzzle Game “Fazuul”(參考原文)