JNDI


JNDI基礎
一 簡介
1.JNDI:Java Naming and Directory Interface,即Java命名和目錄接口。JNDI包含了一些標准API接口,Java程序可以通過這些接口來訪問命名目錄服務。JNDI不依賴於任何獨立的命名目錄服務器,不管采用哪種命名目錄服務器,應用程序都可以通過統一的JNDI接口來調用。要使用JNDI,必須要安裝jdk 1.3以上版本。
2.命名服務:就是將名字和計算機系統內的一個對象建立關聯,從而允許應用程序通過該名字訪問該對象。簡而言之,命名服務就是為計算機系統內的對象起名字。
例如在Internet上的域名服務 (domain naming service,DNS) 就是提供將域名映射到IP地址的命名服務,在打開網站時一般都是 在瀏覽器中輸入名字,通過DNS找到相應的IP地址,然后打開。所有的因特網通信都使用TCP、UDP或IP協議。IP地址由4個字節32位二進制數字組成,數字和名字相比,對於人來說名字比數字要容易記憶,但對於計算機來講,它更善於處理數字。
其實所有的命名服務都提供DNS這種基本功能,即一個系統向命名服務注冊,命名服務提供一個值到另一個值的映射。然后,另外一個系統訪問命名服務就可以取得映射信息。這種交互關系對分布式企業級應用來講顯得非常重要。
3.目錄服務:目錄服務是命名服務的拓展,目錄服務不僅需要保存名稱和對象的關聯,還要保存對象的各種屬性,這樣就允許開發者操作對象的屬性,包括增刪改查對象的屬性。在目錄服務中,你可以根據屬性搜索對象。JNDI允許你訪問文件系統中的文件,定位遠程RMI注冊的對象,訪問象LDAP這樣的目錄服務,定位網絡上的EJB組件。從我們日常生活中去理解目錄服務的概念可以從電話簿說起,電話簿本身就是一個比較典型的目錄服務,如果你要找到某個人的電話號碼,你需要從電話簿里找到這個人的名稱,然后再看其電話號碼。
4.JNDI結構

 

JNDI結構包括JNDI API和JNDI SPI
開發者通過JNDI API以一致的方式來訪問各種命名服務、目錄服務,而JNDI API則保證各種命名服務、目錄服務透明的加入JNDI結構中,Naming Manager則負責管理二者之間的轉換。。
在開發企業級應用時,JNDI顯得尤其重要:客戶端代碼可以通過JNDI來訪問EJB,客戶端代碼需要通過JNDI來訪問容器管理的數據源......Java EE應用中所有遠程對象都需要通過JNDI來訪問。
二 使用JNDI配置數據源
1.數據源的由來
在Java開發中,使用JDBC操作數據庫的四個步驟如下:
加載數據庫驅動程序(Class.forName("數據庫驅動類");)
連接數據庫(Connection con  = DriverManager.getConnection();)
操作數據庫(PreparedStatement stat = con.prepareStatement(sql);stat.executeQuery();)
關閉數據庫,釋放連接(con.close();)
也就是說,所有的用戶都需要經過此四步進行操作,但是這四步之中有三步(加載數據庫驅動程序、連接數據庫、關閉數據庫,釋放連接)對所有人都是一樣的,而所有人只有在操作數據庫上是不一樣,那么這就造成了性能的損耗。
那么最好的做法是,准備出一個空間,此空間里專門保存着全部的數據庫連接,以后用戶用數據庫操作的時候不用再重新加載驅動、連接數據庫之類的,而直接從此空間中取走連接,關閉的時候直接把連接放回到此空間之中。
那么此空間就可以稱為連接池(保存所有的數據庫連接),但是如果要想實現此空間的話,則必須有一個問題要考慮?
如果沒有任何一個用戶使用連接,那么那么應該維持一定數量的連接,等待用戶使用。
如果連接已經滿了,則必須打開新的連接,供更多用戶使用。
如果一個服務器就只能有100個連接,那么如果有第101個人過來呢?應該等待其他用戶釋放連接
如果一個用戶等待時間太長了,則應該告訴用戶,操作是失敗的。
直接用程序實現以上功能,則會比較麻煩,所以在Tomcat 4.1.27之后,在服務器上就直接增加了數據源的配置選項, 直接在服務器上配置好數據源連接池即可。在J2EE服務器上保存着一個數據庫的多個連接。每一個連接通過DataSource可以找到。DataSource被綁定在了JNDI樹上(為每一個DataSource提供一個名字)客戶端通過名稱找到在JNDI樹上綁定的DataSource,再由DataSource找到一個連接。如下圖所示:

 

