EJB 容器之所以能提供透明的遠程調用、負載均衡、故障轉移、安全傳播,核心依賴於 RMI-IIOP 協議棧與 JNDI 命名服務。本文深度剖析 RMI-IIOP 遠程接口設計、Stub/Skeleton 代理機制、對象序列化傳遞語義、JNDI Context 體系與 EJB 客戶端查找流程。
public interface MyRemote extends Remote {
// 1. 必須拋出 RemoteException (或父類 Exception)
// 2. 參數/返回值: 基本類型、Serializable、Remote 接口
// 3. 方法簽名不可為 final/static (接口默認 public abstract)
String process(Request req) throws RemoteException, BusinessException;
// 返回遠程對象引用 (傳遞 Stub)
OtherService getService() throws RemoteException;
}
// 方式一: 繼承 UnicastRemoteObject (自動導出,綁定匿名端口)
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote {
public MyRemoteImpl() throws RemoteException { super(); }
// ... 實現方法
}
// 方式二: 不繼承 (需繼承其他類),手動導出
public class MyRemoteImpl extends SomeBaseClass implements MyRemote {
public MyRemoteImpl() throws RemoteException {
// 導出對象,可指定端口、Socket 工廠
UnicastRemoteObject.exportObject(this, 0); // 0=匿名端口
}
// ... 實現方法
// 取消導出: UnicastRemoteObject.unexportObject(this, true);
}
Client JVM Server JVM
┌─────────────────┐ ┌─────────────────┐
│ Client Code │ │ Remote Impl │
│ myRemote.do() │ │ do() │
└────────┬────────┘ └────────┬────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Stub (Proxy) │ JRMP/IIOP │ Skeleton │
│ - 連接管理 │ ◄─────────────► │ - 反序列化參數 │
│ - 參數序列化 │ 網絡傳輸 │ - 分發調用實現 │
│ - 等待響應 │ │ - 序列化結果 │
│ - 反序列化結果 │ └─────────────────┘
└─────────────────┘
│
▼
Return Value
_Stub.class。rmic -iiop MyRemoteImpl (生成 IIOP Stub/Tie),rmic -v1.2 MyRemoteImpl (生成 JRMP Stub)。| 對象類型 | 傳遞方式 | 序列化機制 | 遠程端修改影響 |
|---|---|---|---|
| 基本類型 | 按值 | 直接寫入流 | 無影響 |
| 實現 Serializable | 按值 (深度複製) | ObjectOutputStream (默認) / Externalizable (自定義) | 修改副本,不影響源 |
| 實現 Remote | 按引用 | 傳遞 Stub 參考 | 調用路由回源端,**影響源** |
| 既 Serializable 又 Remote | 按引用 (Remote 優先) | 傳遞 Stub | 影響源 |
serialVersionUID:強烈建議顯式聲明,避免版本不匹配導致 InvalidClassException。transient:標記字段不參與序列化 (如密碼、連接句柄、緩存)。Externalizable:完全控制讀寫格式 (writeExternal/readExternal),性能優於默認序列化。java.rmi.server.codebase (HTTP/FTP) 下載,**需 SecurityManager 允許**。lookup, bind, list, createSubcontext 等操作。java.naming.factory.initial, provider.url) 確定具體 SPI 實現 (LDAP, CORBA, RMI, DNS, File)。CompositeName) 或複合名稱 (CompoundName),支持層級路徑 java:comp/env/jdbc/MyDB。ejb/MyBean)。Context ctx = new InitialContext(props);
Object ref = ctx.lookup("ejb/MyBean");
MyBeanHome home = (MyBeanHome) PortableRemoteObject.narrow(ref, MyBeanHome.class);
java:comp/env/jdbc/MyDB (DataSource), java:comp/env/jms/MyQueue (JMS), java:comp/env/mail/MyMail (JavaMail)。env-entry 注入配置值 (String, Integer, Boolean 等)。| 屬性常量 | 字符串鍵 | 說明 |
|---|---|---|
| Context.INITIAL_CONTEXT_FACTORY | java.naming.factory.initial | SPI 實現類全限定名 |
| Context.PROVIDER_URL | java.naming.provider.url | 服務端點 URL |
| Context.SECURITY_PRINCIPAL | java.naming.security.principal | 用戶名 |
| Context.SECURITY_CREDENTIALS | java.naming.security.credentials | 密碼 |
| Context.URL_PKG_PREFIXES | java.naming.factory.url.pkgs | URL 上下文工廠包前綴 |
InitialContext (指定 IIOP 工廠 + Provider URL)。lookup("ejb/MyBean") 發起 CORBA/IIOP 查找請求到命名服務 (CosNaming)。PortableRemoteObject.narrow() 縮窄轉型為 Home 接口代理。home.create() → Stub 發送 IIOP 請求 → 服務端 Skeleton/Tie 分發 → 容器創建 EJB 實例/從池獲取 → 返回 EJBObject Stub。RMI-IIOP 提供了**標準化的遠程對象調用協議棧** (接口、Stub/Skeleton、序列化、JRMP/IIOP),JNDI 提供了**統一的命名查找抽象層** (Context、SPI、Reference)。二者結合,構成了 EJB 透明分布式、容器管理服務、異構互操作的基礎設施。掌握遠程接口規範、序列化語義、Stub 代理機制、JNDI 環境配置,是排查 EJB 通訊失敗、ClassCastException、序列化異常、查找超時等疑難雜症的關鍵。