會話Bean(Session Bean)
什么是Session Bean
Session Bean可被視為客戶端程序在服務器上的部分邏輯延伸,每個Session Bean對象對應于特定的客戶端,不能在多個客戶端間共享。換句話說,Session Bean用于表示運行于服務器端的部分業務過程,作為客戶端的代理,管理業務過程或任務,如客戶對賬戶的借貸操作、匯率的計算,等等這些涉及邏輯、算法和工作流的種種任務。這些過程都是特定的客戶行為,EJB根據這些過程在運行時創建過程實例、執行計算或者清除實例。
Session Bean的生存時間
相對于表示業務實體的Entity Bean,Session Bean的生存時間要短,大致等于于一個客戶端會話的延續時間。例如,客戶通過瀏覽器,訪問與Session Bean表示的相關業務過程,或通過一個Java應用程序或者是Applet訪問的相關業務過程的持續時間,或其它 Enterprise Bean訪問此業務過程的持續時間。
Session Bean是非持久的,其狀態不被保存到持久存儲機制(如數據庫、文件系統)中,盡管Session Bean本身可以執行對數據庫的操作,但它并不是一種持久對象的表示。
Enterprise Bean類必須實現的接口中,定義了Enterprise Bean類的一些容器用于管理實例的回調方法。這些回調方法被容器用于與組件進行交互,當容器執行與此Bean相關的一些重要操作時,通過調用這些方法通知組件。例如,用于容器在初始化或清除組件實例時,將調用這些方法中對應的管理回調方法。由于這些方法是由容器使用的,所以,組件中不應去調用這些方法 (有狀態會話bean的remove方法除外)。
會話狀態
Session Bean表示客戶端與應用之間的會話。會話由客戶端與組件之間的交互組成,一般表現為一系列的方法調用。
根據Session Bean有保持會話狀態的方式,可分為有狀態的和無狀態的Session Bean。
無狀態的Session Bean
無狀態的Session Bean,會話狀態只會在單個方法調用中被保持,一旦方法調用結束,組件將丟棄方法調用過程中保持的狀態,不保持跨越方法調用的會話狀態。
由于無狀態Session Bean不提供跨越方法調用的狀態保持,因此,對于任何客戶端調用,相同類別的Session Bean實例之間沒有區別。例如,某個特定客戶端對某個特定的無狀態Session Bean實例的方法進行了調用后,此方法調用中的狀態在調用完成后即被清除,對于后續的其他客戶端方法調用而言,前一個是沒有影響的。因此,無狀態Session Bean通常被用于一些不需要保存跨越方法調用的會話狀態的計算,如匯率的換算等較為簡單的操作。
由于具有這種會話狀態無關的特性,無狀態Session Bean通常可以在服務器啟動時在實例池中創建并保持一些實例,為不同的客戶端調用從實例池中分配已有的實例,免去頻繁的創建、初始化和清除實例的動作。
有狀態的Session Bean
對于有狀態的Session Bean,在整個會話期間,特定組件實例將保持與某個特定客戶端之間跨越方法調用的會話狀態。不同于無狀態的Session Bean,有狀態Session Bean的實例只能對應于一個特定的客戶端會話,不可在不同的客戶端會話間共享,當新的會話開始,有狀態的Session Bean實例即被創建和初始化,當會話結束,實例即被清除。
因此,一般情況下,有狀態的Session Bean所能提供的功能性,要強于無狀態的Session Bean。
容器與會話Bean
容器是為EJB組件提供運行時環境的系統。同一容器中可以部署多個EJB組件。容器提供客戶端通過JNDI對已部署組件進行依賴注入(EJB3.0)或訪問Home接口(EJB2.1)的能力,即客戶端可請求容器注入會話Bean遠程接口或通過JNDI查找指定EJB組件的Home接口。
客戶端不能直接訪問EJB組件的實例,只能通過JNDI查找指定組件的Remote接口,通過Remote接口取得對組件接口的引用。客戶端對組件接口的方法調用,通過組件接口傳播到容器中對應的EJB組件實例。
下圖表示EJB組件及其接口與容器、客戶端間的關系:
EJB組件及其接口與容器、客戶端間的關系
會話Bean的會話狀態
容器為會話Bean提供運行時環境,并對會話Bean實例進行管理,因此,會話Bean實例的狀態也由容器進行維護。根據狀態管理模式,可以將會話Bean劃分為有狀態(Stateful)會話Bean和無狀態(Stateless)會話Bean,有狀態會話Bean的狀態由容器負責維護,而無狀態會話Bean則不需要容器進行狀態管理。
有狀態Session Bean實例的鈍化與激活
對于當前容器中存在的有狀態會話Bean實例,為提供更有效的管理,容器需要將某些空閑的實例狀態從內存臨時轉移到存儲機制中。這種轉移稱為實例的鈍化(Passivation),相反地,將實例狀態從存儲機制恢復,則稱為激活。(Activation)。
當實例被包含于某個事務中時,容器不能對實例進行鈍化操作。
會話狀態
有狀態會話Bean的會話狀態指會話Bean實例中的域值,連同實例中的域通過Java對象引用指向的對象的傳遞閉包(transitive closure)。
|
注意
|
對象的傳遞閉包按照Java編程語言中的串行化協議定義,即是通過串行化對象實例,對實例的域進行保存。參考Sun的對象串行化文檔。
|
某些情況下,會話對象的會話狀態可能會包含打開的資源,如網絡端口和數據庫連接等。當會話對象被鈍化時,容器不能鈍化這些資源,因此,需要開發者在容器對實例發出鈍化事件通知時(容器調用實例中注解為PrePassivate的方法(EJB3.0)或ejbPassivate方法),對上述資源進行關閉;在容器對實例發出激活事件通知時(容器調用實例中注解為PostActivate的方法或ejbActivate方法),重新打開上述資源。
因此在編寫有狀態的Session Bean時,開發者必須注意,保證實例的非transient域是下列類型之一,使容器可以在鈍化實例時,可以完整保存對象的會話狀態:
· 可串行化的對象;
· null值;
· EJB的業務接口引用;
· EJB的遠程接口引用,即使stub class為非序列化亦可
· EJB的遠程Home接口引用,即使stub class為非序列化亦可;
· Entity Bean的本地接口引用,即使其為非序列化亦可;
· EJB的本地Home接口引用,即使其為非序列化亦可;
· 對SessionContext對象的引用,即使其為非序列化亦可;
· 環境命名上下文(指“java:comp/env”JNDI上下文)及其任何子上下文;
· UserTransaction的引用;
· Resource manager連接工廠的引用;
· 對容器管理的EntityManager對象的引用,即使其為非序列化亦可;
· 通過依賴注入或JNDI查找獲得的EntityManagerFactory引用,即使其為非序列化亦可;
· 對javax.ejb.Timer對象的引用
· 不可直接串行化的對象,但是通過在串行化對象期間,將對象的引用更改為對象的業務接口,Home接口和組件接口的引用、對象對SessionContext對象的引用、對象對“java:comp/env”JNDI上下文及其子上下文的引用、對象對UserTransaction接口的引用、對象對EntityManager與EntityManagerFactory的引用進行串行化,可將對象視為可串行化的對象。
事務操作與狀態域
會話Bean的會話狀態是非事務性的。如對象在參與的事務中改變了狀態,之后事務回滾,對象是不能自動回到參加事務前的狀態的。因此,在不能保證會話Bean的會話狀態與事務狀態不一致的情況下,可以通過實現SessionSynchronization接口,在afterCompletion方法發出的通知中,根據事務提交的情況,決定是否重新設置對象的會話狀態。
組件模型單元
對于會話Bean,其組件模型包含以下幾部分:
· Home接口,定義客戶端創建、清除EJB實例的方法;(注:在EJB3.0組件模型中并不需要此接口)
· 組件接口(component interface),定義客戶端可訪問的組件的業務方法,在EJB3.0組件模型中稱為業務接口(business interface);
· 組件類,提供對定義在組件接口中的方法的實現;
· 部署描述,包含此EJB的部署信息,如事務屬性等,在EJB3.0中可使用注解來描述部署信息。
下面分別對開發這些單元時,涉及的普遍過程、規則及注意事項進行描述。
Home接口
EJB2.1規范通過Home接口定義客戶端創建、清除EJB對象的方法。Home接口有兩種類型,本地Home接口和遠程Home接口,分別提供給本地和遠程客戶端使用。在EJB3.0中,可通過依賴注入,JNDI查找,可選的生命周期回調方法注解來實現類似的功能。
遠程Home接口
遠程Home接口使遠程客戶端可以:
· 創建新的Session對象;
· 清除一個Session對象;
· 取得此Session Bean的javax.ejb.EJBMetaData接口。javax.ejb.EJBMetaData接口用于表示此Session Bean的信息,降低客戶端與服務器的綁定程度和用于支持客戶端腳本;
· 取得遠程Home接口的句柄(Handler),此句柄可串行化(serialization)到持久存儲中,然后,可在其他虛擬機中,從持久存儲中,對此串行化對象進行解串行化(deserialization),取得對此遠程Home接口句柄的引用。
編寫遠程Home接口的規則
編寫EJB的遠程Home接口時,開發人員必須遵循如下規則:
· 遠程Home接口必須擴展(extend)javax.ejb.EJBHome接口;
· 定義在接口中的方法的參數和返回值必須是合法的RMI類型,并且必須顯式聲明拋出java.rmi.RemoteException異常;
· 在繼承關系上,遠程接口可以擴展已有的接口,但繼承的方法必須依從上一規則;
· 遠程接口方法必須定義一個或多個create方法。無狀態的Session Bean必須提供一個無參數的create方法,并且只能命名為“create()”的形式;每個create方法必須匹配一個定義在組件類中的ejbCreate方法,此組件類中的ejbCreate方法必須具有相同個數和類型的參數。無狀態Session Bean的create方法在組件類中的匹配方法只能命名為“ejbCreate()”的形式。;
· 每個create方法名稱開頭必須是“create” 。
· create方法的返回值類型必須是Session Bean的遠程接口;
· create方法必須聲明拋出javax.ejb.CreateException異常;
代碼范例
Home接口通過定義一個或多個create方法,提供一種或多種創建session對象的方式。create方法的參數一般用于初始化被創建session對象的狀態。
create方法的返回值類型為Bean的遠程接口類型。
下面是一個范例Session Bean的Home接口的代碼,定義了兩個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;
}
客戶端可通過如下代碼,對已部署在應用服務器上的Session Bean的Home接口進行查找,并使用CartHome接口中定義的create方法創建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接口使本地客戶端可以:
· 創建新的Session對象;
· 清除一個Session對象;
編寫本地Home接口的規則
如果EJB需要提供本地客戶端的訪問,必須提供EJB的本地Home接口,開發人員必須遵循如下規則進行編寫:
· 本地Home接口必須擴展(extend)javax.ejb.EJBLocalHome接口;
· 定義在接口中的方法不能拋出java.rmi.RemoteException異常;
· 在繼承關系上,本地接口可以擴展已有的接口;
· 本地接口方法必須定義一個或多個create方法。無狀態的Session Bean必須提供一個無參數的create方法,并且只能命名為“create()”的形式;每個create方法必須匹配一個定義在組件類中的ejbCreate方法,此組件類中的ejbCreate方法必須具有相同個數和類型的參數。無狀態Session Bean的create方法在組件類中的匹配方法只能命名為“ejbCreate()”的形式。;
· 每個create方法名稱開頭必須是“create” 。
· create方法的返回值類型必須是Session Bean的本地接口;
· create方法必須聲明拋出javax.ejb.CreateException異常;
代碼范例
客戶端可通過類似如下代碼,對已部署在應用服務器上的Session Bean的本地Home接口進行查找:
Context initialContext = new InitialContext();
CartHome cartHome = (CartHome)
initialContext.lookup(“java:comp/env/ejb/cart”);
業務(組件)接口
會話Bean和實體Bean的客戶端不能直接訪問EJB組件類的實例,客戶端程序通過業務接口訪問EJB組件,開發者在業務接口中定義可供客戶端訪問的業務方法。業務接口分為兩種類型,本地接口和遠程接口。
遠程接口
如果EJB需要被遠程客戶端訪問,必須提供EJB的遠程接口,遠程接口為組件提供以下支持:
· 定義EJB對象的業務邏輯方法,遠程接口把對業務方法的調用傳播到Session Bean實例;
· 提供允許客戶端取得遠程Home接口實例引用的方法;
· 提供使客戶端取得Session對象句柄的方法;
· 提供比較兩個EJB實例是否相等的方法;
· 移除Session Bean實例的方法;
必須遵守的的規則
開發人員必須遵循如下規則編寫遠程接口:
· 遠程接口必須使用@Remote注解標記(EJB3.0),或擴展(extend)javax.ejb.EJBObject接口(EJB2.1);
· 若使用EJB2.1規范,定義在接口中的方法的參數和返回值必須是合法的RMI類型,并且必須顯式聲明拋出java.rmi.RemoteException異常;
· 在繼承關系上,遠程接口可以擴展已有的接口,但繼承的方法必須依從上一規則;
· 每個在遠程接口中定義的方法,必須在組件類中有一個匹配的方法,組件類中的匹配方法必須與接口中定義的方法具有相同的名字、相同的參數個數、參數類型和相同的返回值,而且所有在遠程接口中定義的拋出的異常也必須在匹配方法中定義;
· 遠程接口方法不可暴露本地接口類型、本地Home接口類型,而且,對于容器管理持久性Entity Bean,也不可暴露作為Entity Bean中方法參數和返回值的受管理的集合類(collection)。
代碼范例
下面是一個EJB2.1的會話Bean的遠程接口的代碼,其中定義了三個業務方法:
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中,可使用以下形式達到同樣效果:
@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規范中,如果EJB需要被本地客戶端訪問,必須提供EJB的本地接口,本地接口為組件提供以下支持:
· 定義EJB對象的業務邏輯方法,本地接口把對業務方法的調用傳播到Session Bean實例;
· 提供允許客戶端取得本地Home接口實例引用的方法;
· 提供比較兩個EJB實例是否相等的方法;
· 移除Session Bean實例的方法;
在EJB3.0規范中,容器為遠程接口提供了位置透明性,本地客戶端亦可通過遠程接口訪問業務方法。但使用本地接口可以為EJB提供細粒度的業務方法,獲得更好的可重用、與可維護性與運行效率。EJB3.0中的本地接口可以使用@Local注解來標記。
必須遵守的規則
開發人員必須遵循如下規則編寫本地接口:
· 本地接口必須使用@Local注解進行標記,或擴展(extend)javax.ejb.EJBLocalObject接口;
· 本地接口中定義的方法不能聲明拋出java.rmi.RemoteException異常;
· 在繼承關系上,本地接口可以擴展已有的接口;
· 每個在本地接口中定義的方法,必須在組件類中有一個匹配的方法,組件類中的匹配方法必須與接口中定義的方法具有相同的名字、相同的參數個數、參數類型和相同的返回值,而且所有在遠程接口中定義的拋出的異常也必須在匹配方法中定義;
組件類
對于一個Session Bean組件,開發者在業務(組件)接口中定義的的業務方法和在Home接口中定義的create方法,需要在組件類中對這些方法提供實現。EJB2.1中的組件類必須實現javax.ejb.SessionBean接口,容器通過調用從javax.ejb.SessionBean中繼承的管理方法,提供組件訪問容器提供的服務的能力,并通過一些狀態管理回調方法,向組件實例發送其生存周期中的關鍵事件的信息。在EJB3.0中的組件類則不必實現javax.ejb.SessionBean接口,而是通過依賴注入、JNDI查找、生存周期回調方法注解達到同樣的功能。
在組件類中使用注解
EJB3.0的組件類不再強制實現javax.ejb.SessionBean接口,而是通過注解實現同樣的功能,從而避免了嚴格的隱式命名約定。在組件中常用的注解有以下幾個:
@Resource注解
在組件類中可以使用@Resource注解通知容器對上下文資源進行依賴注入,特別地,可以注入SessionContext實例:
@Stateless public class EmployeeServiceBean
implements EmployeeService{
@Resource SessionContext ctx;
...
}
@PostCreate注解
在組件類中可以使用@PostCreate注解標記初始化方法按照EJB3.0規范,容器將會在完成對會話Bean依賴注入之后,會話Bean的第一次方法被調用之前回調該方法。可以在該方法中對依賴注入后的會話Bean狀態進行初試化。
@Remove與@PreDestroy注解
在會話Bean的業務接口中使用@Remove注解標記客戶端請求清除會話Bean的方法。該方法由客戶端調用,若成功完成,容器將清除會話Bean。此注解適用于有狀態會話Bean,無狀態會話Bean的生存周期完全由容器管理,毋須在客戶端顯式刪除。
在會話Bean組件類中可使用@PreDestroy注解標記容器清除會話Bean前的回調方法。容器將在會話Bean被清除前調用該方法。一般,實例可以在此方法中對實例占用的資源進行釋放。
@PrePassivate與@PostActivate注解
在實例將被容器鈍化(Passivate)時,容器會調用被標記為PrePassivate的方法。同樣,當實例將被再次激活(Activate)時,容器會調用被標記為PostActivate的方法。在實例鈍化時,容器將會自動保存實例的狀態信息;在激活實例時,容器也將恢復被保存的實例狀態信息。一般的Session Bean都會忽略此事件,但是,對于使用某些不可串行化的資源,如與特定數據庫的連接等,作為實例狀態一部分的Session Bean,通常需要在ejbPassivate方法中釋放資源,在ejbActivate方法中重新獲取資源。
javax.ejb.SessionBean接口
在EJB2.1規范中,javax.ejb.SessionBean是容器與組件實例之間的協議。通過實現此接口,組件提供容器訪問組件的能力,實際上,容器不需要組件提供任何服務,容器通過javax.ejb.SessionBean中定義的接口方法訪問組件實例,主要是為了給組件提供訪問容器提供的服務的能力,并向組件實例發送通知其生存周期中重要事件的發生信息。
javax.ejb.SessionBean中定義了以下方法。
setSessionContext
容器通過調用setSessionContext方法,將由容器維護的Bean實例的上下文(context)與Bean實例進行關聯。一般,Session Bean實例的上下文被作為實例會話狀態的一部分,被實例所保存。
ejbRemove
在實例將被容器清除時,容器會調用此方法。一般,實例可以在此方法中對實例占用的資源進行釋放。
ejbPassivate與ejbActivate
在實例將被容器鈍化(Passivate)時,容器會調用ejbPassivate方法。同樣,當實例將被再次激活(Activate)時,容器會調用ejbActivate方法。對于使用某些不可串行化的資源,如與特定數據庫的連接等,作為實例狀態一部分的會話Bean,通常需要在ejbPassivate方法中釋放資源,在ejbActivate方法中重新獲取資源。
SessionContext接口
對于會話Bean實例,容器將提供一個SessionContext對象,使實例可以訪問由容器維護的實例的上下文環境。在SessionContext接口中,定義了如下方法:¹
· getEJBObject方法,返回Session Bean的遠程接口;
· getEJBHome方法,返回Session Bean的遠程Home接口;
· getEJBLocalObject,返回Session Bean的本地接口;
· getEJBLocalHome,返回Sesson Bean的本地Home接口;
· getCallerPrincipal方法,返回標識調用此實例的調用者的java.security.Principal對象;
· isCallerInRole方法,檢查此Session Bean的調用者是否是某個特定的角色;
· setRollbackOnly方法,當前事務將被永久標記為回滾,不會被提交。只有容器管理事務的Session Bean可被允許使用此方法;
· getRollbackOnly方法,檢查當前事務是否已被標記為回滾。例如,EJB實例可以通過此方法,判斷是否繼續在當前事務邊界內進行計算。只有容器管理事務的Session Bean可被允許使用此方法;
· getUserTransaction方法,返回javax.transaction.UserTransaction接口。EJB實例可通過此接口對事務邊界進行劃分,并取得事務的狀態。只有容器管理事務的Session Bean可被允許使用此方法;
可選的SessionSynchronization接口
會話Bean的組件類可以選擇是否實現javax.ejb.SessionSynchronization接口。此接口對會話Bean提供事務的同步通知。
· afterBegin通知,標志會話Bean實例一個新事務的開始。當包含在事務中第一個的業務方法被調用之前,容器將調用此方法,后續的業務方法調用將會存在于此事務上下文中;
· afterCompletion通知,標志一個會話Bean事務的提交操作結束,并通知實例提交操作是成功還是回滾;
· beforeCompletion通知,通知會話Bean實例事務將進行提交操作;
EJB3.0規范規定,如果會話Bean直接或間接地實現了SessionSynchronization接口,容器必須回調afterBegin, beforeCompletion與afterCompletion方法。若會話Bean沒有實現SessionSynchronization接口,則容器不會調用這些方法。
串行化的會話Bean方法調用
容器將對每個會話Bean實例的方法調用進行串行化。Apusic應用服務器中的EJB容器支持會話Bean的多個實例并發執行;但是,每個會話Bean實例只會看到依次進行的方法調用,因此開發Session Bean時,不需要將其以可重入(reentrant)的方式進行編寫。
客戶端不能對有狀態的會話Bean對象進行并發調用。當某個客戶端對某特定的會話Bean實例的業務方法調用正在執行中,從相同或不同的客戶端發出了另一個客戶端調用,容器將對第二個客戶端調用拋出java.rmi.RemoteException或javax.ejb.EJBException,這取決于第二個客戶端調用是通過遠程或本地組件接口進行調用。
業務方法必須遵守的規則
會話Bean的組件類中可以定義零到多個業務方法,其方法簽名和命名必須遵守如下規則:
· 方法名是任意的,但不能使用“ejb”開頭,以免與EJB組件架構中的容器管理回調方法發生沖突,如ejbPassivate方法;
· 業務方法必須被聲明為public方法;
· 業務方法不能被聲明為final或static;
· 如業務方法是對應于會話Bean遠程接口中定義的業務方法,則方法參數和返回值必須是合法的RMI/IIOP類型;
· 可以拋出任意應用級異常;
|
注意
|
EJB1.0允許業務方法拋出java.rmi.RemoteException,以指出非應用級的異常。在EJB1.1和EJB2.1兼容的Enterprise Bean開發中,這種方式不建議使用(deprecated),EJB2.1兼容的Enterprise Bean開發中,不應從業務方法拋出java.rmi.RemoteException。
|
代碼實例
以下是一個會話Bean組件類的范例代碼,其對應的組件接口和Home接口,請參考本章中關于組件接口和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) {}
}
生存周期
有狀態Session Bean的生存周期
有狀態Session Bean的生存周期
上圖表示有狀態Session Bean的生存周期。
有狀態會話Bean實例的生命周期開始于用戶端通過依賴注入或JNDI查找獲得一個有狀態會話Bean實例的引用,或者用戶端調用了會話Bean的Home接口中的create<METHOD>方法。這將通知容器調用會話Bean類的newInstance方法創建一個新的會話Bean實例。然后容器將注入SessionContext實例(若可用),并執行其它由元數據注解標記的或在部署描述符中設定的依賴注入。然后容器將調用會話Bean的PostConstruct生存周期回調方法(若已定義)。如果會話Bean遵照EJB2.1規范,則容器調用實例中匹配的ejbCreate<METHOD>或Init方法。最后,容器返回會話Bean的對象引用。現在實例進入就緒狀態,客戶端可以調用其業務方法。
根據會話bean元數據注解中的事務屬性或部署描述文件中的設定,以及客戶端調用所關聯的事務上下文,業務方法將會在一個特定的事務上下文環境或一個未指定的業務上下文環境中執行(在圖中分別以“事務方法”與“非事務方法”表示)。
容器的緩沖算法決定會話Bean實例是否需要移出內存(例如采取最近最久未使用算法)。容器先回調Bean實例的PrePassivate生存周期回調方法(若已定義)。然后,容器把實例的狀態信息保存到二級存儲設備中,會話Bean進入鈍化狀態。在事務之中的會話Bean不能被鈍化。
當會話Bean處于鈍化狀態,容器可能會在會話Bean超時后清除該實例,超時信息在部署描述文件中設置。此時所有對于該實例的引用都將失效。如果客戶端試圖通過業務接口調用任一方法,容器將拋出javax.ejb.NoSuchEJBException異常。如果使用EJB2.1規范,則容器分別為遠程客戶端拋出java.rmi.NoSuchObjectException異常,為本地客戶端拋出javax.ejb.NoSuchObjectLocalException異常。
如果客戶端調用了被標記為Remove的方法,或者home接口或組件接口中的remove方法,容器將在該方法成功完成后回調被標記為PreDestroy的生存周期回調方法(若已定義)。這將終結會話Bean實例的生存周期。此時所有對于該實例的引用都將失效。如果客戶端試圖通過業務接口調用任一方法,容器將拋出javax.ejb.NoSuchEJBException異常。如果使用EJB2.1規范,則容器分別為遠程客戶端拋出java.rmi.NoSuchObjectException異常,為本地客戶端拋出javax.ejb.NoSuchObjectLocalException異常。要注意即使客戶端沒有顯式調用Remove方法,容器也會在EJB對象實例超時后主動調用其PreDestroy方法并清除該實例。
如果客戶端調用了鈍化狀態的會話Bean的方法,容器將激活該實例。此時容器將先從二級存儲設備中恢復實例的狀態信息,并調用實例的PostActivte方法(若已定義)。
如果Remove方法成功完成,或Remove方法中拋出了應用異常而retainIfException為假,或拋出了系統異常,SessionSynchronization方法將不會被調用。如果拋出了應用異常而retainIfException為真,則該會話bean實例既不會被清除也不會被丟棄,SessionSynchronization方法(若已定義)將會在事務結束時被調用。
客戶端代碼對有狀態Session Bean生存周期的控制只能創建(使用依賴注入、JNDI查找或create<METHOD>方法)和清除(使用Remove方法)。其他方法由EJB容器進行調用。
無狀態Session Bean的生存周期
因無狀態Session Bean不會進行被鈍化操作,因此,其生存周期只有兩個階段,不存在和就緒狀態。下圖表示無狀態Session Bean的生存周期:
無狀態Session Bean的生存周期
posted on 2007-10-22 11:45
???MengChuChen 閱讀(2281)
評論(0) 編輯 收藏 所屬分類:
EJB3.0