那么在以后的操作中,除了數據庫的連接方式不一樣之外,其他的所有操作都一樣,只是關閉的時候不是徹底地關閉數據庫,而是把數據庫的連接放回到連接池中去。
2.在tomcat中配置JNDI
主要包括三種方式:web.xml    context.xml    server.xml
這三種的區別是,server.xml與context.xml類似都是所有應用通用的,但是context.xml只是把它分離出來單獨形成了一個文件而已。在WEB-INF/下的context.xml則是應用自己的,所以如果不想把某些信息公開,放在這里就可以了。
(1)server.xml方式
這種方式設置的是全局JNDI配置,在server.xml下配置你必需重啟服務器才能生效,而context.xml配置保存后tomcat會自動加載無需重啟。
Step1:在tomcat服務器的lib目錄下加入數據庫連接的驅動jar包,如
mysql-connector-java-5.1.27.jar  -->mySql數據庫的jar包
ojdbc14.jar   -->Oracle數據庫的jar包
sqljdbc4.jar   -->SQLServer數據庫的jar包
Step2:
(1)server.xml方式
修改tomcat服務器的conf目錄下的server.xml配置文件
在server.xml配置文件中有一個自帶的全局JNDI配置,如圖:

 

在server.xml中添加全局JNDI數據源配置,在<GlobalNamingResources>下繼續添加<Resource>標簽,常見的幾個數據庫的配置如下所示:
<!--配置Oracle數據庫的JNDI數據源-->
<Resource
        name="jdbc/oracle"
        auth="Container"
        type="javax.sql.DataSource"
        maxActive="100"
        maxIdle="30"
        maxWait="10000"
        username="lead_oams"
        password="p"
        driverClassName="oracle.jdbc.driver.OracleDriver"
        url="jdbc:oracle:thin:@192.168.1.229:1521:lead"/>
 
<!--配置MySQL數據庫的JNDI數據源-->
<Resource
        name="jdbc/mysql"
        auth="Container"
        type="javax.sql.DataSource"
        maxActive="100"
        maxIdle="30"
        maxWait="10000"
        username="root"
        password="root"
        driverClassName="com.mysql.jdbc.Driver"
        url="jdbc:mysql://192.168.1.144:3306/leadtest?useUnicode=true&amp;characterEncoding=utf-8"/>
 
<!--配置SQLServer數據庫的JNDI數據源-->
<Resource
        name="jdbc/sqlserver"
        auth="Container"
        type="javax.sql.DataSource"
        maxActive="100"
        maxIdle="30"
        maxWait="10000"
        username="sa"
        password="p@ssw0rd"
        driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"
        url="jdbc:sqlserver://192.168.1.51:1433;DatabaseName=demo"/>
其中,name:表示以后要查找的名稱。通過此名稱可以找到DataSource,此名稱任意更換,但是程序中最終要查找的就是此名稱,為了不與其他的名稱混淆,所以使用jdbc/oracle,現在配置的是一個jdbc的關於oracle的命名服務。
auth:由容器進行授權及管理,指的用戶名和密碼是否可以在容器上生效
type:此名稱所代表的類型,現在為javax.sql.DataSource
maxActive:表示一個數據庫在此服務器上所能打開的最大連接數
maxIdle:表示一個數據庫在此服務器上維持的最小連接數
maxWait:最大等待時間。10000毫秒
(2)context.xml方式
選擇你所要連接的數據庫,打開comcat下的config里的context.xml,將<resource>下的內容復制過去
<Context>
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <Resource
        name="jdbc/mysql"
        auth="Container"
        driverClassName="com.mysql.jdbc.Driver"
        type="javax.sql.DataSource"
        maxActive="100"
        maxIdle="30"
        maxWait="10000"
        username="root"
        password="123456"     
        url="jdbc:mysql://localhost:3306/mydb?useUnicode=true&amp;characterEncoding=utf-8"/>
</Context>
Step3:測試
在項目的WEB-INF的web.xml文件中添加JNDI配置的資源引用:
 <!--Oracle數據庫JNDI數據源引用 -->
  <resource-ref>
      <description>Oracle DB Connection</description>
      <res-ref-name>oracleDataSource</res-ref-name>
      <res-type>javax.sql.DataSource</res-type>
      <res-auth>Container</res-auth>
 </resource-ref>
 
  <!--MySQL數據庫JNDI數據源引用 -->
  <resource-ref>
      <description>MySQL DB Connection</description>
      <res-ref-name>mysqlDataSource</res-ref-name>
      <res-type>javax.sql.DataSource</res-type>
      <res-auth>Container</res-auth>
  </resource-ref>
 
  <!--SQLServer數據庫JNDI數據源引用 -->
  <resource-ref>
      <description>SQLServer DB Connection</description>
      <res-ref-name>sqlserverDataSource</res-ref-name>
      <res-type>javax.sql.DataSource</res-type>
      <res-auth>Container</res-auth>
  </resource-ref>
