JNDI具體用法詳解

JNDI全稱(Java Naming and Directory Interface),是java命名和目錄接口。它是一個應用程序設計的API,為開發人員提供瞭查找和訪問各種命名和目錄服務的通用、統一的接口,類似JDBC都是構建在抽象層上。

1、命名的概念與應用

JNDI中的命名(Naming),就是將Java對象以某個名稱的形式綁定(binding)到一個容器環境(Context)中,以後調用容器環境到JNDI容器環境(Context)的查找(lookup)方法又可以查找出某個名稱所綁定的Java對象。

這樣做的好處在於在真實的項目應用中,通常是由系統程序或框架程序先將資源對象綁定到JNDI環境中,以後在該系統或框架中運行的模塊程序就可以從JNDI環境中查找這些資源對象瞭。比如,Tomcat服務器在啟動時可以創建一個連接到某種數據庫系統的數據源(DataSource)對象,並將該數據源(DataSource)對象綁定到JNDI環境中,以後在這個Tomcat服務器中運行的Servlet和JSP程序就可以從JNDI環境中查詢出這個數據源(DataSource)對象進行使用,而不用關心數據源(DataSource)對象是如何創建出來的,這種方式極大的增強瞭系統的可維護性,這樣,當數據庫系統的連接參數發生變更時,就隻是Tomcat系統管理員一個人要關心的事情,而與所有的應用程序開發人員無關。

容器環境(Context)本身也是一個Java對象,它可以通過一個名稱綁定到另一個容器環境中。將一個Context對象綁定到另一個Context對象中,這就形成瞭一種父子級聯關系,多個Context對象最終可以級聯成一種樹狀結構,樹中的每個Context對象中都可以綁定若幹個Java對象。

上圖中的每個方框分別代表以後個Context對象,它們分別綁定的名稱分別為a、b、c、d、e,其中b和c是a的子Context,d是b的子Context,e又是d的子Context。各個方框內的每個小橢圓分別代表一個Java對象,它們也都有一個綁定的名稱,這些名稱分別為dog、pig、sheet等,在同一個Context不能綁定兩個相同名稱的Java對象,在不同的Context中可以出現同名的綁定對象。可見,Context樹的級聯結構與文件系統中的目錄結構非常類似,Context與其中綁定的Java對象的關系也非常類似於文件系統的目錄與文件的關系。

想要得到一個Context對象,就可以調用它的查詢(lookup)方法來獲得其中綁定的java對象。另外,調用某個Context對象的lookup方法也可以獲得Context樹中的任意一個Context對象,這隻需要在lookup方法中指定相應的Context路徑即可。

在JNDI中不存在著“根”Context的概念,也就是說,執行JNDI操作不是從一個“根”Context對象開始,而是可以從Context樹中的任意以後個Context開始。如論如何程序必須獲得一個作為操作入口的Context對象後才能執行各種JNDI命名操作,為此,JNDI API中提供瞭一個InitialContext類來創建用作JNDI命名操作的入口Context對象。

Context是一個接口,Context對象實際上是Context的某個實現類的實例對象,選擇這個具體的Context實現類並創建其實例對象的過程是由一個Context工廠類來完成的,這個工廠類的類名可以通過JNDI的環境屬性java.naming.factory.initial指定,也可以根據Context的操作方法的url參數的Schema來選擇。

2、目錄的概念與應用

JNDI中的目錄(Directory)與文件系統中的目錄概念有很大的不同,JNDI中的目錄(Directory)是指將一個對象的所有屬性信息保存到一個容器環境中。JNDI的目錄(Directory)原理與JNDI的命令(Naming)原理非常相似,主要的區別在於目錄容器環境中保存的是對象的屬性信息,而不是對象本身,所以,目錄提供的是對屬性的各種操作。事實上,JNDI的目錄(Directory)與命名(Naming)往往是結合在一起使用的,JNDI API中提供的代表目錄容器環境的類為DirContext,DirContext是Context的子類,顯然它除瞭能完成目錄相關操作外,也能完成所有的命令(Naming)操作。DirContext是對Context的擴展,它在Context的基礎上增加瞭對目錄屬性的操作功能,可以在其中綁定對象的屬性信息和查找對象的屬性信息。JNDI中的目錄(Directory)的結構示意圖如下:

