Java Naming and Directory Interface (JNDI) 是 J2EE 標準核心服務之一,所有合規容器 (Tomcat、JBoss、WebLogic 等) 必須提供實現。雖然 Spring 等現代框架封裝了底層細節,但深入理解 JNDI 有助於:排查數據源注入失敗、實現跨應用資源共享、掌握分布式命名服務本質。本文分上下兩篇,系統梳理 JNDI 核心概念、J2EE 容器環境與 J2SE 獨立環境下的完整使用實踐。
rebind 允許覆蓋根上下文 ├── java:comp/env # 組件環境命名上下文 (J2EE 標準) │ ├── jdbc/ │ │ └── MyDataSource # 數據源綁定位置 │ ├── jms/ │ │ └── QueueFactory │ └── url/ │ └── MyURL ├── ejb/ # EJB 遠程引用 └── custom/ # 自定義命名空間
對象屬性以「子目錄/文件」形式存儲:如汽車對象 → 根目錄;輪子/引擎 → 子目錄;品牌/重量 → 文件屬性。
| 維度 | JNDI | IoC (Spring) | COM/CORBA |
|---|---|---|---|
| 核心目的 | 分布式對象查找/命名服務 | 依賴注入/控制反轉 (解耦) | 跨語言進程通訊/組件模型 |
| 對象獲取方式 | 主動查找 | 容器注入 (被動) | 註冊表/命名服務查找 |
| 接口要求 | Serializable/Referenceable | POJO 無侵入 | IDL 定義接口 |
| 典型場景 | 數據源、JMS、EJB、環境變量 | 業務組件裝配、AOP 代理 | 異構系統集成、歷史系統 |
| 並行開發支持 | 弱 (運行期綁定) | 強 (接口契約驅動) | 中 (接口契約) |
核心差異:JNDI 解決「誰提供對象、誰在哪裡找到對象」的運行期發現問題;IoC 解決「誰依賴誰、如何裝配」的設計期耦合問題。兩者互補而非替代。
jndi_test/ ├── src/ │ └── lld/test/jndi/ │ ├── BindServlet.java │ └── LookupServlet.java ├── WebContent/ │ ├── index.jsp │ ├── bind.jsp │ ├── bind_result.jsp │ ├── lookup.jsp │ └── lookup_result.jsp ├── WEB-INF/ │ ├── web.xml │ └── lib/ │ └── jndi.jar (JDK 6+ 已內置,通常不需額外引入)
package lld.test.jndi;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.Date;
public class BindServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
try {
// 1. 獲取初始上下文 (容器自動提供)
Context ctx = new InitialContext();
// 2. 綁定鍵名 (建議使用 java:comp/env/ 前綴)
String key = "java:comp/env/jndi_object";
// 3. 使用 rebind 避免 NameAlreadyBoundException
ctx.rebind(key, new Date()); // 綁定可序列化對象
} catch (Exception e) {
e.printStackTrace();
}
// 4. 轉發結果頁
getServletContext().getRequestDispatcher("/bind_result.jsp")
.forward(req, resp);
}
}
public class LookupServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
try {
Context ctx = new InitialContext();
String key = "java:comp/env/jndi_object";
// 查找並放入請求屬性供 JSP 顯示
Object obj = ctx.lookup(key);
req.setAttribute("found_jndi_obj", obj);
} catch (Exception e) {
e.printStackTrace();
}
getServletContext().getRequestDispatcher("/lookup_result.jsp")
.forward(req, resp);
}
}
<web-app version="2.4" xmlns="https://java.sun.com/xml/ns/j2ee">
<servlet>
<servlet-name>BindServlet</servlet-name>
<servlet-class>lld.test.jndi.BindServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>BindServlet</servlet-name>
<url-pattern>/bind.do</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>LookupServlet</servlet-name>
<servlet-class>lld.test.jndi.LookupServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LookupServlet</servlet-name>
<url-pattern>/lookup.do</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
<a href="bind.do">綁定對象</a> | <a href="lookup.do">查找對象</a>
<%@ page contentType="text/html;charset=GB18030" %>
<% Object o = request.getAttribute("found_jndi_obj"); %>
查找結果: <%= o %>
ctx.close() (雖然容器管理上下文生命周期)context.xml (全局) 或 META-INF/context.xml (應用級):
<Resource name="jdbc/MyDB"
auth="Container"
type="javax.sql.DataSource"
maxTotal="20"
maxIdle="10"
maxWaitMillis="-1"
username="root"
password="123456"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/mydb?useSSL=false"/>
web.xml 引用聲明:
<resource-ref>
<description>MySQL DataSource</description>
<res-ref-name>jdbc/MyDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
代碼中查找使用:
Context initCtx = new InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
DataSource ds = (DataSource) envCtx.lookup("jdbc/MyDB");
Connection conn = ds.getConnection();
J2SE 環境無容器提供 JNDI 服務,需自行集成實現。下篇將詳細講解:
fscontext.jar + providerutil.jar)Referenceable 接口存儲機制ObjectFactory) 實現對象反序列化重建JNDI 作為 Java 命名服務標準,核心價值在於 「名稱解析到對象」的解耦。J2EE 容器環境下使用極簡 (類似 Hashtable),關鍵在於遵循 java:comp/env 命名規範與序列化要求。實際開發中,數據源配置是最高頻場景,掌握 Tomcat/JBoss/WebLogic 的資源配置差異至關重要。下篇將攻克 J2SE 環境無容器時的 JNDI 自建難題。