雖然 EJB 3 已成為主流,但理解 EJB 2.x 架構對維護舊系統、理解演進歷程仍有價值。本文記錄 EJB 2.0 有狀態 SessionBean 完整開發流程:Bean 類、Component 介面、Home 介面、部署描述檔、JBoss 配置、客戶端呼叫。
| 特性 | EJB 2.x | EJB 3.x |
| Bean 類 | implements SessionBean | POJO + @Stateful/@Stateless |
| 建構方法 | ejbCreate(...) 由容器呼叫 | 普通建構子 + @PostConstruct |
| 業務介面 | Component Interface (extends EJBObject) | POJO Interface + @Remote/@Local |
| Home 介面 | 必須 (extends EJBHome) | 不需要 (JNDI 直接查 Bean) |
| 遠端呼叫 | RMI-IIOP、PortableRemoteObject.narrow | RMI、直接轉型 |
| 部署描述檔 | ejb-jar.xml (強制) | 註解為主、XML 可選 |
三、Component 介面:HelloStateful
package lavasoft.testejb20yzt;
import javax.ejb.EJBObject;
import java.rmi.RemoteException;
public interface HelloStateful extends EJBObject {
String sayHello() throws RemoteException; // 必須拋 RemoteException
}
四、Home 介面:HelloStatefulHome
package lavasoft.testejb20yzt;
import javax.ejb.CreateException;
import javax.ejb.EJBHome;
import java.rmi.RemoteException;
public interface HelloStatefulHome extends EJBHome {
HelloStateful create(String someOne) throws CreateException, RemoteException;
}

五、標準部署描述檔:ejb-jar.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN'
'https://java.sun.com/dtd/ejb-jar_2_0.dtd'>
<ejb-jar>
<display-name>Hello Stateful EJB</display-name>
<enterprise-beans>
<session>
<display-name>helloStatefulEJB</display-name>
<ejb-name>helloStatefulEJB</ejb-name>
<home>lavasoft.testejb20yzt.HelloStatefulHome</home>
<remote>lavasoft.testejb20yzt.HelloStateful</remote>
<ejb-class>lavasoft.testejb20yzt.HelloStatefulBean</ejb-class>
<session-type>Stateful</session-type>
<transaction-type>Bean</transaction-type>
<security-identity>
<use-caller-identity/>
</security-identity>
</session>
</enterprise-beans>
</ejb-jar>
六、JBoss 專屬配置:jboss.xml
<?xml version="1.0" encoding="UTF-8"?>
<jboss>
<enterprise-beans>
<session>
<ejb-name>helloStatefulEJB</ejb-name>
<jndi-name>ejb/helloStatefulEJB</jndi-name>
</session>
</enterprise-beans>
</jboss>
七、客戶端呼叫程式碼
package lavasoft.client;
import lavasoft.testejb20yzt.HelloStateful;
import lavasoft.testejb20yzt.HelloStatefulHome;
import javax.ejb.CreateException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;
import java.rmi.RemoteException;
import java.util.Properties;
public class TestStatefulSessionBeanClient {
public static void main(String[] args) throws Exception {
Properties props = new Properties();
props.setProperty("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
props.setProperty("java.naming.provider.url", "localhost:1099");
InitialContext ctx = new InitialContext(props);
// 1. 查詢 Home 介面
Object objref = ctx.lookup("ejb/helloStatefulEJB");
// 2. 關鍵:PortableRemoteObject.narrow 進行類型轉換 (RMI-IIOP 要求)
HelloStatefulHome home = (HelloStatefulHome) PortableRemoteObject.narrow(objref, HelloStatefulHome.class);
// 3. 透過 Home 建立 Component 介面 (對應 ejbCreate)
HelloStateful hello = home.create("StatefulEJB");
// 4. 呼叫業務方法
String msg = hello.sayHello();
System.out.println(msg);
}
}
八、執行結果驗證
客戶端輸出:
Hello, StatefulEJB!

JBoss 控制台輸出:
HelloStatefulBean: setSessionContext() is called!
HelloStatefulBean: ejbCreate(String someOne) is called!
HelloStatefulBean: sayHello() is called!

九、本地介面規則 (補充)
EJB 2.x 本地介面需額外定義:
- Local Interface:extends
EJBLocalObject,方法不拋 RemoteException
- Local Home:extends
EJBLocalHome
- ejb-jar.xml 使用
<local-home>、<local> 元素
- JNDI 名稱通常為
ejb/xxxLocal
- 同 JVM 呼叫無需 PortableRemoteObject.narrow

十、遷移至 EJB 3 核心步驟
- 移除
implements SessionBean、所有 ejbXxx 方法
- Component 介面移除
extends EJBObject、RemoteException,加 @Remote
- 刪除 Home 介面
- Bean 類加
@Stateful,ejbCreate 改為建構子或 @PostConstruct
- 刪除 ejb-jar.xml、jboss.xml (或保留僅覆蓋 JNDI)
- 客戶端移除
PortableRemoteObject.narrow,直接 ctx.lookup("BeanName/remote") 轉型
- 上一篇文章: Java 反射深度測試:建構物件、呼叫私有方法、存取私有欄位完整實戰
- 下一篇文章: Struts 2 select/doubleselect/updownselect 標籤資料顯示完整指南
Copyright ©
程式碼之家 All Rights Reserved