上圖中的每個外層的方框分別代表一個DirContext對象,它們綁定的名稱分別為a、b,b是a的子DirContext。圖中的每個小橢圓分別代表一個java對象,各個裡層的方框分別代表一個對象屬性。從名稱為a的DirContext中的內容可以看到,一個DirContext容器環境中即可以綁定對象自身,也可以綁定對象的屬性信息,綁定的對象和綁定的屬性是完全獨立的兩個事物,即使它們的綁定名稱相同,它們的操作也是完全獨立的。另外,一個屬性可以有多個屬性值,例如,dog對象的category屬性就設置瞭兩個屬性值:meat和pet。

從名稱為b的DirContext中的內容可以看到,一個DirContext容器環境中也可以隻綁定對象的屬性信息,而不綁定任何對象自身。與Context的操作原理類似,JNDI API中提供瞭一個InitialDirContext類創建用作JNDI命名與目錄屬性操作的入口DirContext對象。

3、JNDI的用法:創建一個數據源

沒有JNDI時的做法

Connection conn = null;
try{
    Class.forName("com.mysql.jdbc.Driver",true,Thread.currentThread().getContextClassLoader());
    conn = DriverManager.getConnection("jdbc:mysql://MyDBServer?user=xxx&password=xxx");
    .....
    conn.close();
}catch(Exception e){
    e.printStackTrace();
}finally{
    if(conn!=null){
        try{
            conn.close();
        }catch(SQLException e){}
    }
}

這種做法隻適用於小規模開發,在大規模開發中就會存在許多問題,比如:

  • 數據庫服務器名稱MyDBServer、用戶名和口令都可能需要改變,由此引發JDBC URL需要修改。
  • 數據庫可能改用別的產品,引發JDBC驅動程序包和類名需要修改。
  • 隨著實際使用終端的增加,原配置的連接池參數可能需要調整。

在開發過程中,程序員應該不需要關心“具體的數據庫後臺是什麼?JDBC驅動程序是什麼?”等這些問題,程序員編寫的程序應該沒有對JDBC驅動程序的引用,沒有服務器名稱,沒有用戶名稱等,而是把這些問題交給容器來配置和管理,這樣程序員隻需要對這些配置和管理進行引用即可。

使用JNDI

context.xml配置數據庫連接信息:

<?xml version="1.0" encoding="UTF-8"?>
 
<Context>
    <!-- Default set of monitored resources. If one of these changes, the    -->
    <!-- web application will be reloaded.                                   -->
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
 
    <!-- Uncomment this to disable session persistence across Tomcat restarts -->
    <!--<Manager pathname="" />-->
 
    <Resource name="jdbc/mysql" auth="Container"
              type="javax.sql.DataSource" driverClassName="com.mysql.jdbc.Driver"
              url="jdbc:mysql://127.0.0.1:3306/task"
              username="root" password="123456" maxTotal="20" maxIdle="10"
              maxWaitMillis="-1"/>
</Context>

web.xml中引入數據源:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
    <resource-ref>
        <res-ref-name>jdbc/mysql</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
    </resource-ref>
</web-app>

獲取Connection對象,查詢數據庫:

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
 
@WebServlet("/test")
public class Servlet1 extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        try {
            Context initContext = new InitialContext();
            Context envContext = (Context) initContext.lookup("java:/comp/env");
            DataSource ds = (DataSource) envContext.lookup("jdbc/mysql");
            Connection conn = ds.getConnection();
            PreparedStatement ps = conn.prepareStatement("select * from t_role");
            ResultSet rs = ps.executeQuery();
            System.out.println(rs.next());
            rs.close();
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
 
        }
    }
}

到此這篇關於JNDI具體用法詳解的文章就介紹到這瞭,更多相關JNDI 用法內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: