同一份客戶端代碼,在應用服務器本地運行完美;部署到獨立客戶機卻拋出 NoClassDefFoundError、NamingException、ClassCastException。根因在於:本地環境自動注入 JNDI 屬性、Stub/Tie 類、安全上下文;遠程環境一切靠手工配置。
| 術語 | 比喻 | 技術含義 |
|---|---|---|
| 命名空間 | 城鎮 | EJB Home 發布的邏輯容器 |
| 上下文 | 城鎮位置 | 命名空間中的某個節點 |
| 初始上下文 | 進城大道 | 客戶端連接命名服務的入口 |
| 子上下文 | 街道名 | 從初始上下文到目標的相對路徑 |
| Home 接口 | 店鋪招牌 | 定義 create/find 方法,JNDI 發布點 |
| Remote 接口 | 店員技能 | 業務方法集合,create() 後獲得 |
| JNDI | 城鎮模板 | 統一命名/目錄訪問 API |
| CosNaming/LDAP | 具體城鎮 | JNDI 兩大服務提供者實現 |
jndi.properties(類路徑首個)-Djava.naming.factory.initial=... -Djava.naming.provider.url=...new InitialContext(props) 顯式傳遞最佳實踐:採用方式 3,將屬性外部化到配置文件/命令行,避免硬編碼,支援多環境部署。
# JDK 自帶
tnameserv -ORBInitialPort 900 # Windows: tnameserv.exe
Properties props = new Properties();
props.put("java.naming.factory.initial", "com.sun.jndi.cosnaming.CNCtxFactory");
props.put("java.naming.provider.url", "iiop://localhost:900");
InitialContext ic = new InitialContext(props);
ic.createSubcontext("Test"); // 驗證命名空間可寫
String lookupName = "ejb/HelloWorldHome"; // 相對初始上下文路徑
Object ref = ic.lookup(lookupName);
HelloWorldHome home = (HelloWorldHome) PortableRemoteObject.narrow(ref, HelloWorldHome.class);
HelloWorld ejb = home.create();
ejb.hello();
關鍵:lookupName 必須與部署工具綁定的 JNDI 名一致。WebSphere 預設綁定在 ejb/ 子上下文,若用非 WebSphere 初始上下文工廠,需傳完整路徑 ejb/HelloWorldHome。
/ 分隔,如 ejb/HelloWorldHome, 分隔,且須反序:cn=HelloWorldHome,cn=MyServer,o=myldapprops.put("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory");
props.put("java.naming.provider.url", "ldap://localhost:1389/o=myldap"); // BaseDN 在 URL 中
// 查找名:反序 DN
ic.lookup("cn=HelloWorldHome,cn=MyServer");
| 項目 | WebLogic | WebSphere |
|---|---|---|
| InitialContextFactory | weblogic.jndi.WLInitialContextFactory | com.ibm.websphere.naming.WsnInitialContextFactory |
| Provider URL | t3://host:7001 | iiop://host:2809 |
| UserTransaction JNDI | javax.transaction.UserTransaction | jta/usertransaction |
| 客戶端 JDK | 任意 JDK | 必須 IBM JDK(IIOP Stub 依賴) |
j2ee.jar 等每階段必備public class EJBLookupHelper {
public static Object lookup(String factory, String url, String name) throws Exception {
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY, factory);
p.put(Context.PROVIDER_URL, url);
return new InitialContext(p).lookup(name);
}
}
// 使用
Object ref = EJBLookupHelper.lookup(
"com.ibm.websphere.naming.WsnInitialContextFactory",
"iiop://was-host:2809",
"ejb/MyEJBHome");
MyHome home = (MyHome) PortableRemoteObject.narrow(ref, MyHome.class);
JNDI 查找看似枯燥,實則是分佈式 Java 應用的「導航系統」。掌握命名空間拓撲、初始上下文語義、Stub/Tie 部署、產品差異四大維度,才能從「本地跑通」走向「生產穩健」。下次遇到 NameNotFoundException,請先打開 JNDI 瀏覽器——真相往往在樹結構裡。