在企業級 Java 應用中,遠程客戶端訪問 Enterprise Bean 是基礎且關鍵的環節。本文以 TravelAgent EJB 為例,詳細講解如何編寫獨立 Java 客戶端應用,通過 JNDI 定位遠程接口,完成 Cabin 實體的創建與查詢操作,並深入剖析不同應用服務器的 JNDI 配置差異。
客戶端主要完成三件事:獲取 JNDI 上下文、查找遠程接口、調用業務方法。以下是標準實現:
package com.titan.clients;
import com.titan.travelagent.TravelAgentRemote;
import com.titan.domain.Cabin;
import javax.naming.*;
import javax.rmi.PortableRemoteObject;
import java.util.Properties;
public class Client {
public static void main(String[] args) {
try {
Context jndiContext = getInitialContext();
Object ref = jndiContext.lookup("TravelAgentBean/remote");
TravelAgentRemote dao = (TravelAgentRemote)
PortableRemoteObject.narrow(ref, TravelAgentRemote.class);
// 創建 Cabin 實體
Cabin cabin = new Cabin();
cabin.setId(1);
cabin.setName("Master Suite");
cabin.setDeckLevel(1);
cabin.setShipId(1);
cabin.setBedCount(3);
dao.createCabin(cabin);
// 通過主鍵查詢
Cabin found = dao.findCabin(1);
System.out.println("Name: " + found.getName());
System.out.println("DeckLevel: " + found.getDeckLevel());
System.out.println("ShipId: " + found.getShipId());
System.out.println("BedCount: " + found.getBedCount());
} catch (NamingException ne) {
ne.printStackTrace();
}
}
public static Context getInitialContext() throws NamingException {
Properties props = new Properties();
// 此處需填入廠商特定的 JNDI 屬性
return new InitialContext(props);
}
}
getInitialContext() 的具體實現強依賴應用服務器廠商,以下是主流服務器的典型配置:
Properties props = new Properties();
props.put(Context.PROVIDER_URL, "iiop:///");
props.put(Context.INITIAL_CONTEXT_FACTORY,
"com.ibm.ejs.ns.jndi.CNInitialContextFactory");
return new InitialContext(props);
Properties props = new Properties();
props.put(Context.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory");
props.put(Context.URL_PKG_PREFIXES,
"org.jboss.naming:org.jnp.interfaces");
props.put(Context.PROVIDER_URL, "jnp://localhost:1099");
return new InitialContext(props);
若客戶端本身運行在 Java EE 容器中,無需顯式設置屬性,容器會自動注入預配置的 JNDI 環境:
public static Context getInitialContext() throws NamingException {
return new InitialContext(); // 無參構造即可
}
此時查找名稱通常綁定在 java:comp/env/ejb/ 名字空間下,例如:
Object ref = jndiContext.lookup("java:comp/env/ejb/TravelAgentRemote");
JNDI lookup() 返回的是 java.lang.Object,必須通過 PortableRemoteObject.narrow() 窄化為具體的遠程接口類型。這是因為 RMI-IIOP 基於 CORBA,而 CORBA 不支持直接強制轉型(某些語言無轉型概念),因此需要顯式窄化操作。此機制自 EJB 1.1 引入,沿用至 EJB 3.0 遠程客戶端。
JNDI 查找名稱(如 TravelAgentBean/remote)由部署人員在部署描述文件或管理控制台中指定,與代碼中的邏輯名稱解耦。部署時需將邏輯名稱映射到具體的 EJB 遠程接口。EJB 3.0 也支持通過 @RemoteBinding 等注解直接指定 JNDI 名稱,簡化配置。
PortableRemoteObject.narrow()。InitialContext,查找名稱綁定在 java:comp/env/ejb/,利用依賴注入(@EJB)更簡潔。NamingException,記錄日誌並提供友好錯誤提示。掌握 JNDI 查找機制與廠商差異,是構建健壯、可移植 EJB 客戶端應用的基石。