摘要: 比如,我们有一个Bank(银行)类。Bank有两个方法,deposit(存钱)和withdraw(取钱)。
类和方法的定义如下:
网管有家www.bitscn.net
Code 2.1 Bank.java
class Bank{
public float deposit(AccountInfo account, float money){
// 增加account账户的钱数,返回账户里当前的钱数
}
public float withdraw(AccountInfo account, float money){
// 减少account账户的钱数,返回取出的钱数
}
};
这两个方法涉及到用户的账户资金等重要信息,必须要非常小心,所以编写完上面的商业逻辑之后,项目负责人又提出了新的要求--给Bank类的每个重要方法加上安全认证特性。
于是,我们不得不分别在上面
阅读全文
1. 簡介
Session Bean 是目前 EJB 規格 (EJB 2.0) 中 3種 Enterprise Bean 的其中一種,另外兩種分別是 Entity Bean 和 Message Driven Bean,這3種 Enterprise Bean 依照其特性被應用在不同的範疇中。其中 Session Bean 和 Entity Bean 早在 EJB 1.x 規格中就已經存在,Message Driven Bean 卻是在 EJB 2.0 才被加入。
在 EJB 架構中,Session Bean 主要目的是塑造企業流程,當中可能包括系統運算、存取資料庫、呼叫其他 Enterprise Bean等等。其範例包括流程引擎、買賣交易、型錄查詢等應用。接下來會從 Session Bean 的各個不同的特性說明其實際應用的方式、時機和要注意的地方。
2. Stateless & Stateful Session Bean
在 EJB 規格中,Session Bean被細分為 Stateless Session Bean 和 Stateful Session Bean。兩者根據特性在應用上有著很大差異:
與客戶端之間的對話資訊
Session Bean 不能被共用,每一個 Bean 只能用來服務一個客戶,而 Bean 和客戶端之間會有一份對話資訊 (請參考附註 [A] )。
●Stateless Session Bean
基本上只在單一方法呼叫中保存對話資訊,一旦 Bean 完成處理後,對話資訊會隨著被清除。
●Stateful Session Bean
在同一個 Session 裡,可以保留呼叫者與 Session Bean 之間的對話資訊與狀態,等下一次執行方法時繼續提供使用。
生命週期
Session Bean 的生命週期除了依不同廠商的 EJB Container 會有所不同外,Bean 的 Stateful 特性也會影響到其存活的時機。
●Stateless Session Bean
當客戶端所呼叫 Stateless Session Bean完畢後,Bean 會馬上被 EJB Container 銷毀,或將實例 (Instance) 保存下來以供其他客戶端使用。
●Stateful Session Bean
和 Stateless Session Bean 不同,當客戶端所呼叫Stateless Session Bean完畢後,Bean 不會馬上被 EJB Container 銷毀,而是繼續存在於 Session Bean Pool (請參考附註 [B] ),直到 Session 完結。
使用時機
●Stateless Session Bean
1. 不需要在每次客戶端對 Session Bean 的方法呼叫後保持對話資訊。
●Stateful Session Bean
1. 在每個 Session Bean 的方法呼叫之間需要保持 Session 中的資訊。
2. 利用 Session Bean 的對話狀態 (Conversational State) (請參考附錄) 管理企業流程。
效能
●Stateless Session Bean
由於不需要管理對話資訊,也就是可以佔用比較少系統資源去暫存與客戶端之間的對話資訊,而且在每次被呼叫完畢後可以馬上轉移給其他客戶端使用,故效能較佳。
●Stateful Session Bean
每次被客戶端呼叫完畢後都會將代表這客戶端的狀態資訊暫時儲存起來,在 Session 完畢後也要把之前所記錄與客戶端的對話資訊清除,所以會消耗較多系統資源。
3. 開發 Session Bean
開發一支 Session Bean 時,至少包括4個步驟:
1. 編寫 Home Interface
2. 編寫 Remote Interface
3. 編寫 Bean 的實作 (Implement) 類別,當中包括描述企業流程的方法實作
4. 編寫部署文件 (ejb-jar.xml)
編寫 Home 介面
Session Bean 的`介面繼承自 javax.ejb.EJBHome,負責控制一個 Session Bean 的生命週期,含有建立與銷毀 EJB 物件的方法,一般情況下,其命名規則為 <<Bean-name>> + Home。要注意的是,Home介面中需要定義一個 create() 方法 (對應到 Bean 實作類別的 ejbCreate()),用來取得一個 Bean 的實例。另外 Home 介面中也可以定義一個 remove() 方法對應到 Bean 實作類別的 ejbRemove()),用來移除 Bean 的實例。
【範例】
package examples;
import javax.ejb.*;
import java.rmi.RemoteException;
public Interface HelloWorldHome extends EJBHome{
HelloWorld create() throws RemoteExcception, CreateException;
}
編寫 Remote介面
繼承自 javax.ejb.EJBObject,含有 Bean 裡所有的每一個方法以供遠端程序呼叫,一段情況下,其命名規則為 <<Bean-name>>。由於遠端介面是 RMI-IIOP 遠端介面 (繼承 java.rmi.Remote) ,因此必須拋出 java.rmi.RemoteException。
【範例】
package examples;
import javax.ejb.*;
import java.rmi.RemoteException;
public Interface HelloWorld extends EJBObject{
public void helloWorld() throws RemoteException;
}
編寫 Session Bean 的實作類別
繼承自 javax.ejb.SessionBean,一段情況下,其命名規則為 <<Bean-name>> + Bean。定義一個 Session Bean 可以透過定義一個實現 javax.ejb.SessionBean 介面的類別來達成,該介面定義如下:
public Interface javax.ejb.SessionBean extends javax.ejb.EnterpriseBean{
public void ejbCreate(…) throws RemoteException;
public void ejbActivate() throws RemoteException;
public void ejbPassivate() throws RemoteException;
public void ejbRemove() throws RemoteException;
public void setSessionContext(SessionContext ctx) throws RemoteException;
}
以上幾個方法並不是要讓客戶端使用和呼叫的,而是要讓 EJB Container 使用來管理 Session Bean 的生命週期,因此不可以直接在客戶端程式裡呼叫。每個方法的說明如下,詳細的方法呼叫時機請參考「Session Bean 與 EJB Container 互動」:
方法
|
說明
|
ejbCreate(…)
|
初始化 Session Bean
|
ejbActivate()
|
EJB container 將 Session Bean 主動化 (activate) 後馬上呼叫此一方法,取得 Bean 所需資源 (在 stateless 不需實作,因為不需維護對話資訊)
|
ejbPassivate()
|
EJB container 將 Session Bean 被動化 (passivate) 後馬上呼叫此一方法,釋放 Bean 所佔的資源(如 socket 連線)
|
ejbRemove()
|
EJB container 將 Session Bean 從記憶體中移除之前會呼叫此一方法,釋放所有已配置的資源
|
setSessionContext(SessionContext ctx)
|
EJB container 將 Session Bean 與 Session Context 關聯在一起,Bean 可以從中查詢目前交易狀態和資訊
|
【範例】
package examples;
import javax.ejb.*;
public Interface HelloWorldBean extends EJBObject{
public void helloWorld(){
System.out.println(“Hello Word !!!”);
}
public void ejbCreate() throws CreateException{
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”);
}
}
編寫部署文件 ejb-jar.xml
要讓所編寫的 Session Bean 可以在 EJB Container 部署和正確運作,除了編寫程式碼外,我們還需要把 Session Bean 的特性描述在部署文件,也就是 ejb-jar.xml。編寫 Session Bean 時,不需要在程式碼中指明所寫的 Bean 是 stateless 還是 stateful,其 stateful 特性是定義在部署文件裡,也就是說可以透過修改部署文件而簡單的把 Session Bean 在 stateless 和 stateful 之間切換。
【範例】
<!DOCTYPE ejb-jar PUBLIC
“-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN”
“http://java.sun.com/dtd/ejb-jar_2_0.dtd”>
<ejb-jar>
<enterprise-Beans>
<Session>
<ejb-name>Hello</ejb-name>
<Home>examples.HelloWorldHome</Home>
<remote>examples.HelloWorld</ remote>
<ejb-class>examples.HelloWorldBean</ejb-class>
<Session-type>Stateless</Session-type>
<transaction-type>Container</transaction-type>
</Session>
</enterprise-Beans>
</ejb-jar>
<ejb-name>
|
Session Bean 的名稱
|
<Home>
|
Session Bean 的 Home 介面
|
<remote>
|
Session Bean 的 remote 介面
|
<ejb-class>
|
Session Bean 實作的類別
|
<Session-type>
|
描述Session Bean 的 stateful 特性 (Stateless / Stateful)
|
<transaction-type>
|
描述 Session Bean 的交易特性 (Container / Bean)
|
4. Session Bean 與 EJB Container 互動
在客戶端的程式呼叫 Session Bean 的方法時,其實 Bean 會根據其特性 (stateful 特性) 和 EJB container 互動,以下簡單的說明 EJB container 如何管理 Session Bean 的生命週期。
Stateless Session Bean
1. 當客戶端程式呼叫 Stateless Session Bean 上的方法之前,會先呼叫 Bean 的Home 介面中的create() 以取得一個 Bean 的參考 (reference)。
2. EJB container 會馬上呼叫 ejbCreate() 和 setSessionContext() 產生一個 Bean 實例和把 Session Context 和 Bean 關聯在一起。
3. 當方法呼叫完畢,EJB container 會呼叫 ejbRemove() 把 Bean 銷毀,Bean 所佔用的資源也會被釋放。
Stateful Session Bean
1. 和 Stateless Session Bean 一樣,客戶端程式呼叫 Stateful Session Bean 上的方法之前會先呼叫 create() 取得一個 Bean 的參考。
2. EJB container 會馬上呼叫 ejbCreate() 和 setSessionContext() 產生一個 Bean 實例和把 Session Context 和 Bean 關聯在一起。
3. 由於 Stateful Session Bean 會管理與客戶端之間的對話資訊,所以在Bean 的方法被呼叫完畢後,EJB container 不會馬上把 Bean 銷毀,而是在適當時間 (依不同廠商的EJB Container 會有所不同) 把 Bean 被動化 (呼叫 ejbPassivate()) 和將對話資訊暫存起來。
4. 當客戶端程式再次呼叫 Bean 上的方法時,如果 Bean 是在被動狀態,EJB container 會呼叫 Bean 的 ejbActivate() 把 Bean 的狀態轉為預備狀態,而之前暫存的對話資訊也會再放進 Bean 實例裡。
5. 當客戶端程式呼叫 Bean 的 remove() 或是客戶端的 Session 已經完結時,EJB container會呼叫 ejbRemove() 把 Bean 銷毀,Bean 所佔用的資源也會被釋放,之前所保持的與客戶端之間的對話資訊也會同時被清除。
附註 [A] 對話資訊:包括 Session 資訊和對話狀態 Conversational State。當客戶端程式呼叫 Session Bean 時,可能會改變其實例參數 (Instance Variable) 的值。在客戶端程式再次呼叫同一個 Session Bean 參考的方法時,Stateful Session Bean 會保留上次方法呼叫被改變的實例參數的值,但 Stateless Session Bean 卻不會。
附註 [B] Session Bean Pool:EJB container 存放 Session Bean 實例 (Instance) 的地方,可根據不同的需要而設定大小 (設定於佈署文件中),Pool 的大小會影響系統效能。
一、前言
從前面幾期的介紹,我們知道Enterprise JavaBean(EJB)是J2EE架構中用來實作business tier的技術。就J2EE Design Pattern而言,business tier代表了MVC pattern中的model,model負責處理的是對business最重要的domain objects與business logic。在這個脈絡下,J2EE Design Pattern建議我們使用session bean與message-driven bean實作business logic,而以entity bean實作domain objects。透過使用entity bean,我們以物件模式塑模(modeling)business data,這不僅使我們可以運用物件導向程式設計的優點,也得以享受EJB Container提供的各種背景服務(如Persistence、Transaction、Security、Concurrency等)。本期及下一期我們將就entity bean的種類、生命週期、程式碼的實作、以及client端的使用對entity bean做一初步的介紹。後續的幾期我們將介紹entity bean的進階功能與相關設定。
二、Entity bean的種類
就資料保存(Data Persistency)的觀點而言,entity bean以物件模式保存資料只是各種資料保存模式中的一種。在實作中,entity bean除了接收來自clent端的資料外,主要還是從所謂的Enterprise Information System tier(EIS)取得既存的資料並將處理後的資料儲存在EIS tier,而關聯式資料庫(RDBM)則是目前最為普遍的EIS tier技術。面對不同的資料保存模式,如何在不同的tier之間維持資料的一致性與完整性(Data Interity)是個重要的課題。針對這個問題,EJB提供了兩種技術:CMP bean與BMP bean。BMP bean(Bean-Managed Persistence)將有關資料保存的工作交由開發人員負責,開發人員必須熟悉EJB Container呼叫call back 方法與其他bean life-cycle方法的時機與方式,並在BMP bean中實作與存取資料庫有關的程式碼。而CMP bean(Container-Managed Persistence)則將有關資料保存的工作交由EJB Container處理,開發人員不需要在entity bean中編寫有關存取資料庫的程式碼,而只需要在部署描述子(deployment descriptor)中定義好entity bean與資料庫之間的對應(mappings),在部署階段部署工具會自動產生相關的JDBC程式碼。這麼做的好處首先在於實際部署時得以動態產生entity bean與data sources之間的繫結;更重要的是,它使開發人員得以專注於以物件導向的思維模式集中心力於business logic上。
在EJB 1.1規格書中並沒有對CMP如何與資料庫schema之間的mappings提出一個標準的規範,此外對於如何建立多個entity bean之間的關係,如何尋找與定位(finding and locating)entity bean的做法亦付之闕如。但在EJB 2.0規格書中則對CMP做出了重大的改進,包括標準的Query Language(QL)、Container-Managed Relationships(CMR)等,我們將在下期作進一步的介紹。以下是有助於選擇CMP或BMP的因素:
1. CMP比BMP易於開發維護,EJB Container會對資料存取的過程做最佳化。
2. BMP適用於對效能要求較高與較為複雜的應用程式,但若開發不當,則很容易拖垮系統的效能。
3. CMP不支援較為複雜的SQL語法,如在where子句中對日期與時間的比較。
4. CMP支援的data sources類型受container provider的限制。此外目前的CMP並不支援非JDBC的data sources。
5. 如果要使用CMR,則必須使用CMP。
三、Entity bean的生命週期
EJB Container運用集區(Pool)的概念來管理entity bean以妥善運用系統資源並增進系統效能。根據entity bean是否被放入集區與是否與EJB Object結合,entity bean有三種狀態:分別是Not Exist State、Pooled State、Ready State。下圖展示了EJB Container如何藉由呼叫bean class的call back與life-cycle方法在這三種狀態間轉換(下圖引用自Richard Monson-Haefel, Enterprise JavaBeans, 3rd Edition, O’Reilly,Page 308, Figure 11-2):

1. Not Exist State
在這個狀態中,entity bean可視為一堆檔案的集合,這些檔案包括部署描述子、component interface,以及所有在部署階段產生的輔助classes。此時不存在任何entity bean instance。
2. Pooled State
當EJB Server啟動後,它會讀取相關檔案,產生一些entity bean的instance。在產生entity bean instance後,EJB Container會呼叫entity bean的setEntityContext()方法,將EntityContext物件賦予entity bean instance,EntityContext物件記載著該entity bean instance所在的EJB Container的狀態。在entity bean instance被賦予EntityContext後,就被放到集區中,進入Pooled State。此時的entity bean只有預設值,並不代表資料庫裡的任何資料。
有幾種情況可以使得在Pooled State中的entity bean instance離開集區並被資源回收。首先,EJB Server可視需要增加或減少集區中的entity bean instance,以有效利用系統資源並在效能上有較佳表現。其次,當停止EJB Server時,EJB Container也會釋放所有在集區中的bean instance。最後,當entity bean instance出現不可修復的錯誤時,EJB Container也會將之移出集區,並以集區中其他entity bean instance取代之。
在entity bean instance被移出集區之後與被資源回收之前,EJB Container會呼叫其unsetEntityContext()方法,以通知該entity bean instance即將被毀滅。
3. Ready State
當client端呼叫entity bean的home interface上的create()方法時,EJB Server會產生一個EJB Object,並從集區中取出一個entity bean與之結合,此時entity bean進入Ready State。接著EJB Container會依序呼叫在entity bean instance上與create()對應的ejbCreate()與ejbPostCreate()方法。當ejbPostCreate()方法執行完成後,create()方法會回傳一個EJB Object的reference給client端,此時entity bean instance與EJB Object就可以服務來自client端的請求。
前面介紹Pooled State時曾提到EJB Container為了有效利用系統資源,會視需要增加或減少集區中的entity bean instance,同樣地,EJB Container也會視需要在Pooled State與Ready State之間移動entity bean instance。Entity bean instance從Ready State移至Pooled State叫做Passivation過程,從Pooled State移至Ready State則叫做Activation過程。在Passivation過程中,EJB Container會呼叫entity bean的ejbStore()方法,將目前entity bean instance的資料寫回資料庫,再呼叫entity bean的ejbPassivate()方法通知該entity bean instance其即將被移回Pooled State。ejbPassivate()方法執行完成後,entity bean instance就與EJB Object分離並回到Pooled State。
Activation過程預設了某entity bean先前的Passivation過程。在Passivation過程結束後,原先與EJB Object結合的entity bean instance回到Pooled State,而EJB Object則維持其與client端的連線。一旦client再度呼叫EJB Object上的方法時,EJB Container就從集區中任意取出一entity bean instance與既存的EJB Object結合,繼續服務來自client端的請求。在這過程中,EJB Container呼叫entity bean的ejbActivate()方法,通知該entity bean instance準備更新來自資料庫的資料,再呼叫entity bean的ejbLoad()方法通知該entity bean instance資料已經重新寫入,準備服務來自client端的請求。
最後一種從Ready State將entity bean instance移回Pooled State的情況是client端呼叫EJB Object或EJB Home上相關的的remove方法時,EJB Container會呼叫entity bean上的ejbRemove()方法,此時系統會刪除資料庫中與該entity bean instance相對應的資料。ejbRemove()方法執行完成後,該entity bean instance就被移回Pooled State。