典型分佈式部署架構:Web 容器 (Tomcat/Jetty/獨立 WebLogic) 與 EJB 容器 (WebLogic/WebSphere/JBoss) 分離部署。核心挑戰:JNDI 遠程查找配置 與 Client Stub 類可用性。
客戶端 (Web 模塊) 必須告知 JNDI 服務提供者 (EJB 容器) 的位置與協議。
Properties env = new Properties();
env.put("java.naming.factory.initial", "com.sun.jndi.cosnaming.CNCtxFactory"); // Sun CORBA Naming
env.put("java.naming.provider.url", "iiop://192.168.0.2:3700"); // EJB 容器 IIOP 監聽地址
// 可選:安全憑證
// env.put("java.naming.security.principal", "ejbuser");
// env.put("java.naming.security.credentials", "ejbpwd");
Context ctx = new InitialContext(env);
Hashtable env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.cosnaming.CNCtxFactory");
env.put(Context.PROVIDER_URL, "iiop://192.168.0.2:3700");
// WebLogic 專用工廠 (若調用 WebLogic EJB)
// env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
// env.put(Context.PROVIDER_URL, "t3://192.168.0.2:7001");
Context ctx = new InitialContext(env);
| 參數 | 說明 | 常見值 |
|---|---|---|
java.naming.factory.initial | JNDI SPI 實現類,決定協議 (IIOP/T3/HTTP) | Sun: com.sun.jndi.cosnaming.CNCtxFactoryWebLogic: weblogic.jndi.WLInitialContextFactoryJBoss: org.jnp.interfaces.NamingContextFactory (Legacy) / org.wildfly.naming.client.WildFlyInitialContextFactory (EJB Client API) |
java.naming.provider.url | 命名服務端點 (IP:Port) | iiop://host:port (標準 CORBA)t3://host:port (WebLogic 專有)https://host:port (HTTP 穿透) |
java.naming.security.principal/credentials | 安全認證 (若 EJB 啟用安全) | 用戶名/密碼 |
原理:RMI-IIOP 遠程調用需要客戶端持有 Stub (存根) 類,負責序列化參數、發送 IIOP 請求、反序列化返回值。Stub 類在 EJB 部署時由容器工具 (如 appc, ejbc, rmic -iiop) 自動生成。
MyServiceRemote.java)。MyServiceHome.java) —— EJB 2.x 必需。MyServiceRemote_Stub.class, MyServiceHome_Stub.class)。WebApp.war
└── WEB-INF/
└── lib/
└── my-ejb-client.jar <-- 打包上述所有接口 + Stub 類
注意:不要** 將 EJB 實現類 (Bean 類) 打包到 Client JAR,僅需接口與 Stub。
public class EJBClientServlet extends HttpServlet {
private MyServiceRemote service;
public void init() throws ServletException {
try {
// 1. 構建 JNDI 環境 (可從 web.xml context-param 讀取)
Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
env.put(Context.PROVIDER_URL, "t3://192.168.0.2:7001");
Context ctx = new InitialContext(env);
// 2. 查找 Home 接口
Object homeRef = ctx.lookup("ejb/MyService"); // JNDI 名稱需與 EJB 部署一致
MyServiceHome home = (MyServiceHome) PortableRemoteObject.narrow(homeRef, MyServiceHome.class);
// 3. 創建 Remote 業務代理
service = home.create();
} catch (Exception e) {
throw new ServletException("EJB 初始化失敗", e);
}
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
try {
String result = service.doBusiness(req.getParameter("param"));
resp.getWriter().println("EJB 調用結果: " + result);
} catch (RemoteException e) {
resp.sendError(500, "EJB 遠程調用失敗: " + e.getMessage());
}
}
}
| 現象 | 可能原因 | 解決方案 |
|---|---|---|
ClassNotFoundException: ..._Stub | Client JAR 缺少 Stub 類 | 重新編譯/部署 EJB 生成 Stub,打包進 WEB-INF/lib |
ClassCastException: ... cannot be cast to MyServiceHome | Stub 版本不匹配 / 接口不一致 | 確保 Client JAR 與 EJB 部署版本完全一致;清理緩存重新部署 |
CommunicationException / ConnectException | 網絡不通 / 端口錯誤 / 防火牆 | telnet IP Port 驗證;檢查 PROVIDER_URL 協議/端口;關閉防火牆或開放端口 |
NoClassDefFoundError: org/omg/CORBA/... | 客戶端缺少 CORBA 運行時庫 | WebLogic: 添加 wlclient.jar; WildFly: 添加 wildfly-ejb-client-bom 依賴 |
SecurityException / AuthenticationFailed | 安全憑證缺失/錯誤 | 在 env 中設置 principal/credentials;或配置 SSL 互信 |
@EJB MyService service 注入。跨服務器仍需 Remote 接口。@Path 資源,Web 端用 HttpClient/RestTemplate 調用,**無需 Stub,防火牆友好,異構語言互通**。.proto,生成多語言 Stub,性能極佳,適合微服務架構。跨服務器調用 EJB 核心在於:**正確的 JNDI 初始化參數 (工廠類 + IIOP/T3 URL) + 完整的 Client Stub JAR 部署**。掌握此機制,是維護傳統 J2EE 分佈式系統、進行微服務化拆分前的必備技能。新架構建議遷移至 REST/gRPC,降低耦合與運維複雜度。