JNDI配置的資源引用包括:
res-ref-name:表示引用資源的名稱
res-type:此資源對應的類型為javax.sql.DataSource
res-auth:容器授權管理
(2)編寫測試內容
<%@ page import="java.sql.*,javax.sql.*,javax.naming.*"  %>
<%
  Connection conn=null; 
  try
  {
   //初始化查找命名空間
   Context ctx = new InitialContext();
   //InitialContext ctx = new InitialContext();亦可
   //找到DataSource,對名稱進行定位java:comp/env是必須加的,后面跟你的DataSource名
   DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/mysql");
   //取出連接
   conn = ds.getConnection();
   System.out.println("connection pool connected !!");   
  } catch (NamingException e) {
   System.out.println(e.getMessage());
  } catch (SQLException e) {
   e.printStackTrace();
  }finally { 
      if(conn!=null) { 
          try { 
          conn.close(); 
          } catch(SQLException e) { } 
    } 
  }
%>
報錯:org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot create JDBC driver of class '' for connect URL 'null'
三 JNDI常用操作
1.JNDI架構提供了一組標准命名系統的API,這些API在JDK1.3之前是作為一個單獨的擴展包jndi.jar(通過這個地址下載),這個基礎API構建在與SPI之上。這個API提供如下五個包javax.naming,javax.naming.directory,javax.naming.event,javax.naming.ldap,javax.naming.spi
2.通過JNDI來訪問被綁定對象要按一下操作:
(1)創建Context對象
(2)調用Context的lookup方法根據JNDI名稱查找被綁定對象;或者調用bind方法來執行綁定;或者調用unbind方法來解除綁定...就是調用Context的方法來執行綁定、查找等操作。
(3)關閉Context
3.Context只是一個接口,通常會使用它的實現類InitialContext來創建實例。InitialContext提供了兩個構造器:
InitialContext():讀取系統屬性作為Context屬性來創建InitialContext
InitialContext(Hashtable<?,?> environment):以environment參數指定的屬性作為Context屬性來創建InitialContext
如果創建InitialContext對象時沒有傳入任何參數,那么它必須能從系統屬性(System.getProperties()方法返回值)中讀到合適的Context屬性來執行初始化,否則它將拋出NoInitialContextException異常。
在JSP頁面中執行以下代碼:
<%
  Properties props=System.getProperties();
    for(String name:props.stringPropertyNames()){
        out.println(name+"--->"+props.getProperty(name)+"<br/>");
    }
%>
會看到如下的結果:
由此可見,當在服務器環境下的Web應用中創建InitialContext時,由於服務器啟動時已經添加了它所需的系統屬性,因此直接創建InitialContext()就可以了。
Hashtable至少包含如下兩個key:
java.naming.factory.initial:可用Context內的INITIA_CONTEXT_FACTORY常量代替,該key的值應該為初始化Context的工廠類。
java.naming.providor.url:可用Context內的PROVIDER_URL常量代替,該key的值應該為Context服務提供者的URL
4.舉例
以文件系統的JNDI為例,創建如下InitialContext對象:
public class myTest {
    public static void main(String[] args) throws NamingException {
        final String fileName="lyy.doc";
        Hashtable env=new Hashtable();
        env.put(Context.INITIAL_CONTEXT_FACTORY,"org.apache.naming.java.javaURLContextFactory");
        env.put(Context.PROVIDER_URL, "file:/d:/wscite");
        Context ctx=new InitialContext(env);
        Object file=ctx.lookup(fileName);
        System.out.println(fileName+"名稱被綁定到:"+file);
        ctx.close();
    }
}
報錯:java.lang.NoClassDefFoundError: org/apache/juli/logging/LogFactory
打開window->Preferences->你所用的jdk版本->edit->add external jars->選擇tomact包里的bin的tomcat-juli.jar->添加
上面的程序將d:/wscite目錄作為一個Context,然后以此Context來查找對應的名稱。對於文件系統的JNDI來說,每個文件夾相當於一個Context,每個文件名相當於一個JNDI名,而文件對象則是實際被綁定的對象。
方法:
(1)查找對象
通過Context提供的lookup(jndi)方法來實現,該方法接受被綁定的JNDI名,返回與之綁定的對象
該方法只能返回一個Object類型的對象,因此要注意強制類型轉換
(2)綁定
JNDI通過Context的bind(String name,Object obj)方法來執行綁定,第一個參數是被綁定的JNDI名,第二個參數是被綁定的對象
該方法就相當於為obj對象起了一個name
(3)重新綁定
rebind(String name,Object obj),如果該名稱已被綁定,則是修改,如果沒綁定就與bind功能相同。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM