一個 EJB 遠程調用涉及 10 個類,遠超普通 RMI 的 4 個:
| 類別 | 用戶編寫 | 產生時機 | 說明 |
|---|---|---|---|
| Bean 類 | ✓ | 開發期 | 業務邏輯實現 |
| Bean WebLogic 實現類 | ✗ | ejbc 編譯期 | 容器生成,攔截生命週期 |
| Remote 介面 | ✓ | 開發期 | 客戶端調用契約 |
| Remote WebLogic 實現類 (EOImpl) | ✗ | ejbc 編譯期 | 委託 Bean 實現類 |
| EOImpl Stub | ✗ | 部署期動態生成 | 客戶端代理,序列化傳遞 |
| EOImpl Skeleton | ✗ | 部署期動態生成 | 伺服器端接收調用 |
| Home 介面 | ✓ | 開發期 | 生命週期管理契約 |
| Home WebLogic 實現類 (HomeImpl) | ✗ | ejbc 編譯期 | 實現 create/find 等 |
| HomeImpl Stub | ✗ | 部署期動態生成 | JNDI 查詢返回的代理 |
| HomeImpl Skeleton | ✗ | 部署期動態生成 | 伺服器端處理 Home 調用 |
關鍵差異:EJB 的 Stub 由伺服器動態生成並序列化傳給客戶端,客戶端無需預先部署 Stub 類。
ctx.lookup("Hello")HelloBean_HomeImpl_WLStub 實例Home 介面引用(實為 HomeImpl Stub)home.create() → 實際調用 HomeImpl_WLStub.create()HomeImpl_WLSkeleton → HomeImpl.create()HomeImpl.create() 呼叫 BeanImpl.ejbCreate() 創建 Bean 實例HomeImpl 創建 EOImpl_WLStub 實例,序列化傳回客戶端remote.helloWorld() → 實際調用 EOImpl_WLStub.helloWorld()EOImpl_WLSkeleton → EOImpl.helloWorld()EOImpl 委託 BeanImpl.helloWorld()(用戶代碼終於被執行!)HomeImpl 在伺服器端充當 BeanImpl 的代理(非 RMI,進程內調用),EOImpl 同理。容器透過這層攔截實現事務、安全、持久化等企業級服務。
客戶端代碼只依賴 Home.class、Remote.class,完全屏蔽實現類、Stub、Skeleton、容器生成類。部署時動態生成 Stub/Skeleton 字節碼,實現「零客戶端部署」。
// 用戶編寫
HelloBean // Bean 類
HelloHome // Home 介面
Hello // Remote 介面
// ejbc 生成(打包在 EJB JAR)
HelloBean_Impl
HelloBean_HomeImpl
HelloBean_EOImpl
// 部署期動態生成(不見類文件)
HelloBean_HomeImpl_WLStub / _WLSkeleton
HelloBean_EOImpl_WLStub / _WLSkeleton
HelloHome home = (HelloHome) PortableRemoteObject.narrow(
ctx.lookup("Hello"), HelloHome.class);
System.out.println(home); // 輸出: HelloBean_HomeImpl_WLStub@18c458
Hello remote = home.create();
System.out.println(remote); // 輸出: HelloBean_EOImpl_WLStub@8fa0d1
remote.helloWorld(); // 觸發第二次 RMI 循環
EJB 遠程調用本質是雙層 RMI 代理鏈路:Home Stub 創建 Remote Stub,Remote Stub 委託業務邏輯。容器生成的實現類與動態 Stub/Skeleton 構成攔截層,將企業級關注點(事務、安全、資源管理)與業務邏輯分離。雖然 EJB 2.x 已被輕量級框架取代,但其分層代理、介面契約、伺服器端動態代理生成等設計思想,深刻影響了後續的 Spring、gRPC、微服務側車等架構演進。