【永恒的瞬間】
          ?Give me hapy ?
           

          會話BeanSession Bean

          什么是Session Bean

          Session Bean可被視為客戶端程序在服務(wù)器上的部分邏輯延伸,每個Session Bean對象對應(yīng)于特定的客戶端,不能在多個客戶端間共享。換句話說,Session Bean用于表示運(yùn)行于服務(wù)器端的部分業(yè)務(wù)過程,作為客戶端的代理,管理業(yè)務(wù)過程或任務(wù),如客戶對賬戶的借貸操作、匯率的計算,等等這些涉及邏輯、算法和工作流的種種任務(wù)。這些過程都是特定的客戶行為,EJB根據(jù)這些過程在運(yùn)行時創(chuàng)建過程實例、執(zhí)行計算或者清除實例。

           Session Bean的生存時間

          相對于表示業(yè)務(wù)實體的Entity Bean,Session Bean的生存時間要短,大致等于于一個客戶端會話的延續(xù)時間。例如,客戶通過瀏覽器,訪問與Session Bean表示的相關(guān)業(yè)務(wù)過程,或通過一個Java應(yīng)用程序或者是Applet訪問的相關(guān)業(yè)務(wù)過程的持續(xù)時間,或其它       Enterprise Bean訪問此業(yè)務(wù)過程的持續(xù)時間。

          Session Bean是非持久的,其狀態(tài)不被保存到持久存儲機(jī)制(如數(shù)據(jù)庫、文件系統(tǒng))中,盡管Session Bean本身可以執(zhí)行對數(shù)據(jù)庫的操作,但它并不是一種持久對象的表示。

          Enterprise Bean類必須實現(xiàn)的接口中,定義了Enterprise Bean類的一些容器用于管理實例的回調(diào)方法。這些回調(diào)方法被容器用于與組件進(jìn)行交互,當(dāng)容器執(zhí)行與此Bean相關(guān)的一些重要操作時,通過調(diào)用這些方法通知組件。例如,用于容器在初始化或清除組件實例時,將調(diào)用這些方法中對應(yīng)的管理回調(diào)方法。由于這些方法是由容器使用的,所以,組件中不應(yīng)去調(diào)用這些方法 (有狀態(tài)會話bean的remove方法除外)。

          會話狀態(tài)

          Session Bean表示客戶端與應(yīng)用之間的會話。會話由客戶端與組件之間的交互組成,一般表現(xiàn)為一系列的方法調(diào)用。

          根據(jù)Session Bean有保持會話狀態(tài)的方式,可分為有狀態(tài)的和無狀態(tài)的Session Bean。

           無狀態(tài)的Session Bean

          無狀態(tài)的Session Bean,會話狀態(tài)只會在單個方法調(diào)用中被保持,一旦方法調(diào)用結(jié)束,組件將丟棄方法調(diào)用過程中保持的狀態(tài),不保持跨越方法調(diào)用的會話狀態(tài)。

          由于無狀態(tài)Session Bean不提供跨越方法調(diào)用的狀態(tài)保持,因此,對于任何客戶端調(diào)用,相同類別的Session Bean實例之間沒有區(qū)別。例如,某個特定客戶端對某個特定的無狀態(tài)Session Bean實例的方法進(jìn)行了調(diào)用后,此方法調(diào)用中的狀態(tài)在調(diào)用完成后即被清除,對于后續(xù)的其他客戶端方法調(diào)用而言,前一個是沒有影響的。因此,無狀態(tài)Session Bean通常被用于一些不需要保存跨越方法調(diào)用的會話狀態(tài)的計算,如匯率的換算等較為簡單的操作。

          由于具有這種會話狀態(tài)無關(guān)的特性,無狀態(tài)Session Bean通常可以在服務(wù)器啟動時在實例池中創(chuàng)建并保持一些實例,為不同的客戶端調(diào)用從實例池中分配已有的實例,免去頻繁的創(chuàng)建、初始化和清除實例的動作。

           有狀態(tài)的Session Bean

          對于有狀態(tài)的Session Bean,在整個會話期間,特定組件實例將保持與某個特定客戶端之間跨越方法調(diào)用的會話狀態(tài)。不同于無狀態(tài)的Session Bean,有狀態(tài)Session Bean的實例只能對應(yīng)于一個特定的客戶端會話,不可在不同的客戶端會話間共享,當(dāng)新的會話開始,有狀態(tài)的Session Bean實例即被創(chuàng)建和初始化,當(dāng)會話結(jié)束,實例即被清除。

          因此,一般情況下,有狀態(tài)的Session Bean所能提供的功能性,要強(qiáng)于無狀態(tài)的Session Bean。

          容器與會話Bean

          容器是為EJB組件提供運(yùn)行時環(huán)境的系統(tǒng)。同一容器中可以部署多個EJB組件。容器提供客戶端通過JNDI對已部署組件進(jìn)行依賴注入(EJB3.0)或訪問Home接口(EJB2.1)的能力,即客戶端可請求容器注入會話Bean遠(yuǎn)程接口或通過JNDI查找指定EJB組件的Home接口。

          客戶端不能直接訪問EJB組件的實例,只能通過JNDI查找指定組件的Remote接口,通過Remote接口取得對組件接口的引用。客戶端對組件接口的方法調(diào)用,通過組件接口傳播到容器中對應(yīng)的EJB組件實例。

          下圖表示EJB組件及其接口與容器、客戶端間的關(guān)系:

          EJB組件及其接口與容器、客戶端間的關(guān)系

           會話Bean的會話狀態(tài)

          容器為會話Bean提供運(yùn)行時環(huán)境,并對會話Bean實例進(jìn)行管理,因此,會話Bean實例的狀態(tài)也由容器進(jìn)行維護(hù)。根據(jù)狀態(tài)管理模式,可以將會話Bean劃分為有狀態(tài)(Stateful)會話Bean和無狀態(tài)(Stateless)會話Bean,有狀態(tài)會話Bean的狀態(tài)由容器負(fù)責(zé)維護(hù),而無狀態(tài)會話Bean則不需要容器進(jìn)行狀態(tài)管理。

          有狀態(tài)Session Bean實例的鈍化與激活

          對于當(dāng)前容器中存在的有狀態(tài)會話Bean實例,為提供更有效的管理,容器需要將某些空閑的實例狀態(tài)從內(nèi)存臨時轉(zhuǎn)移到存儲機(jī)制中。這種轉(zhuǎn)移稱為實例的鈍化(Passivation),相反地,將實例狀態(tài)從存儲機(jī)制恢復(fù),則稱為激活。(Activation)。

          當(dāng)實例被包含于某個事務(wù)中時,容器不能對實例進(jìn)行鈍化操作。

           會話狀態(tài)

          有狀態(tài)會話Bean的會話狀態(tài)指會話Bean實例中的域值,連同實例中的域通過Java對象引用指向的對象的傳遞閉包(transitive closure)。

          注意

          對象的傳遞閉包按照Java編程語言中的串行化協(xié)議定義,即是通過串行化對象實例,對實例的域進(jìn)行保存。參考Sun對象串行化文檔

          某些情況下,會話對象的會話狀態(tài)可能會包含打開的資源,如網(wǎng)絡(luò)端口和數(shù)據(jù)庫連接等。當(dāng)會話對象被鈍化時,容器不能鈍化這些資源,因此,需要開發(fā)者在容器對實例發(fā)出鈍化事件通知時(容器調(diào)用實例中注解為PrePassivate的方法(EJB3.0)ejbPassivate方法),對上述資源進(jìn)行關(guān)閉;在容器對實例發(fā)出激活事件通知時(容器調(diào)用實例中注解為PostActivate的方法或ejbActivate方法),重新打開上述資源。

          因此在編寫有狀態(tài)的Session Bean時,開發(fā)者必須注意,保證實例的非transient域是下列類型之一,使容器可以在鈍化實例時,可以完整保存對象的會話狀態(tài):

          ·             可串行化的對象;

          ·             null值;

          ·             EJB的業(yè)務(wù)接口引用;

          ·             EJB的遠(yuǎn)程接口引用,即使stub class為非序列化亦可

          ·             EJB的遠(yuǎn)程Home接口引用,即使stub class為非序列化亦可;

          ·             Entity Bean的本地接口引用,即使其為非序列化亦可;

          ·             EJB的本地Home接口引用,即使其為非序列化亦可;

          ·             SessionContext對象的引用,即使其為非序列化亦可;

          ·             環(huán)境命名上下文(指“java:comp/env”JNDI上下文)及其任何子上下文;

          ·             UserTransaction的引用;

          ·             Resource manager連接工廠的引用;

          ·             對容器管理的EntityManager對象的引用,即使其為非序列化亦可;

          ·             通過依賴注入或JNDI查找獲得的EntityManagerFactory引用,即使其為非序列化亦可;

          ·             javax.ejb.Timer對象的引用

          ·             不可直接串行化的對象,但是通過在串行化對象期間,將對象的引用更改為對象的業(yè)務(wù)接口,Home接口和組件接口的引用、對象對SessionContext對象的引用、對象對“java:comp/env”JNDI上下文及其子上下文的引用、對象對UserTransaction接口的引用、對象對EntityManagerEntityManagerFactory的引用進(jìn)行串行化,可將對象視為可串行化的對象。

          事務(wù)操作與狀態(tài)域

          會話Bean的會話狀態(tài)是非事務(wù)性的。如對象在參與的事務(wù)中改變了狀態(tài),之后事務(wù)回滾,對象是不能自動回到參加事務(wù)前的狀態(tài)的。因此,在不能保證會話Bean的會話狀態(tài)與事務(wù)狀態(tài)不一致的情況下,可以通過實現(xiàn)SessionSynchronization接口,在afterCompletion方法發(fā)出的通知中,根據(jù)事務(wù)提交的情況,決定是否重新設(shè)置對象的會話狀態(tài)。

          組件模型單元

          對于會話Bean,其組件模型包含以下幾部分:

          ·             Home接口,定義客戶端創(chuàng)建、清除EJB實例的方法;(注:在EJB3.0組件模型中并不需要此接口)

          ·             組件接口(component interface),定義客戶端可訪問的組件的業(yè)務(wù)方法,在EJB3.0組件模型中稱為業(yè)務(wù)接口(business interface)

          ·             組件類,提供對定義在組件接口中的方法的實現(xiàn);

          ·             部署描述,包含此EJB的部署信息,如事務(wù)屬性等,在EJB3.0中可使用注解來描述部署信息。

          下面分別對開發(fā)這些單元時,涉及的普遍過程、規(guī)則及注意事項進(jìn)行描述。

          Home接口

          EJB2.1規(guī)范通過Home接口定義客戶端創(chuàng)建、清除EJB對象的方法。Home接口有兩種類型,本地Home接口和遠(yuǎn)程Home接口,分別提供給本地和遠(yuǎn)程客戶端使用。在EJB3.0中,可通過依賴注入,JNDI查找,可選的生命周期回調(diào)方法注解來實現(xiàn)類似的功能。

           遠(yuǎn)程Home接口

          遠(yuǎn)程Home接口使遠(yuǎn)程客戶端可以:

          ·             創(chuàng)建新的Session對象;

          ·             清除一個Session對象;

          ·             取得此Session Beanjavax.ejb.EJBMetaData接口。javax.ejb.EJBMetaData接口用于表示此Session Bean的信息,降低客戶端與服務(wù)器的綁定程度和用于支持客戶端腳本;

          ·             取得遠(yuǎn)程Home接口的句柄(Handler),此句柄可串行化(serialization)到持久存儲中,然后,可在其他虛擬機(jī)中,從持久存儲中,對此串行化對象進(jìn)行解串行化(deserialization),取得對此遠(yuǎn)程Home接口句柄的引用。

           編寫遠(yuǎn)程Home接口的規(guī)則

          編寫EJB的遠(yuǎn)程Home接口時,開發(fā)人員必須遵循如下規(guī)則:

          ·             遠(yuǎn)程Home接口必須擴(kuò)展(extendjavax.ejb.EJBHome接口;

          ·             定義在接口中的方法的參數(shù)和返回值必須是合法的RMI類型,并且必須顯式聲明拋出java.rmi.RemoteException異常;

          ·             在繼承關(guān)系上,遠(yuǎn)程接口可以擴(kuò)展已有的接口,但繼承的方法必須依從上一規(guī)則;

          ·             遠(yuǎn)程接口方法必須定義一個或多個create方法。無狀態(tài)的Session Bean必須提供一個無參數(shù)的create方法,并且只能命名為“create()”的形式;每個create方法必須匹配一個定義在組件類中的ejbCreate方法,此組件類中的ejbCreate方法必須具有相同個數(shù)和類型的參數(shù)。無狀態(tài)Session Beancreate方法在組件類中的匹配方法只能命名為“ejbCreate()”的形式。;

          ·             每個create方法名稱開頭必須是“create”

          ·             create方法的返回值類型必須是Session Bean的遠(yuǎn)程接口;

          ·             create方法必須聲明拋出javax.ejb.CreateException異常;

          代碼范例

          Home接口通過定義一個或多個create方法,提供一種或多種創(chuàng)建session對象的方式。create方法的參數(shù)一般用于初始化被創(chuàng)建session對象的狀態(tài)。

          create方法的返回值類型為Bean的遠(yuǎn)程接口類型。

          下面是一個范例Session BeanHome接口的代碼,定義了兩個create方法:

          public interface CartHome extends javax.ejb.EJBHome 
          {
              Cart create(String customerName, String account)
                  throws RemoteException, BadAccountException,
                  CreateException;
           
              Cart createLargeCart(String customerName, String account)
                  throws RemoteException, BadAccountException,
                  CreateException;
          }

          客戶端可通過如下代碼,對已部署在應(yīng)用服務(wù)器上的Session BeanHome接口進(jìn)行查找,并使用CartHome接口中定義的create方法創(chuàng)建session對象:

          Context initialContext = new InitialContext();
          CartHome cartHome = (CartHome)
              javax.rmi.PortableRemoteObject.narrow(
              initialContext.lookup(“java:comp/env/ejb/cart”),
              CartHome.class);
           
          Cart shoppingCart = home.create("Duke DeEarl","123");

          本地Home接口

          本地Home接口使本地客戶端可以:

          ·             創(chuàng)建新的Session對象;

          ·             清除一個Session對象;

           編寫本地Home接口的規(guī)則

          如果EJB需要提供本地客戶端的訪問,必須提供EJB的本地Home接口,開發(fā)人員必須遵循如下規(guī)則進(jìn)行編寫:

          ·             本地Home接口必須擴(kuò)展(extendjavax.ejb.EJBLocalHome接口;

          ·             定義在接口中的方法不能拋出java.rmi.RemoteException異常;

          ·             在繼承關(guān)系上,本地接口可以擴(kuò)展已有的接口;

          ·             本地接口方法必須定義一個或多個create方法。無狀態(tài)的Session Bean必須提供一個無參數(shù)的create方法,并且只能命名為“create()”的形式;每個create方法必須匹配一個定義在組件類中的ejbCreate方法,此組件類中的ejbCreate方法必須具有相同個數(shù)和類型的參數(shù)。無狀態(tài)Session Beancreate方法在組件類中的匹配方法只能命名為“ejbCreate()”的形式。;

          ·             每個create方法名稱開頭必須是“create”

          ·             create方法的返回值類型必須是Session Bean的本地接口;

          ·             create方法必須聲明拋出javax.ejb.CreateException異常;

           代碼范例

          客戶端可通過類似如下代碼,對已部署在應(yīng)用服務(wù)器上的Session Bean的本地Home接口進(jìn)行查找:

          Context initialContext = new InitialContext();
          CartHome cartHome = (CartHome)
              initialContext.lookup(“java:comp/env/ejb/cart”);

           業(yè)務(wù)(組件)接口

          會話Bean和實體Bean的客戶端不能直接訪問EJB組件類的實例,客戶端程序通過業(yè)務(wù)接口訪問EJB組件,開發(fā)者在業(yè)務(wù)接口中定義可供客戶端訪問的業(yè)務(wù)方法。業(yè)務(wù)接口分為兩種類型,本地接口和遠(yuǎn)程接口。

           遠(yuǎn)程接口

          如果EJB需要被遠(yuǎn)程客戶端訪問,必須提供EJB的遠(yuǎn)程接口,遠(yuǎn)程接口為組件提供以下支持:

          ·             定義EJB對象的業(yè)務(wù)邏輯方法,遠(yuǎn)程接口把對業(yè)務(wù)方法的調(diào)用傳播到Session Bean實例;

          ·             提供允許客戶端取得遠(yuǎn)程Home接口實例引用的方法;

          ·             提供使客戶端取得Session對象句柄的方法;

          ·             提供比較兩個EJB實例是否相等的方法;

          ·             移除Session Bean實例的方法;

           必須遵守的的規(guī)則

          開發(fā)人員必須遵循如下規(guī)則編寫遠(yuǎn)程接口:

          ·             遠(yuǎn)程接口必須使用@Remote注解標(biāo)記(EJB3.0),或擴(kuò)展(extendjavax.ejb.EJBObject接口(EJB2.1);

          ·             若使用EJB2.1規(guī)范,定義在接口中的方法的參數(shù)和返回值必須是合法的RMI類型,并且必須顯式聲明拋出java.rmi.RemoteException異常;

          ·             在繼承關(guān)系上,遠(yuǎn)程接口可以擴(kuò)展已有的接口,但繼承的方法必須依從上一規(guī)則;

          ·             每個在遠(yuǎn)程接口中定義的方法,必須在組件類中有一個匹配的方法,組件類中的匹配方法必須與接口中定義的方法具有相同的名字、相同的參數(shù)個數(shù)、參數(shù)類型和相同的返回值,而且所有在遠(yuǎn)程接口中定義的拋出的異常也必須在匹配方法中定義;

          ·             遠(yuǎn)程接口方法不可暴露本地接口類型、本地Home接口類型,而且,對于容器管理持久性Entity Bean,也不可暴露作為Entity Bean中方法參數(shù)和返回值的受管理的集合類(collection)。

          代碼范例

          下面是一個EJB2.1的會話Bean的遠(yuǎn)程接口的代碼,其中定義了三個業(yè)務(wù)方法:

          public interface Cart extends EJBObject 
          {
              public void addBook(String title) 
                  throws RemoteException;
           
              public void removeBook(String title) 
                  throws BookException, RemoteException;
           
              public Vector getContents() 
                  throws RemoteException;
          }

          EJB3.0中,可使用以下形式達(dá)到同樣效果:

          @Remote
          public interface Cart extends EJBObject 
          {
              public void addBook(String title) 
                  throws RemoteException;
           
              public void removeBook(String title) 
                  throws BookException, RemoteException;
           
              public Vector getContents() 
                  throws RemoteException;
          }
           

          本地接口

          EJB2.1規(guī)范中,如果EJB需要被本地客戶端訪問,必須提供EJB的本地接口,本地接口為組件提供以下支持:

          ·             定義EJB對象的業(yè)務(wù)邏輯方法,本地接口把對業(yè)務(wù)方法的調(diào)用傳播到Session Bean實例;

          ·             提供允許客戶端取得本地Home接口實例引用的方法;

          ·             提供比較兩個EJB實例是否相等的方法;

          ·             移除Session Bean實例的方法;

          EJB3.0規(guī)范中,容器為遠(yuǎn)程接口提供了位置透明性,本地客戶端亦可通過遠(yuǎn)程接口訪問業(yè)務(wù)方法。但使用本地接口可以為EJB提供細(xì)粒度的業(yè)務(wù)方法,獲得更好的可重用、與可維護(hù)性與運(yùn)行效率。EJB3.0中的本地接口可以使用@Local注解來標(biāo)記

          必須遵守的規(guī)則

          開發(fā)人員必須遵循如下規(guī)則編寫本地接口:

          ·             本地接口必須使用@Local注解進(jìn)行標(biāo)記,或擴(kuò)展(extendjavax.ejb.EJBLocalObject接口;

          ·             本地接口中定義的方法不能聲明拋出java.rmi.RemoteException異常;

          ·             在繼承關(guān)系上,本地接口可以擴(kuò)展已有的接口;

          ·             每個在本地接口中定義的方法,必須在組件類中有一個匹配的方法,組件類中的匹配方法必須與接口中定義的方法具有相同的名字、相同的參數(shù)個數(shù)、參數(shù)類型和相同的返回值,而且所有在遠(yuǎn)程接口中定義的拋出的異常也必須在匹配方法中定義;

          組件類

          對于一個Session Bean組件,開發(fā)者在業(yè)務(wù)(組件)接口中定義的的業(yè)務(wù)方法和在Home接口中定義的create方法,需要在組件類中對這些方法提供實現(xiàn)。EJB2.1中的組件類必須實現(xiàn)javax.ejb.SessionBean接口,容器通過調(diào)用從javax.ejb.SessionBean中繼承的管理方法,提供組件訪問容器提供的服務(wù)的能力,并通過一些狀態(tài)管理回調(diào)方法,向組件實例發(fā)送其生存周期中的關(guān)鍵事件的信息。在EJB3.0中的組件類則不必實現(xiàn)javax.ejb.SessionBean接口,而是通過依賴注入、JNDI查找、生存周期回調(diào)方法注解達(dá)到同樣的功能。

          在組件類中使用注解

          EJB3.0的組件類不再強(qiáng)制實現(xiàn)javax.ejb.SessionBean接口,而是通過注解實現(xiàn)同樣的功能,從而避免了嚴(yán)格的隱式命名約定。在組件中常用的注解有以下幾個:

           @Resource注解

          在組件類中可以使用@Resource注解通知容器對上下文資源進(jìn)行依賴注入,特別地,可以注入SessionContext實例:

          @Stateless public class EmployeeServiceBean
          implements EmployeeService{
              @Resource SessionContext ctx;
          ...

          @PostCreate注解

          在組件類中可以使用@PostCreate注解標(biāo)記初始化方法按照EJB3.0規(guī)范,容器將會在完成對會話Bean依賴注入之后,會話Bean的第一次方法被調(diào)用之前回調(diào)該方法。可以在該方法中對依賴注入后的會話Bean狀態(tài)進(jìn)行初試化。

           @Remove@PreDestroy注解

          在會話Bean的業(yè)務(wù)接口中使用@Remove注解標(biāo)記客戶端請求清除會話Bean的方法。該方法由客戶端調(diào)用,若成功完成,容器將清除會話Bean。此注解適用于有狀態(tài)會話Bean,無狀態(tài)會話Bean的生存周期完全由容器管理,毋須在客戶端顯式刪除。

          在會話Bean組件類中可使用@PreDestroy注解標(biāo)記容器清除會話Bean前的回調(diào)方法。容器將在會話Bean被清除前調(diào)用該方法。一般,實例可以在此方法中對實例占用的資源進(jìn)行釋放。

           @PrePassivate@PostActivate注解

          在實例將被容器鈍化(Passivate)時,容器會調(diào)用被標(biāo)記為PrePassivate的方法。同樣,當(dāng)實例將被再次激活(Activate)時,容器會調(diào)用被標(biāo)記為PostActivate的方法。在實例鈍化時,容器將會自動保存實例的狀態(tài)信息;在激活實例時,容器也將恢復(fù)被保存的實例狀態(tài)信息。一般的Session Bean都會忽略此事件,但是,對于使用某些不可串行化的資源,如與特定數(shù)據(jù)庫的連接等,作為實例狀態(tài)一部分的Session Bean,通常需要在ejbPassivate方法中釋放資源,在ejbActivate方法中重新獲取資源。

           javax.ejb.SessionBean接口

          EJB2.1規(guī)范中,javax.ejb.SessionBean是容器與組件實例之間的協(xié)議。通過實現(xiàn)此接口,組件提供容器訪問組件的能力,實際上,容器不需要組件提供任何服務(wù),容器通過javax.ejb.SessionBean中定義的接口方法訪問組件實例,主要是為了給組件提供訪問容器提供的服務(wù)的能力,并向組件實例發(fā)送通知其生存周期中重要事件的發(fā)生信息。

          javax.ejb.SessionBean中定義了以下方法。

           setSessionContext

          容器通過調(diào)用setSessionContext方法,將由容器維護(hù)的Bean實例的上下文(context)與Bean實例進(jìn)行關(guān)聯(lián)。一般,Session Bean實例的上下文被作為實例會話狀態(tài)的一部分,被實例所保存。

          ejbRemove

          在實例將被容器清除時,容器會調(diào)用此方法。一般,實例可以在此方法中對實例占用的資源進(jìn)行釋放。

           ejbPassivateejbActivate

          在實例將被容器鈍化(Passivate)時,容器會調(diào)用ejbPassivate方法。同樣,當(dāng)實例將被再次激活(Activate)時,容器會調(diào)用ejbActivate方法。對于使用某些不可串行化的資源,如與特定數(shù)據(jù)庫的連接等,作為實例狀態(tài)一部分的會話Bean,通常需要在ejbPassivate方法中釋放資源,在ejbActivate方法中重新獲取資源。

          SessionContext接口

          對于會話Bean實例,容器將提供一個SessionContext對象,使實例可以訪問由容器維護(hù)的實例的上下文環(huán)境。在SessionContext接口中,定義了如下方法:¹

          ·             getEJBObject方法,返回Session Bean的遠(yuǎn)程接口;

          ·             getEJBHome方法,返回Session Bean的遠(yuǎn)程Home接口;

          ·             getEJBLocalObject,返回Session Bean的本地接口;

          ·             getEJBLocalHome,返回Sesson Bean的本地Home接口;

          ·             getCallerPrincipal方法,返回標(biāo)識調(diào)用此實例的調(diào)用者的java.security.Principal對象;

          ·             isCallerInRole方法,檢查此Session Bean的調(diào)用者是否是某個特定的角色;

          ·             setRollbackOnly方法,當(dāng)前事務(wù)將被永久標(biāo)記為回滾,不會被提交。只有容器管理事務(wù)的Session Bean可被允許使用此方法;

          ·             getRollbackOnly方法,檢查當(dāng)前事務(wù)是否已被標(biāo)記為回滾。例如,EJB實例可以通過此方法,判斷是否繼續(xù)在當(dāng)前事務(wù)邊界內(nèi)進(jìn)行計算。只有容器管理事務(wù)的Session Bean可被允許使用此方法;

          ·             getUserTransaction方法,返回javax.transaction.UserTransaction接口。EJB實例可通過此接口對事務(wù)邊界進(jìn)行劃分,并取得事務(wù)的狀態(tài)。只有容器管理事務(wù)的Session Bean可被允許使用此方法;

           可選的SessionSynchronization接口

          會話Bean的組件類可以選擇是否實現(xiàn)javax.ejb.SessionSynchronization接口。此接口對會話Bean提供事務(wù)的同步通知。

          ·             afterBegin通知,標(biāo)志會話Bean實例一個新事務(wù)的開始。當(dāng)包含在事務(wù)中第一個的業(yè)務(wù)方法被調(diào)用之前,容器將調(diào)用此方法,后續(xù)的業(yè)務(wù)方法調(diào)用將會存在于此事務(wù)上下文中;

          ·             afterCompletion通知,標(biāo)志一個會話Bean事務(wù)的提交操作結(jié)束,并通知實例提交操作是成功還是回滾;

          ·             beforeCompletion通知,通知會話Bean實例事務(wù)將進(jìn)行提交操作;

          EJB3.0規(guī)范規(guī)定,如果會話Bean直接或間接地實現(xiàn)了SessionSynchronization接口,容器必須回調(diào)afterBegin, beforeCompletionafterCompletion方法。若會話Bean沒有實現(xiàn)SessionSynchronization接口,則容器不會調(diào)用這些方法。

           串行化的會話Bean方法調(diào)用

          容器將對每個會話Bean實例的方法調(diào)用進(jìn)行串行化。Apusic應(yīng)用服務(wù)器中的EJB容器支持會話Bean的多個實例并發(fā)執(zhí)行;但是,每個會話Bean實例只會看到依次進(jìn)行的方法調(diào)用,因此開發(fā)Session Bean時,不需要將其以可重入(reentrant)的方式進(jìn)行編寫。

          客戶端不能對有狀態(tài)的會話Bean對象進(jìn)行并發(fā)調(diào)用。當(dāng)某個客戶端對某特定的會話Bean實例的業(yè)務(wù)方法調(diào)用正在執(zhí)行中,從相同或不同的客戶端發(fā)出了另一個客戶端調(diào)用,容器將對第二個客戶端調(diào)用拋出java.rmi.RemoteExceptionjavax.ejb.EJBException,這取決于第二個客戶端調(diào)用是通過遠(yuǎn)程或本地組件接口進(jìn)行調(diào)用。

           業(yè)務(wù)方法必須遵守的規(guī)則

          會話Bean的組件類中可以定義零到多個業(yè)務(wù)方法,其方法簽名和命名必須遵守如下規(guī)則:

          ·             方法名是任意的,但不能使用“ejb”開頭,以免與EJB組件架構(gòu)中的容器管理回調(diào)方法發(fā)生沖突,如ejbPassivate方法;

          ·             業(yè)務(wù)方法必須被聲明為public方法;

          ·             業(yè)務(wù)方法不能被聲明為finalstatic

          ·             如業(yè)務(wù)方法是對應(yīng)于會話Bean遠(yuǎn)程接口中定義的業(yè)務(wù)方法,則方法參數(shù)和返回值必須是合法的RMI/IIOP類型;

          ·             可以拋出任意應(yīng)用級異常;

          注意

          EJB1.0允許業(yè)務(wù)方法拋出java.rmi.RemoteException,以指出非應(yīng)用級的異常。在EJB1.1EJB2.1兼容的Enterprise Bean開發(fā)中,這種方式不建議使用(deprecated),EJB2.1兼容的Enterprise Bean開發(fā)中,不應(yīng)從業(yè)務(wù)方法拋出java.rmi.RemoteException

          代碼實例

          以下是一個會話Bean組件類的范例代碼,其對應(yīng)的組件接口和Home接口,請參考本章中關(guān)于組件接口和Home接口中的范例部分:

          import java.util.*;
          import javax.ejb.*;
           
          public class CartBean implements SessionBean {
           
             String customerName;
             String customerId;
             Vector contents;
           
             public void ejbCreate(String person) throws CreateException {
           
                if (person == null) {
                  throw new CreateException("Null person not allowed.");
                }
                else {
                   customerName = person;
                }
           
                customerId = "0";
                contents = new Vector();
             }
           
             public void ejbCreate(String person, String id) throws CreateException {
           
                if (person == null) {
                  throw new CreateException("Null person not allowed.");
                }
                else {
                   customerName = person;
                }
           
                IdVerifier idChecker = new IdVerifier();
                if (idChecker.validate(id)) {
                   customerId = id;
                }
                else {
                   throw new CreateException("Invalid id: " + id);
                }
           
                contents = new Vector();
             }
           
             public void addBook(String title) {
           
                contents.addElement(title);
             }
           
             public void removeBook(String title) throws BookException {
           
                boolean result = contents.removeElement(title);
                if (result == false) {
                   throw new BookException(title + " not in cart.");
                }
             }
           
             public Vector getContents() {
                return contents;
             }
           
             public CartBean() {}
             public void ejbRemove() {}
             public void ejbActivate() {}
             public void ejbPassivate() {}
             public void setSessionContext(SessionContext sc) {}
           
          }

           

           生存周期

           有狀態(tài)Session Bean的生存周期

          有狀態(tài)Session Bean的生存周期

          上圖表示有狀態(tài)Session Bean的生存周期。

          有狀態(tài)會話Bean實例的生命周期開始于用戶端通過依賴注入或JNDI查找獲得一個有狀態(tài)會話Bean實例的引用,或者用戶端調(diào)用了會話BeanHome接口中的create<METHOD>方法。這將通知容器調(diào)用會話Bean類的newInstance方法創(chuàng)建一個新的會話Bean實例。然后容器將注入SessionContext實例(若可用),并執(zhí)行其它由元數(shù)據(jù)注解標(biāo)記的或在部署描述符中設(shè)定的依賴注入。然后容器將調(diào)用會話BeanPostConstruct生存周期回調(diào)方法(若已定義)。如果會話Bean遵照EJB2.1規(guī)范,則容器調(diào)用實例中匹配的ejbCreate<METHOD>Init方法。最后,容器返回會話Bean的對象引用。現(xiàn)在實例進(jìn)入就緒狀態(tài),客戶端可以調(diào)用其業(yè)務(wù)方法。

          根據(jù)會話bean元數(shù)據(jù)注解中的事務(wù)屬性或部署描述文件中的設(shè)定,以及客戶端調(diào)用所關(guān)聯(lián)的事務(wù)上下文,業(yè)務(wù)方法將會在一個特定的事務(wù)上下文環(huán)境或一個未指定的業(yè)務(wù)上下文環(huán)境中執(zhí)行(在圖中分別以事務(wù)方法非事務(wù)方法表示)。

          容器的緩沖算法決定會話Bean實例是否需要移出內(nèi)存(例如采取最近最久未使用算法)。容器先回調(diào)Bean實例的PrePassivate生存周期回調(diào)方法(若已定義)。然后,容器把實例的狀態(tài)信息保存到二級存儲設(shè)備中,會話Bean進(jìn)入鈍化狀態(tài)。在事務(wù)之中的會話Bean不能被鈍化。

          當(dāng)會話Bean處于鈍化狀態(tài),容器可能會在會話Bean超時后清除該實例,超時信息在部署描述文件中設(shè)置。此時所有對于該實例的引用都將失效。如果客戶端試圖通過業(yè)務(wù)接口調(diào)用任一方法,容器將拋出javax.ejb.NoSuchEJBException異常。如果使用EJB2.1規(guī)范,則容器分別為遠(yuǎn)程客戶端拋出java.rmi.NoSuchObjectException異常,為本地客戶端拋出javax.ejb.NoSuchObjectLocalException異常。

          如果客戶端調(diào)用了被標(biāo)記為Remove的方法,或者home接口或組件接口中的remove方法,容器將在該方法成功完成后回調(diào)被標(biāo)記為PreDestroy的生存周期回調(diào)方法(若已定義)。這將終結(jié)會話Bean實例的生存周期。此時所有對于該實例的引用都將失效。如果客戶端試圖通過業(yè)務(wù)接口調(diào)用任一方法,容器將拋出javax.ejb.NoSuchEJBException異常。如果使用EJB2.1規(guī)范,則容器分別為遠(yuǎn)程客戶端拋出java.rmi.NoSuchObjectException異常,為本地客戶端拋出javax.ejb.NoSuchObjectLocalException異常。要注意即使客戶端沒有顯式調(diào)用Remove方法,容器也會在EJB對象實例超時后主動調(diào)用其PreDestroy方法并清除該實例。

          如果客戶端調(diào)用了鈍化狀態(tài)的會話Bean的方法,容器將激活該實例。此時容器將先從二級存儲設(shè)備中恢復(fù)實例的狀態(tài)信息,并調(diào)用實例的PostActivte方法(若已定義)。

          如果Remove方法成功完成,或Remove方法中拋出了應(yīng)用異常而retainIfException為假,或拋出了系統(tǒng)異常,SessionSynchronization方法將不會被調(diào)用。如果拋出了應(yīng)用異常而retainIfException為真,則該會話bean實例既不會被清除也不會被丟棄,SessionSynchronization方法(若已定義)將會在事務(wù)結(jié)束時被調(diào)用。

          客戶端代碼對有狀態(tài)Session Bean生存周期的控制只能創(chuàng)建(使用依賴注入、JNDI查找或create<METHOD>方法)和清除(使用Remove方法)。其他方法由EJB容器進(jìn)行調(diào)用。

           無狀態(tài)Session Bean的生存周期

          因無狀態(tài)Session Bean不會進(jìn)行被鈍化操作,因此,其生存周期只有兩個階段,不存在和就緒狀態(tài)。下圖表示無狀態(tài)Session Bean的生存周期:

          無狀態(tài)Session Bean的生存周期

          posted on 2007-10-22 11:45 ???MengChuChen 閱讀(2285) 評論(0)  編輯  收藏 所屬分類: EJB3.0

          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 海盐县| 伊金霍洛旗| 阆中市| 宜阳县| 沛县| 高邑县| 湟源县| 株洲市| 云和县| 安图县| 罗江县| 甘南县| 中方县| 合山市| 波密县| 桦甸市| 玛沁县| 无棣县| 钟山县| 营山县| 梁平县| 临夏县| 瓦房店市| 蒙城县| 宜春市| 博罗县| 宁阳县| 湖州市| 错那县| 安乡县| 行唐县| 金堂县| 繁昌县| 丰宁| 深泽县| 岳普湖县| 连州市| 天水市| 东乡族自治县| 许昌县| 和龙市|