首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

JNDI 核心原理與 J2EE/J2SE 環境下完整實戰指南 (上)

     編輯:Java 綜合教學

前言:為什麼要理解 JNDI?

Java Naming and Directory Interface (JNDI) 是 J2EE 標準核心服務之一,所有合規容器 (Tomcat、JBoss、WebLogic 等) 必須提供實現。雖然 Spring 等現代框架封裝了底層細節,但深入理解 JNDI 有助於:排查數據源注入失敗、實現跨應用資源共享、掌握分布式命名服務本質。本文分上下兩篇,系統梳理 JNDI 核心概念、J2EE 容器環境與 J2SE 獨立環境下的完整使用實踐。

一、JNDI 核心模型:本質是「分布式哈希表」

1.1 兩大核心操作

  • bind/rebind (綁定):將對象以「名稱/鍵」存入命名上下文,rebind 允許覆蓋
  • lookup (查找):根據名稱從上下文獲取對象引用

1.2 命名空間結構:目錄樹模型

根上下文
├── java:comp/env           # 組件環境命名上下文 (J2EE 標準)
│   ├── jdbc/
│   │   └── MyDataSource    # 數據源綁定位置
│   ├── jms/
│   │   └── QueueFactory
│   └── url/
│       └── MyURL
├── ejb/                    # EJB 遠程引用
└── custom/                 # 自定義命名空間

對象屬性以「子目錄/文件」形式存儲:如汽車對象 → 根目錄;輪子/引擎 → 子目錄;品牌/重量 → 文件屬性。

1.3 JNDI vs IoC vs COM/CORBA

維度JNDIIoC (Spring)COM/CORBA
核心目的分布式對象查找/命名服務依賴注入/控制反轉 (解耦)跨語言進程通訊/組件模型
對象獲取方式主動查找容器注入 (被動)註冊表/命名服務查找
接口要求Serializable/ReferenceablePOJO 無侵入IDL 定義接口
典型場景數據源、JMS、EJB、環境變量業務組件裝配、AOP 代理異構系統集成、歷史系統
並行開發支持弱 (運行期綁定)強 (接口契約驅動)中 (接口契約)

核心差異:JNDI 解決「誰提供對象、誰在哪裡找到對象」的運行期發現問題;IoC 解決「誰依賴誰、如何裝配」的設計期耦合問題。兩者互補而非替代。

二、J2EE 容器環境實戰:Tomcat + Servlet/JSP 演示

2.1 項目結構

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+ 已內置,通常不需額外引入)

2.2 綁定對象 Servlet

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);
    }
}

2.3 查找對象 Servlet

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);
    }
}

2.4 web.xml 配置

<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>

2.5 JSP 頁面 (關鍵片段)


<a href="bind.do">綁定對象</a> | <a href="lookup.do">查找對象</a>


<%@ page contentType="text/html;charset=GB18030" %>
<% Object o = request.getAttribute("found_jndi_obj"); %>
查找結果: <%= o %>

三、J2EE 環境關鍵約束與最佳實踐

📋 必須遵守的規範

  1. 對象必須實現 Serializable:容器間傳遞/集群同步需要序列化
  2. 使用 java:comp/env/ 前綴:J2EE 標準環境命名上下文,確保可移植性
  3. 優先用 rebind:避免重複綁定拋出異常
  4. 資源釋放:用完調用 ctx.close() (雖然容器管理上下文生命周期)

3.1 Tomcat 數據源配置實戰 (最常見場景)

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 獨立環境實戰預告 (下篇)

J2SE 環境無容器提供 JNDI 服務,需自行集成實現。下篇將詳細講解:

  • Sun FSContext 文件系統實現方案 (fscontext.jar + providerutil.jar)
  • JBoss JNP 獨立命名服務部署
  • 自定義對象實現 Referenceable 接口存儲機制
  • 工廠模式 (ObjectFactory) 實現對象反序列化重建
  • 完整 Bind/Lookup 獨立運行代碼演示

五、總結

JNDI 作為 Java 命名服務標準,核心價值在於 「名稱解析到對象」的解耦。J2EE 容器環境下使用極簡 (類似 Hashtable),關鍵在於遵循 java:comp/env 命名規範與序列化要求。實際開發中,數據源配置是最高頻場景,掌握 Tomcat/JBoss/WebLogic 的資源配置差異至關重要。下篇將攻克 J2SE 環境無容器時的 JNDI 自建難題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved