测试开发进阶——常用中间件概念——JDNI——连接数据库理解

JNDI的基本应用
        JNDI是Java Naming and Directory Interface(JAVA命名和目录接口)的英文简写,它是为JAVA应用程序提供命名和目录访问服务的API(Application Programing Interface,应用程序编程接口)。

1.命名的概念与应用


        JNDI中的命名(Naming),就是将Java对象以某个名称的形式绑定(binding)到一个容器环境(Context)中,以后调用容器环境(Context)的查找(lookup)方法又可以查找出某个名称所绑定的Java对象。读者也许会感到奇怪:自己创建一个Java对象,将其绑定到JNDI容器环境中后又查询出来,这有什么意思?

       在真实的项目应用中,通常是由系统程序或框加程序先将资源对象绑定到JNDI环境中,以后在该系统或框架中运行的模块程序就可以从JNDI环境中查找这些资源对象了。例如,Tomcat服务器在启动时可以创建一个连接到某种数据库系统的数据源(DataSource)对象,并将该数据源(DataSource)对象绑定到JNDI环境中,以后在这个Tomcat服务器中运行的Servlet和JSP程序就可以从JNDI环境中查询出这个数据源(DataSource)对象进行使用,而不用关心数据源(DataSource)对象是如何创建出来的,这种方式极大地增强了系统的可维护性,当数据库系统的连接参数发生变更时,这只是Tomcat系统管理员一个人要关心的事情,而与所有的应用程序开发人员无关。


        容器环境(Context)本身也是一个Java对象,它也可以通过一个名称绑定到另一个容器环境(Context)中。将一个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树中的一个Java对象,首先要得到其所在的Context对象,只要得到了一个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来选择。

================================================

JNDI是为了一个最最核心的问题:是为了解耦,是为了开发出更加可维护、可扩展的系统

JNDI和JDBC起的作用类似:

JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。

JDBC为工具/数据库开发人员提供了一个标准的API,据此可以构建更高级的工具和接口,使数据库开发人员能够用纯 Java API 编写数据库应用程序。

JNDI(Java Naming and Directory Interface)是一个应用程序设计的API,为开发人员提供了查找和访问各种命名和目录服务的通用、统一的接口,类似JDBC都是构建在抽象层上。


JNDI是 Java 命名与目录接口(Java Naming and Directory Interface),在J2EE规范中是重要的规范之一,不少专家认为,没有透彻理解JNDI的意义和作用,就没有真正掌握J2EE特别是EJB的知识。 

那么,JNDI到底起什么作用?//带着问题看文章是最有效的 

要了解JNDI的作用,我们可以从“如果不用JNDI我们怎样做?用了JNDI后我们又将怎样做?”这个问题来探讨。 

没有JNDI的做法: 

程序员开发时,知道要开发访问MySQL数据库的应用,于是将一个对 MySQL JDBC 驱动程序类的引用进行了编码,并通过使用适当的 JDBC URL 连接到数据库。 

就像以下代码这样:

复制代码
        Connection con = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
//            con = DriverManager.getConnection("jdbc:mysql://localhost:3306/database", "user", "password");
            con = DriverManager.getConnection("jdbc:mysql://localhost:3306/database?user=user&password=password");
            stmt = con.createStatement();
复制代码

这是传统的做法,也是以前非Java程序员(如Delphi、VB等)常见的做法。这种做法一般在小规模的开发过程中不会产生问题,只要程序员熟悉Java语言、了解JDBC技术和MySQL,可以很快开发出相应的应用程序。 


没有JNDI的做法存在的问题: 
1、数据库服务器名称MyDBServer 、用户名和口令都可能需要改变,由此引发JDBC URL需要修改; 
2、数据库可能改用别的产品,如改用DB2或者Oracle,引发JDBC驱动程序包和类名需要修改; 
3、随着实际使用终端的增加,原配置的连接池参数可能需要调整; 
4、...... 


解决办法: 

程序员应该不需要关心“具体的数据库后台是什么?JDBC驱动程序是什么?JDBC URL格式是什么?访问数据库的用户名和口令是什么?”等等这些问题,程序员编写的程序应该没有对 JDBC 驱动程序的引用,没有服务器名称,没有用户名称或口令 —— 甚至没有数据库池或连接管理。而是把这些问题交给J2EE容器来配置和管理,程序员只需要对这些配置和管理进行引用即可。 

由此,就有了JNDI。 

//看的出来,是为了一个最最核心的问题:是为了解耦,是为了开发出更加可维护、可扩展//的系统 

用了JNDI之后的做法: 

首先,在在J2EE容器中配置JNDI参数,定义一个数据源,也就是JDBC引用参数,给这个数据源设置一个名称;然后,在程序中,通过数据源名称引用数据源从而访问后台数据库。 

//红色的字可以看出,JNDI是由j2ee容器提供的功能 

在描述JNDI,例如获得数据源时,JNDI地址 有两种写法,例如同是 jdbc/testDS 数据源: A: java:comp/env/jdbc/testDS B: jdbc/testDS

这两种写法,配置的方式也不尽相同,第一种方法应该算是一种利于程序移植或迁移的方法,它的实现与“映射”的概念相 同,而B方法,则是一个硬引用。

java:comp/env 是环境命名上下文(environment naming context(ENC)),是在EJB规范1.1以后引入的,引入这个是为了解决原来JNDI查找所引起的冲突问题,也是为了提高EJB或者J2EE应 用的移植性。 在J2EE中的引用常用的有:        
  

      JDBC 数据源引用在java:comp/env/jdbc 子上下文中声明        
      JMS 连接工厂在java:comp/env/jms 子上下文中声明        
      JavaMail 连接工厂在java:comp/env/mail 子上下文中声明        
      URL 连接工厂在 java:comp/env/url子上下文中声明


java:comp/env和JNDI是不同的,很多人都有一些混淆,甚至认为这个就是JNDI名称。

其实,java:comp/env 是环境命名上下文(environment naming context(ENC),是在EJB规范1.1以后引入的,引入这个是为了解决原来JNDI查找所引起的冲突问题。

比如你要把一个EJB的Jar包部署到两台Server,而这两台Server共享一台JNDI名字空间,此时问题就出来了,因为JNDI名字空间要求JNDI名字必须唯一。

使用ENC查找,将可以避免这个冲突,EJB或者J2EE应用的移植性也提高了。 ENC是一个引用,引用是用于定位企业应用程序的外部资源的逻辑名。

引用是在应用程序部署描述符文件中定义的。在部署时,引用被绑定到目标可操作环境中资源的物理位置( JNDI 名)。

使用ENC是把对其它资源的JNDI查找的硬编码解脱出来,通过配置这个引用可以在不修改代码的情况下,将引用指向不同的EJB(JNDI)。



可以通过下面的结构示意来发现这两种描述的不同之处:


A:       java:comp/env/jdbc/testDS(虚地址)   ------>    映射描述符   ------>        jdbc/testDS (实际的地址)


B:       jdbc/testDS (实际的地址) 从这种结构上来看,A的确是便于移植的。

再来看一个例子: 假如你需要获取datasource,例如:dataSource = (DataSource) ctx.lookup("java:comp/env/jdbc/testDS"); 那么在配置文件中进行资源映射时,在web.xml中,

     <resource-ref>
        <res-ref-name>jdbc/testDS</res-ref-name>
        <res-type>javax.sql.DataSource</res-type>
        <res-auth>Container</res-auth>
      </resource-ref>

在相应的资源配置xml中(不同的应用服务器均不同,WSAD中,可以进行可视化的设置),

    <reference-descriptor>
      <resource-description>
        <res-ref-name>jdbc/DBPool</res-ref-name>
        <jndi-name>OraDataSource</jndi-name>
      </resource-description>
    </reference-descriptor>

Tomcat5.5h server.xml中加入:

复制代码
<Context
            docBase="D:/workspace/Hello/WebContent"
            path="/Hello"
            reloadable="true">
          <Resource
            name="jdbc/DBPool"
            type="javax.sql.DataSource"
            maxActive="100"
            maxIdle="10"
            maxWait="3000"
            driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"
            url="jdbc:sqlserver://192.168.1.37:1433;DatabaseName=xxx;user=sa;password=sa123"/>
</Context>
复制代码

实际服务器中的JNDI名字是OraDataSource,逻辑名jdbc/DBPool只是用来和它作映射的,这样做的好处是为了提高可移植性,移植的 时候只需要把配置文件改一下就可以,而应用程序可不用改动。

java:comp/env是标准的J2EE环境查找规则使用这种方式必须做一次环境名到JNDI名的映射这种隔离使得在写程序时不必关注真正的 JNDI名字其实说白了跟把JNDI名放到配置文件里是一样的用法,如把java:comp/env/my/datasource映射到 my.ora.dataource

补充一下不加的时候是全局的JNDI名,这样将造成应用间EJB的耦合太高,不建议使用

注:

java:comp/env/   前面是固定的  

java:comp/env是标准的J2EE环境查找规则  

comp是company的缩写

env是environment的缩写

使用这种方式必须做一次环境名到JNDI名的映射 这种隔离使得在写程序时不必关注真正的JNDI名字 其实说白了跟把JNDI名放到配置文件里是一样的  

其他扩展:

J2EE 1.3 ,资源的管理绑定一个资源,但是使用时应该先配置资源引用 。 你在 web.xml 中或者 ejb-jar.xml 上配置对 EJB 或者 DataSource 的引用才能使用相应的资源。 不管是资源的配置还是资源引用的配置都可以在布署的阶段来修改的, 但是程序可以不用改,你只要让引用不变就行了,因为你自己容器中将要放多少东西你写代码时就知道(就是一个项目要用的东西),但是你的服务器中将来要放多 少资源你写代码时是不知道的,因为资源是在整个服务器,很容易在将来的某个时候可能多得不可管理。

在 web.xml 和 ejb-jar.xml 都可以有个 mycompay/abc 这个资源引用,名字相同没关系,因为它们在不同的容器中,只要同一个容器中唯一就行了,资源引用与实际资源的JNDI 相同也没关系。

现在的应用服务器都支持 J2EE 1.3,都会有个把 资源引用对应到一个实际的资源的这么一个配置文件。 像 IBM WebSphere 在 web 项目中 /WEB-INF/ibm-web-bnd.xml 就是用来将一个资源引用绑定到实际的 JNDI 资源上去的, 而EJB 项目中是 ibm-ejb-jar-bnd.xml 。 我用 &Web&Sphere &Application &Developer 开发的时候, 在 web.xml 中添加一个资源引用,比如对数据源的引用 ,WSAD 会自动到 ibm-web-bnd.xml中添加一个相应的绑定条目,如果我在 ejb-jar.xml 中添加一个 Local Ejb Ref , WSAD 也会自动到 ibm-ejb-jar-bnd.xml 中添加一个相应的条目。  

假如你写了一个EJB,获取datasource如:dataSource = (DataSource) ctx.lookup("java:comp/env/jdbc/DBPool"); 那么在配置文件中进行资源映射时,在ejb-jar.xml中,

      <resource-ref> 
        <res-ref-name>jdbc/DBPool</res-ref-name> 
        <res-type>javax.sql.DataSource</res-type> 
        <res-auth>Container</res-auth> 
      </resource-ref> 

在weblogic-ejb-jar.xml中,

    <reference-descriptor> 
      <resource-description> 
        <res-ref-name>jdbc/DBPool</res-ref-name> 
        <jndi-name>OraDataSource</jndi-name> 
      </resource-description> 
    </reference-descriptor> 

//转者注:如果是在jboss则在jboss.xml中做如下修改

    <resource-managers> 
        <resource-manager> 
            <res-name>jdbc/DBPool</res-name> 
            <res-jndi-name>OraDataSource</res-jndi-name> 
        </resource-manager> 
    </resource-managers> 

实际服务器中的JNDI名字是OraDataSource,逻辑名jdbc/DBPool只是用来和它作映射的,这样做的好处是为了提高可移植性,移植的 时候只需要把配置文件改一下就可以,而应用程序可不用改动。

假如你写了一个一般的应用程序,想直接通过JNDI来获取数据源,那么直接lookup(“mytest”)就可以了(假如服务器上的JNDI为 mytest),用第一种写法反而会报错的。

http://blog.csdn.net/tony8829/article/details/7252651



具体操作如下(以JBoss为例): 

1、配置数据源 
在JBoss 的 D:jboss420GAdocsexamplesjca 文件夹下面,有很多不同数据库引用的数据源定义模板。将其中的 mysql-ds.xml 文件Copy到你使用的服务器下,如 D:jboss420GAserverdefaultdeploy。 

修改 mysql-ds.xml 文件的内容,使之能通过JDBC正确访问你的MySQL数据库,如下: 

复制代码
<?xml version="1.0" encoding="UTF-8"?>  
<datasources>  
<local-tx-datasource>  
    <jndi-name>MySqlDS</jndi-name>  
    <connection-url>jdbc:mysql://localhost:3306/lw</connection-url>  
    <driver-class>com.mysql.jdbc.Driver</driver-class>  
    <user-name>root</user-name>  
    <password>rootpassword</password>  
<exception-sorter-class-name>  
org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter  
</exception-sorter-class-name>  
    <metadata>  
       <type-mapping>mySQL</type-mapping>  
    </metadata>  
</local-tx-datasource>  
</datasources>
复制代码

这里,定义了一个名为MySqlDS的数据源,其参数包括JDBC的URL,驱动类名,用户名及密码等。 

2、在程序中引用数据源:

复制代码
Connection conn=null;  
try {  
  Context ctx=new InitialContext();  
  Object datasourceRef=ctx.lookup("java:MySqlDS"); //引用数据源  
  DataSource ds=(Datasource)datasourceRef;  
  conn=ds.getConnection();  
  ......  
  c.close();  
} catch(Exception e) {  
  e.printStackTrace();  
} finally {  
  if(conn!=null) {  
    try {  
      conn.close();  
    } catch(SQLException e) { }  
  }  
} 
复制代码

直接使用JDBC或者通过JNDI引用数据源的编程代码量相差无几,但是现在的程序可以不用关心具体JDBC参数了。//解藕了,可扩展了 

在系统部署后,如果数据库的相关参数变更,只需要重新配置 mysql-ds.xml 修改其中的JDBC参数,只要保证数据源的名称不变,那么程序源代码就无需修改。 

由此可见,JNDI避免了程序与数据库之间的紧耦合,使应用更加易于配置、易于部署。 

JNDI的扩展: 

JNDI在满足了数据源配置的要求的基础上,还进一步扩充了作用:所有与系统外部的资源的引用,都可以通过JNDI定义和引用。 

//注意什么叫资源 

所以,在J2EE规范中,J2EE 中的资源并不局限于 JDBC 数据源。引用的类型有很多,其中包括资源引用(已经讨论过)、环境实体和 EJB 引用。特别是 EJB 引用,它暴露了 JNDI 在 J2EE 中的另外一项关键角色:查找其他应用程序组件。 

EJB 的 JNDI 引用非常类似于 JDBC 资源的引用。在服务趋于转换的环境中,这是一种很有效的方法。可以对应用程序架构中所得到的所有组件进行这类配置管理,从 EJB 组件到 JMS 队列和主题,再到简单配置字符串或其他对象,这可以降低随时间的推移服务变更所产生的维护成本,同时还可以简化部署,减少集成工作。外部资源”。 


总结: 

J2EE 规范要求所有 J2EE 容器都要提供 JNDI 规范的实现。//sun 果然喜欢制定规范JNDI 在 J2EE 中的角色就是“交换机” —— J2EE 组件在运行时间接地查找其他组件、资源或服务

的通用机制。在多数情况下,提供 JNDI 供应者的容器可以充当有限的数据存储,这样管理员就可以设置应用程序的执行属性,并让其他应用程序引用这些属性(Java 管理扩展

(Java Management Extensions,JMX)也可以用作这个目的)。JNDI 在 J2EE 应用程序中的主要角色就是提供间接层,这样组件就可以发现所需要的资源,而不用了解这些间接性。 

在 J2EE 中,JNDI 是把 J2EE 应用程序合在一起的粘合剂,JNDI 提供的间接寻址允许跨企业交付可伸缩的、功能强大且很灵活的应用程序。这是 J2EE 的承诺,而且经过一些计划和

预先考虑,这个承诺是完全可以实现的。 


从上面的文章中可以看出: 


1、JNDI 提出的目的是为了解藕,是为了开发更加容易维护,容易扩展,容易部署的应用。 
2、JNDI 是一个sun提出的一个规范(类似于jdbc),具体的实现是各个j2ee容器提供商,sun   只是要求,j2ee容器必须有JNDI这样的功能。 
3、JNDI 在j2ee系统中的角色是“交换机”,是J2EE组件在运行时间接地查找其他组件、资源或服务的通用机制。 
4、JNDI 是通过资源的名字来查找的,资源的名字在整个j2ee应用中(j2ee容器中)是唯一的。 

===================================================

原文地址:https://www.cnblogs.com/xiaobaibailongma/p/15328797.html