Stateful Session Bean 為特定客戶維護會話狀態,每個並發用戶對應一個 Bean 實例。為優化記憶體使用,容器在客戶閒置超過閾值時,將 Bean 鈍化 至二級儲存(硬碟),需求恢復時再 激活 回記憶體。此機制依賴 javax.ejb.EnterpriseBean 繼承 java.io.Serializable 介面。
ejbPassivate():容器將 Bean 寫入磁碟前呼叫,開發者應釋放非序列化資源(JDBC 連線、JMS Session、Socket 等),並確保會話狀態欄位可序列化。ejbActivate():容器從磁碟還原 Bean 後呼叫,開發者重新獲取資源、恢復暫態狀態。容器保證在鈍化/激活週期中,SessionContext、Home 介面、EJBObject 引用、JNDI `java:comp/env` 資源引用自動保持有效,無需手動處理。
package com.wiley.compBooks.roman.session.count;
import javax.ejb.*;
import java.rmi.RemoteException;
public interface Count extends EJBObject {
public int count() throws RemoteException; // 遞增計數器
}
package com.wiley.compBooks.roman.session.count;
import javax.ejb.*;
public class CountBean implements SessionBean {
private SessionContext ctx;
public int val; // 會話狀態:計數器數值
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 ejbRemove() { System.out.println("ejbRemove()"); }
public void ejbActivate() { System.out.println("ejbActivate()"); }
public void ejbPassivate() { System.out.println("ejbPassivate()"); }
public void setSessionContext(SessionContext ctx) { this.ctx = ctx; }
}
package com.wiley.compBooks.roman.session.count;
import javax.ejb.*;
import java.rmi.RemoteException;
public interface CountHome extends EJBHome {
Count create(int val) throws RemoteException, CreateException;
}
部署描述符限制同時最多 2 個 Bean 實例在記憶體中。客戶端建立 3 個 EJB 物件,第 3 個將強制觸發鈍化:
public class CountClient {
public static void main(String[] args) {
try {
Properties props = System.getProperties();
Context ctx = new InitialContext(props);
CountHome home = (CountHome) ctx.lookup("CountHome");
Count[] counts = new Count[3];
int countVal = 0;
// 建立 3 個 Bean,第 3 個會被鈍化
for (int i = 0; i < 3; i++) {
counts[i] = home.create(countVal);
countVal = counts[i].count();
System.out.println(countVal);
Thread.sleep(500);
}
// 再次呼叫,驗證激活邏輯
System.out.println("Calling count() on Beans...");
for (int i = 0; i < 3; i++) {
countVal = counts[i].count();
System.out.println(countVal);
Thread.sleep(500);
}
// 清理
for (int i = 0; i < 3; i++) counts[i].remove();
} catch (Exception e) { e.printStackTrace(); }
}
}
伺服端輸出將顯示 ejbPassivate() 與 ejbActivate() 的呼叫順序,證明容器正確管理了 Bean 的持久化與還原。此機制讓有狀態 Bean 在高併發環境下仍能保持良好擴展性。