J2EE学习笔记:第三天
2011-06-08 14:48
232 查看
1 java 命名与目录服务
Java 命名和目录接口 Java Naming and Directory Interface JNDI
JNDI最简单的形式就是用来“查找”通过J2EE服务器注册的资源。
JNDI的高级使用支持对java对象和其他信息的复杂的存储和检索。
2 什么是命名和目录服务
命名服务提供了一种为对象命名的机制,这样我们就可以不需要知道对象的具体位置而可以很方便的获取和使用对象,这类似于我们的web访问,我们可以定位到任何网络能够访问到的机器上的对象。
命名服务器要求我们要有正确的环境获取(obtaining a context),只有在正确的环境中才能找到想要的名字。
命名服务 为 分离 “服务提供者” 和 “服务使用者” 提供了一种必不可少的机制。
命名服务 允许 服务提供者 根据 服务的名字 注册 所能提供的服务, 服务使用者 只需要知道服务的名字就可以使用这些服务
常见的命名服务有:
1 域名服务,识别网络中的机器的internet命名服务
2 LDAP 轻量级目录访问协议
3 分布式组件编程 CORBA ,分布式java编程RMI
虽然没有为windows活动目录指定服务的提供者,但是JNDI也支持windows 活动目录,因为windows 活动目录支持LDAP接口,所以可以通过JNDI LDAP服务提供者接口对windows 目录进行访问。
目录服务也可以将名字和对象进行关联,但是通过关联属性给对象提供了额外的信息。目录服务一般情况下提供功能用来查找有某一具体属性或者属性值的记录,所以目录服务是依赖于属性值的联合查找。
3 JNDI
JNDI是 为 java程序中 命名和目录服务 定义接口的java api。它只是一个api而不是一种 命名和目录服务。 如果要使用这个api,那么 命名和目录服务 的 一个实现必须是可用的。
同时 JNDI 为 底层的服务提供者实现提供了一个跨服务的接口。
Java程序 通过 中间的JNDI层接口 和 底层的命名服务交互
可以通过实现JNDI服务提供者接口Service Provider Interface,SPI 向JNDI层插入其他的命名服务(怎么这个概念跟android内的内容提供者那么相似的)
4 每一种命名服务都会有其自身的提供名字的机制。
例如
DNS的命名规则—www.xxx.com
LDAP -- cn =Martin Bond, ou = Authors , o = SAMS, c =us
JNDI提供了创建 和 操作 分层 构造名字的 类,在尽可能少解析的情况下将字符串传递到底层服务。
5 使用JNDI可以带来什么好处
( Reference:解惑 JNDI By Robin On 2011年04月27日· Leave a Comment · In J2EE, 信息世界 | IT )
传统的数据库连接操作需要我们编写下面的代码
Connection conn=null;
Try{
ClassForName( “com.mysql.jdbc.Driver”,
True, Thread.currentThread().getContextClassLoader());
Conn=DriverManager.getConnection(“jdbc:mysql://MyDBServer? User= xx&password=xx”);
…
Conn.close();
}
Catch(Exception e){
e.printStackTrace();
}
Finally{
If( conn!=null)
Try{
Conn.close()
}
Catch(SQLException e){}
}
但是这种传统的作法存在很多问题:
1 一旦修改数据库服务器名称,登录名,密码等,就需要修改JDBC的URL
2 如果使用其他数据库产品,如Sql Server,那么还要修改JDBC的驱动程序包
3 原配置的数据库连接池的参数可能需要调整
如果使用JNDI,那会怎样?
我们需要为我们的J2EE容器定义一个数据源,其中填充JDBC的引用参数,数据源名称等,然后我们就可以利用J2EE容器来通过数据源名称引用数据源从而访问到后台数据库。
具体操作如下(以 JBoss 为例):
1. 配置数据源
将其中的 mysql-ds.xml 文件Copy到你使用的服务器 deploy 目录下,修改 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(); //命名对象所在的环境context
Object dataSourceRef=ctx.lookup(“java:MySqlDS”); //查找对象,引用数据源
DataSource ds= (DataSource)dataSourceRef;
Conn=ds.getConnection();
}
现在程序可以不用关心具体的JDBC参数了,系统部署后,如果参数有变更,只需要重新配置jndi的xml文件,就可以完成数据配置修改。
JNDI 架构提供了一个标准的、与命名系统无关的 API,这个 API 构建在特定于命名系统的驱动程序之上。这一层帮助把应用程序和实际的数据源隔离开来,因此无论应用程序是访问 LDAP、RMI、DNS 还是其他的目录服务,这都没有关系。换句话说,JNDI 与任何特定的目录服务实现无关,您可以使用任何目录,只要您拥有相应的服务提供程序接口(或驱动程序)即可,如下图所示:
注意,关于 JNDI 有一点很重要,即它同时提供应用程序编程接口(Application Programming Interface ,API)和服务提供程序接口(Service Provider Interface ,SPI)。这样做的实际意义在于,对于您的与命名或目录服务交互的应用程序来说,必须存在用于该服务的一个 JNDI 服务提供程序,这便是 JNDI SPI 发挥作用的舞台。一个服务提供程序基本上就是一组类,这些类针对特定的命名和目录服务实现了各种 JNDI 接口(后文会介绍如何实现一个服务提供程序,实现JNDI接口)——这与 JDBC 驱动程序针对特定的数据系统实现各种 JDBC 接口极为相似。作为一名应用程序开发人员,您不需要担心 JNDI SPI。您只需确保,您为每个想使用的命名或目录服务提供了一个服务提供程序。
JNDI的强大功能还远远不止如此,所有与系统外部的资源的引用,都可以通过JNDI定义和引用来完成,甚至是应用程序组件,如EJB。我们可以对应用程序架构中所得到的所有组件进行配置管理,从EJB组件到JMS队列和主题,再到简单配置字符串或者其他对象,这都可以使用JNDI来进行配置管理,从而简化部署和维护。
J2EE 规范要求所有 J2EE 容器都要提供 JNDI 规范的实现。JNDI 在 J2EE 中的角色就是“交换机” —— J2EE 组件在运行时 间接地 查找其他组件、资源或服务的通用机制。
6 JNDI应用实例
JNDI的官方tutorial内介绍一个使用文件系统的例子,该例子使用File System作为命名服务器,通过JNDI引用到本地文件系统中的某个文件
(项目位置 D:\MyEclipse Workspaces\MyEclipse 8.6\JNDI proj)
public static void main(String[] args) {
try {
Hashtable env=new Hashtable();
String name = "F:\\123.txt";
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.fscontext.RefFSContextFactory");
Context c= new InitialContext(env);
Object obj= c.lookup(name);
System.out.println("the name "+name+" is bound to object: " + obj+"\r\n");
/*
JNDI(Java Naming and Directory Interface,Java命名和目录接口)是一组在Java应用中访问命名和目录服务的API。命名服务将名称和对象联系起来,使得我们可以用名称访问对象。目录服务是一种命名服务,在这种服务里,对象不但有名称,还有属性。
*/
File f=(File)obj;
System.out.println(f.getAbsolutePath()+" "+f.getParent());
} catch (NamingException e) {
e.printStackTrace();
}
}
这里我们引用的是F盘内的一个文件,在初始化ContextFacotry时,需要设置相关的jar包,也就是服务提供者的访问接口,我们需要下载相应的JNDI服务器 (下载地址:http://java.sun.com/products/jndi/downloads/index.html)
这里下载的是文件系统服务提供器的JNDI,然后将其中的
包添加到项目的build path中,既可以成功运行。
可以看到其中的代码,我们利用一个Context环境,在其中使用name查找特定的对象,并返回这个对象的实体,也就是说这个实体和这个name是绑定在一起了,然后我们就可以对这个实体做相关的操作,在这里我让实体转型为File类型,然后就可以方便的对file进行操作了。当然访问file并不需要这么困难,但是JDNI的好处就在对于其他外部组件或者资源也可以通过名字查找来获取实体。
7 接下来我们来分析下服务提供者是如何做到提供命名服务的
从6中的链接,我们还可以从oracle官网上下载到一个jndi-1_2_1的压缩包,解压后里面有一个example,其中就包含了如何构建一个服务提供者。
从6中我们知道,要使用某个服务提供者我们先要获取这个服务提供者所提供的context,所以我们在客户端(这里的客户端指使用服务提供者的app,如6中的app)内new了一个Hashtable并填充了一个key/value记录
Env.put(Context. INITIAL_CONTEXT_FACTORY , “com.sun.jndi.fscontext.RefFSContextFactory”);
其中com.sun.jndi.fscontext.RefFSContextFactory是我们下载的fscontext.jar包内的一个RefFSContextFactory类,所以我们自定义的服务提供者也需要有这么一个类
它实现了InitialContextFactory接口内的getInitialContext函数(如下),返回一个Context。
然后客户端调用InitialContext类的构造函数,得到Context对象,Context c= new InitialContext(env);
深入到InitialContext的调用堆栈:
传入的environment就是我们一开始初始化的Hashtable,记录了java.naming.factory.initial / spi.flat.FlatInitCtxFactory这样的名值对。
继续看init(environment)的调用堆栈:
看看getInitialEnvironment的解释
* Given the environment parameter passed to the initial context
* constructor, returns the full environment for that initial
* context (never null). This is based on the environment
* parameter, the applet parameters (where appropriate), the
* system properties, and all application resource files.
*
* <p> This method will modify <tt>env</tt> and save
* a reference to it. The caller may no longer modify it.
我们知道内部对传入的environment做了一些修改并返回了一个完整的“环境”
接下来就是解析这个环境,看看getDefaultInitCtx()的函数实现
这里我们通过NamingManager(命名管理器,见上文JNDI体系结构图:命名管理器是JNDI API和JNDI SPI中间的控制器)和myProps拿到了所要的Context,再看看getInitialContext函数的实现,其中关键代码如下
通过loadClass将className(在这里是我们在HashTable中填入的java.naming.factory.initial)进行解析后初始化一个对应的实例对象factory
这个factory可以看做是我们自己使用我们所定义的FaltInitCtxFactory类的一个实例对象。
(这个FlatInitCtxFactory就是我们在插入HashTable的记录时所指定的value,而key就是前面的java.naming.factory.initial
)
最后利用这个factory实例对象,调用其函数 getInitialContext,返回Context
到这里我们的初始化Context就分析了一半了,接下来就是分析Context的初始化,也就是getInitialContext内的new FlatCtx (env)构造函数的实现。
我们知道JDNI的服务提供者必须暴露接口让上层的JNDI使用者来使用,所以这里FlatCtx来必须实现这些接口(这些接口都是Context基类所定义的)具体的实现方式就是根据服务提供者的命名规则来实现,这里就不在赘述,可以参考源码的FlatCtx类的实现。
最后是服务提供者样例Flat的使用
当然我们也可以像之前的例子那样根据name查找对象,获取对象信息
通过实例和分析,稍微对JNDI有了初步印象后,接下来的一篇文章我们继续分析JNDI的详细内容。
JNDI百度百科.rar
解惑_JNDI.rar
Java 命名和目录接口 Java Naming and Directory Interface JNDI
JNDI最简单的形式就是用来“查找”通过J2EE服务器注册的资源。
JNDI的高级使用支持对java对象和其他信息的复杂的存储和检索。
2 什么是命名和目录服务
命名服务提供了一种为对象命名的机制,这样我们就可以不需要知道对象的具体位置而可以很方便的获取和使用对象,这类似于我们的web访问,我们可以定位到任何网络能够访问到的机器上的对象。
命名服务器要求我们要有正确的环境获取(obtaining a context),只有在正确的环境中才能找到想要的名字。
命名服务 为 分离 “服务提供者” 和 “服务使用者” 提供了一种必不可少的机制。
命名服务 允许 服务提供者 根据 服务的名字 注册 所能提供的服务, 服务使用者 只需要知道服务的名字就可以使用这些服务
常见的命名服务有:
1 域名服务,识别网络中的机器的internet命名服务
2 LDAP 轻量级目录访问协议
3 分布式组件编程 CORBA ,分布式java编程RMI
虽然没有为windows活动目录指定服务的提供者,但是JNDI也支持windows 活动目录,因为windows 活动目录支持LDAP接口,所以可以通过JNDI LDAP服务提供者接口对windows 目录进行访问。
目录服务也可以将名字和对象进行关联,但是通过关联属性给对象提供了额外的信息。目录服务一般情况下提供功能用来查找有某一具体属性或者属性值的记录,所以目录服务是依赖于属性值的联合查找。
3 JNDI
JNDI是 为 java程序中 命名和目录服务 定义接口的java api。它只是一个api而不是一种 命名和目录服务。 如果要使用这个api,那么 命名和目录服务 的 一个实现必须是可用的。
同时 JNDI 为 底层的服务提供者实现提供了一个跨服务的接口。
Java程序 通过 中间的JNDI层接口 和 底层的命名服务交互
可以通过实现JNDI服务提供者接口Service Provider Interface,SPI 向JNDI层插入其他的命名服务(怎么这个概念跟android内的内容提供者那么相似的)
4 每一种命名服务都会有其自身的提供名字的机制。
例如
DNS的命名规则—www.xxx.com
LDAP -- cn =Martin Bond, ou = Authors , o = SAMS, c =us
JNDI提供了创建 和 操作 分层 构造名字的 类,在尽可能少解析的情况下将字符串传递到底层服务。
5 使用JNDI可以带来什么好处
( Reference:解惑 JNDI By Robin On 2011年04月27日· Leave a Comment · In J2EE, 信息世界 | IT )
传统的数据库连接操作需要我们编写下面的代码
Connection conn=null;
Try{
ClassForName( “com.mysql.jdbc.Driver”,
True, Thread.currentThread().getContextClassLoader());
Conn=DriverManager.getConnection(“jdbc:mysql://MyDBServer? User= xx&password=xx”);
…
Conn.close();
}
Catch(Exception e){
e.printStackTrace();
}
Finally{
If( conn!=null)
Try{
Conn.close()
}
Catch(SQLException e){}
}
但是这种传统的作法存在很多问题:
1 一旦修改数据库服务器名称,登录名,密码等,就需要修改JDBC的URL
2 如果使用其他数据库产品,如Sql Server,那么还要修改JDBC的驱动程序包
3 原配置的数据库连接池的参数可能需要调整
如果使用JNDI,那会怎样?
我们需要为我们的J2EE容器定义一个数据源,其中填充JDBC的引用参数,数据源名称等,然后我们就可以利用J2EE容器来通过数据源名称引用数据源从而访问到后台数据库。
具体操作如下(以 JBoss 为例):
1. 配置数据源
将其中的 mysql-ds.xml 文件Copy到你使用的服务器 deploy 目录下,修改 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(); //命名对象所在的环境context
Object dataSourceRef=ctx.lookup(“java:MySqlDS”); //查找对象,引用数据源
DataSource ds= (DataSource)dataSourceRef;
Conn=ds.getConnection();
}
现在程序可以不用关心具体的JDBC参数了,系统部署后,如果参数有变更,只需要重新配置jndi的xml文件,就可以完成数据配置修改。
JNDI 架构提供了一个标准的、与命名系统无关的 API,这个 API 构建在特定于命名系统的驱动程序之上。这一层帮助把应用程序和实际的数据源隔离开来,因此无论应用程序是访问 LDAP、RMI、DNS 还是其他的目录服务,这都没有关系。换句话说,JNDI 与任何特定的目录服务实现无关,您可以使用任何目录,只要您拥有相应的服务提供程序接口(或驱动程序)即可,如下图所示:
注意,关于 JNDI 有一点很重要,即它同时提供应用程序编程接口(Application Programming Interface ,API)和服务提供程序接口(Service Provider Interface ,SPI)。这样做的实际意义在于,对于您的与命名或目录服务交互的应用程序来说,必须存在用于该服务的一个 JNDI 服务提供程序,这便是 JNDI SPI 发挥作用的舞台。一个服务提供程序基本上就是一组类,这些类针对特定的命名和目录服务实现了各种 JNDI 接口(后文会介绍如何实现一个服务提供程序,实现JNDI接口)——这与 JDBC 驱动程序针对特定的数据系统实现各种 JDBC 接口极为相似。作为一名应用程序开发人员,您不需要担心 JNDI SPI。您只需确保,您为每个想使用的命名或目录服务提供了一个服务提供程序。
JNDI的强大功能还远远不止如此,所有与系统外部的资源的引用,都可以通过JNDI定义和引用来完成,甚至是应用程序组件,如EJB。我们可以对应用程序架构中所得到的所有组件进行配置管理,从EJB组件到JMS队列和主题,再到简单配置字符串或者其他对象,这都可以使用JNDI来进行配置管理,从而简化部署和维护。
J2EE 规范要求所有 J2EE 容器都要提供 JNDI 规范的实现。JNDI 在 J2EE 中的角色就是“交换机” —— J2EE 组件在运行时 间接地 查找其他组件、资源或服务的通用机制。
6 JNDI应用实例
JNDI的官方tutorial内介绍一个使用文件系统的例子,该例子使用File System作为命名服务器,通过JNDI引用到本地文件系统中的某个文件
(项目位置 D:\MyEclipse Workspaces\MyEclipse 8.6\JNDI proj)
public static void main(String[] args) {
try {
Hashtable env=new Hashtable();
String name = "F:\\123.txt";
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.fscontext.RefFSContextFactory");
Context c= new InitialContext(env);
Object obj= c.lookup(name);
System.out.println("the name "+name+" is bound to object: " + obj+"\r\n");
/*
JNDI(Java Naming and Directory Interface,Java命名和目录接口)是一组在Java应用中访问命名和目录服务的API。命名服务将名称和对象联系起来,使得我们可以用名称访问对象。目录服务是一种命名服务,在这种服务里,对象不但有名称,还有属性。
*/
File f=(File)obj;
System.out.println(f.getAbsolutePath()+" "+f.getParent());
} catch (NamingException e) {
e.printStackTrace();
}
}
这里我们引用的是F盘内的一个文件,在初始化ContextFacotry时,需要设置相关的jar包,也就是服务提供者的访问接口,我们需要下载相应的JNDI服务器 (下载地址:http://java.sun.com/products/jndi/downloads/index.html)
这里下载的是文件系统服务提供器的JNDI,然后将其中的
包添加到项目的build path中,既可以成功运行。
可以看到其中的代码,我们利用一个Context环境,在其中使用name查找特定的对象,并返回这个对象的实体,也就是说这个实体和这个name是绑定在一起了,然后我们就可以对这个实体做相关的操作,在这里我让实体转型为File类型,然后就可以方便的对file进行操作了。当然访问file并不需要这么困难,但是JDNI的好处就在对于其他外部组件或者资源也可以通过名字查找来获取实体。
7 接下来我们来分析下服务提供者是如何做到提供命名服务的
从6中的链接,我们还可以从oracle官网上下载到一个jndi-1_2_1的压缩包,解压后里面有一个example,其中就包含了如何构建一个服务提供者。
从6中我们知道,要使用某个服务提供者我们先要获取这个服务提供者所提供的context,所以我们在客户端(这里的客户端指使用服务提供者的app,如6中的app)内new了一个Hashtable并填充了一个key/value记录
Env.put(Context. INITIAL_CONTEXT_FACTORY , “com.sun.jndi.fscontext.RefFSContextFactory”);
其中com.sun.jndi.fscontext.RefFSContextFactory是我们下载的fscontext.jar包内的一个RefFSContextFactory类,所以我们自定义的服务提供者也需要有这么一个类
它实现了InitialContextFactory接口内的getInitialContext函数(如下),返回一个Context。
然后客户端调用InitialContext类的构造函数,得到Context对象,Context c= new InitialContext(env);
深入到InitialContext的调用堆栈:
传入的environment就是我们一开始初始化的Hashtable,记录了java.naming.factory.initial / spi.flat.FlatInitCtxFactory这样的名值对。
继续看init(environment)的调用堆栈:
看看getInitialEnvironment的解释
* Given the environment parameter passed to the initial context
* constructor, returns the full environment for that initial
* context (never null). This is based on the environment
* parameter, the applet parameters (where appropriate), the
* system properties, and all application resource files.
*
* <p> This method will modify <tt>env</tt> and save
* a reference to it. The caller may no longer modify it.
我们知道内部对传入的environment做了一些修改并返回了一个完整的“环境”
接下来就是解析这个环境,看看getDefaultInitCtx()的函数实现
这里我们通过NamingManager(命名管理器,见上文JNDI体系结构图:命名管理器是JNDI API和JNDI SPI中间的控制器)和myProps拿到了所要的Context,再看看getInitialContext函数的实现,其中关键代码如下
通过loadClass将className(在这里是我们在HashTable中填入的java.naming.factory.initial)进行解析后初始化一个对应的实例对象factory
这个factory可以看做是我们自己使用我们所定义的FaltInitCtxFactory类的一个实例对象。
(这个FlatInitCtxFactory就是我们在插入HashTable的记录时所指定的value,而key就是前面的java.naming.factory.initial
)
最后利用这个factory实例对象,调用其函数 getInitialContext,返回Context
到这里我们的初始化Context就分析了一半了,接下来就是分析Context的初始化,也就是getInitialContext内的new FlatCtx (env)构造函数的实现。
我们知道JDNI的服务提供者必须暴露接口让上层的JNDI使用者来使用,所以这里FlatCtx来必须实现这些接口(这些接口都是Context基类所定义的)具体的实现方式就是根据服务提供者的命名规则来实现,这里就不在赘述,可以参考源码的FlatCtx类的实现。
最后是服务提供者样例Flat的使用
当然我们也可以像之前的例子那样根据name查找对象,获取对象信息
通过实例和分析,稍微对JNDI有了初步印象后,接下来的一篇文章我们继续分析JNDI的详细内容。
JNDI百度百科.rar
解惑_JNDI.rar
相关文章推荐
- 扬扬的J2EE学习笔记(四)Platform Services平台服务
- processing第三天学习笔记
- J2EE学习笔记之EL表达式
- J2EE系列之MyBatis学习笔记(九)-- 杂项
- J2EE学习笔记——MVC模式的用户管理系统(一)
- J2EE学习笔记:第二天
- go web 第三天 学习笔记 --mysql
- J2EE学习笔记五:entity bean概念
- J2EE学习(2)--何謂容器【良葛格学习笔记搬】
- TP框架学习笔记第三天
- J2EE学习笔记--Scripting Elements
- 笔记:ORACLE数据库基础学习 第三天
- 第三天学习笔记
- Struts2 框架学习第三天笔记
- (转)韩顺平j2ee学习笔记与心得
- J2EE学习笔记一:相关概念和基础知识
- J2EE学习笔记:第五天 EJB
- 技术转型产品学习笔记与理解[BRD]简述——第三天