會話Bean同其他企業(yè)Bean類型最主要的區(qū)別是生命周期的差異性。會話Bean實例是存活短暫的對象,會話Bean實例并不能夠在多客戶間共享。
通常,客戶會話的持續(xù)期決定了使用中的會話Bean的存活期,一旦應(yīng)用服務(wù)器癱瘓,會話Bean實例也應(yīng)該不復(fù)存在。因為,會話Bean僅僅是內(nèi)存對象,一旦其生存的周邊環(huán)境遭到破壞,會話Bean也將不復(fù)存在。會話Bean并不是持久化的,因此,會話Bean并不會保存到持久化存儲源中,這同實體Bean不一樣,會話Bean能夠操作RDBMS,但是其本身并不是持久化對象。
會話指客戶同EJB組件的交互,它由客戶和EJB組件間的多次方法調(diào)用構(gòu)成。會話Bean存在2種子類型:有狀態(tài)會話Bean和無狀態(tài)會話Bean,各自用于建模不同類型的會話。有狀態(tài)會話Bean是這樣一種EJB,即其服務(wù)的業(yè)務(wù)過程能夠延伸到多個方法請求或者事務(wù)中,為完成這種業(yè)務(wù)過程,有狀態(tài)會話Bean需要為單個客戶保存狀態(tài)信息。如果在方法調(diào)用期間有狀態(tài)會話Bean的狀態(tài)發(fā)生改變,則這種改變必須反映到同一客戶的隨后調(diào)用中。無狀態(tài)會話Bean是這樣一種EJB,即其服務(wù)的業(yè)務(wù)過程只需要單個業(yè)務(wù)方法即可完成。由于他們不需維護客戶多個方法調(diào)用間的會話狀態(tài),因此它是無狀態(tài)的。在每次方法調(diào)用結(jié)束后,EJB容器可能會銷毀無狀態(tài)會話Bean實例,或者實例化新的實例,或者清楚掉上次方法調(diào)用中的相關(guān)信息。
無狀態(tài)意指不存在會話狀態(tài)。無狀態(tài)會話Bean能夠含有同特定客戶不相關(guān)的狀態(tài)信息,比如所有客戶將使用到數(shù)據(jù)庫鏈接工廠,開發(fā)者可以將它存儲在private變量中。如果開發(fā)者將數(shù)據(jù)存儲在private變量中,則將隨時丟失其中存儲的數(shù)據(jù)。
EJB容器將維護EJB實例池,而且這些EJB實例是可重用的。在每次方法調(diào)用時,都會有不同EJB實例或同一實例服務(wù)客戶。為了限制內(nèi)存中運行的有狀態(tài)會話Bean實例的數(shù)量,EJB容器需要將有狀態(tài)會話Bean的會話狀態(tài)保存到硬盤或者其他存儲源中。該過程稱之為掛起。在掛起有狀態(tài)會話Bean后,會話狀態(tài)被安全的保存下來,而且其釋放的內(nèi)存可以供其他應(yīng)用使用。一旦被掛起的有狀態(tài)會話Bean實例的客戶再次調(diào)用它,被掛起的會話狀態(tài)將重新回到有狀態(tài)會話Bean實例中,該過程稱之為激活。
有狀態(tài)會話Bean實例的會話狀態(tài)必須遵循Java對象序列化設(shè)定的規(guī)則。在掛起有狀態(tài)會話Bean實例時,EJB容器借助于對象序列化將會話狀態(tài)轉(zhuǎn)換成二進(jìn)制blob,然后將它寫入到硬盤中。在轉(zhuǎn)移會話狀態(tài)信息后,有狀態(tài)會話Bean實例(指刮起了會話狀態(tài)的那些EJB實例)還能夠服務(wù)于其他客戶,即同新的客戶進(jìn)行新的會話過程。
一旦EJB中的成員變量符合如下條件,則可以認(rèn)為它是會話狀態(tài)的組成部分之一。
1、成員變量是非transient類型的java原始類型。
2、成員變量是非transient類型的Java對象類型。
當(dāng)容器將EJB實例掛起時,它需要將實例的會話狀態(tài)寫入到二級存儲源中,比如文件或者RDBMS中。通過調(diào)用EJB實例的ejbPassivate()回調(diào)方法,容器能夠完成實例的掛起工作。借助于ejbPassivate()方法能夠告知EJB實例:EJB容器需要掛起它,這使得釋放其持有的資源成為可能。比如EJB實例可能持有的資源有:RDBMS連接、已打開的Socket和文件或者其他任何資源。
在實際場合中,客戶調(diào)用了EJB對象中的某個方法,而當(dāng)時在內(nèi)存中暫時找不到該EJB對象,與此同時,EJB容器持有的企業(yè)Bean實例的個數(shù)已經(jīng)到達(dá)了設(shè)定的上限。因此在處理客戶請求前,容器需要掛起最近未使用的EJB實例,在掛起它后,容器才能夠獲得所需的EJB對象。
有狀態(tài)會話Bean的部署描述符。
<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar version="2.1" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd">
<enterprise-beans>
<session>
<ejb-name>Count</ejb-name>
<home>examples.CountHome</home>
<remote>examples.Count</remote>
<ejb-class>examples.CountBean</ejb-class>
<session-type>Stateful</session-type>
<transaction-type>Container</transaction-type>
</session>
</enterprise-beans>
</ejb-jar>
服務(wù)端企業(yè)Bean:
package com.wyq.ejb02;
import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
/**
* 演示有狀態(tài)Bean
* 它會初始化val,并提供業(yè)務(wù)方法。
* 該實例演示了最簡單的有狀態(tài)會話Bean,并給出了掛起、激活的工作機理。
*/
public class CountBean implements SessionBean {
//會話狀態(tài)
public int val;
//業(yè)務(wù)方法
public int count(){
System.out.println("count");
return ++val;
}
public void ejbCreate(int val)throws CreateException{
this.val = val;
System.out.println("ejbCreate()");
}
public void ejbActivate() throws EJBException, RemoteException {
System.out.println("ejbActivate()");
}
public void ejbPassivate() throws EJBException, RemoteException {
System.out.println("ejbPassivate()");
}
public void ejbRemove() throws EJBException, RemoteException {
System.out.println("ejbRemove()");
}
public void setSessionContext(SessionContext ctx) throws EJBException,
RemoteException {
}
}
客戶端調(diào)用:
package com.wyq.ejb02;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import com.wyq.ejb01.HelloHome;
/**
* 1、獲得JNDI InitialContext上下文。
* 2、借助于JNDI,定義Home對象。
* 3、使用Home對象創(chuàng)建3個不同的CountEjb對象。因此,這將建立起3個不同的會話過程,而且模擬了3個不同的客戶。
* 4、由于內(nèi)存中僅能存活2個EJB實例,因此在創(chuàng)建Count EJB實例期間,EJB容器需要完成實例的掛起操作。
* 5、調(diào)用各個EJB對象的count()方法。
* 6、最后,刪除所有的EJB對象。
*/
public class CountClient {
/**
* 客戶代碼示例
*
* 此時,創(chuàng)建了3個EJB對象。但我們規(guī)定容器:在內(nèi)存中最多存在2個實例。
* 因為,能夠看到掛起操作的發(fā)生。
*/
public static void main(String[] args) {
try{
/*
* 獲得JNDI環(huán)境屬性
*/
Properties props = System.getProperties();
/*
* 獲得對Home對象的引用,Home對象是EJB對象的工廠
*/
Context ctx = new InitialContext(props);
Object obj = ctx.lookup("CountHome");
CountHome home =(CountHome)javax.rmi.PortableRemoteObject.narrow(obj,CountHome.class);
/*
* 能夠持有3個Count對象的數(shù)組
*/
Count count[] = new Count[3];
int countVal = 0;
/*
* 創(chuàng)建EJB實例,并調(diào)用各自的count()
*/
System.out.println("Instantiating beans
");
for(int i=0;i<3;i++){
/*
* 創(chuàng)建EJB對象,并初始化它們
*/
count[i] = home.create(countVal);
/*
* 加1,并打印出來
*/
countVal = count[i].count();
System.out.print(countVal);
/*
* 等待1/2秒
*/
Thread.sleep(500);
}
/*
* 調(diào)用各個EJB對象的count()方法,從而能夠瀏覽到EJB被掛起,并被成功激活
*/
System.out.println("Calling count() on beans
");
for(int i=0;i<3;i++){
/*
* 加1,并打印出來
*/
countVal = count[i].count();
System.out.println(countVal);
/*
* 等待1/2秒
*/
Thread.sleep(500);
}
/*
* 使用完后,銷毀它們
*/
for(int i=0;i<3;i++){
count[i].remove();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
會話Bean的聲明周期流程圖
1、起初,EJB實例并不存在。
2、EJB容器將決定是否需要實例化新的EJB實例。容器將何時實例化新的EJB實例,取決于容器使用的EJB實例池策略。
3、容器實例化EJB Bean類。EJB容器將調(diào)用Class.newInsatance("HelloBean.class");即動態(tài)創(chuàng)建HelloBean實例,這使得容器不會將EJBBean類的名字硬編碼在Java代碼中。最后,這使得容器更具通用性,能夠操控任何企業(yè)Bean.
4、容器調(diào)用setSessionContext()方法。這為EJB實例設(shè)置了上下文對象。最終,EJB實例將能夠訪問到EJB容器。
5、容器調(diào)用ejbCreate().這將初始化EJB實例。由于無狀態(tài)會話Bean的ejbCreate()方法并不存在參數(shù),因此EJB客戶不可能為它提供任何啟動EJB實例的參數(shù)信息。
6、EJB容器調(diào)用EJB實例的業(yè)務(wù)方法。對于EJB實例提供的所有業(yè)務(wù)方法,EJB容器都可以使用。由于所有EJB實例間不存在區(qū)別,因此完全不同的客戶可以調(diào)用相同的業(yè)務(wù)方法。在業(yè)務(wù)方法調(diào)用結(jié)束后,各個無狀態(tài)會話Bean實例依然是相同的,因此,EJB容器能夠針對客戶請求,在每個方法級將各個EJB實例指定給客戶,即使同一客戶對同一業(yè)務(wù)方法的多次調(diào)用,都可以由不同的EJB實例響應(yīng)它,當(dāng)然,將EJB實例指定給客戶的具體實現(xiàn)策略取決于具體的EJB容器。
7、最后,容器調(diào)用ejbRemove()方法。
posted on 2009-11-02 16:35
王永慶 閱讀(190)
評論(0) 編輯 收藏 所屬分類:
EJB學(xué)習(xí)筆記