一、概念:

1、JNDI(JavaNaming and Directory Interface,Java命名和目录接口)是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,客户端通过统一接口的API,来使用不同服务,这些不同的服务分别实现了JNDI服务供应接口(SPI),使得Java应用程序可以和这些命名服务和目录服务之间进行交互。目录服务是命名服务的一种自然扩展。两者之间的关键差别是目录服务中对象不但可以有名称还可以有属性(例如,用户有email地址),而命名服务中对象没有属性。

JNDI中的命名(Naming),就是将Java对象以某个名称的形式绑定(binding)到一个容器环境(Context)中,以后调用容器环境(Context)的查找(lookup)方法又可以查找出某个名称所绑定的Java对象。

JNDI可访问的现有的目录及服务有:DNS、XNam 、Novell目录服务、LDAP(Lightweight Directory Access Protocol轻型目录访问协议)、 CORBA对象服务、文件系统、Windows XP/2000/NT/Me/9x的注册表、RMI、DSML v1&v2、NIS。

JNDI中的目录(Directory)是指将一个对象的所有属性信息保存到一个容器环境中。JNDI的目录(Directory)原理与JNDI的命名(Naming)原理非常相似,主要的区别在于目录容器环境中保存的是对象的属性信息,而不是对象本身,所以,目录提供的是对属性的各种操作。

2、JNDI树结构示意图:

3、JNDI环境属性:

4、  JNDI的基本使用方法:
一种是在代码里指定上面的参数:
  Hashtable
env = new Hashtable();

  env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");

  env.put(Context.PROVIDER_URL, "dns:" +
dnsServer);

  DirContext ctx = new InitialDirContext(env);

另一种是在jndi.properties文件中指定,文件内容可以如下:

  java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory

  java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces

  java.naming.provider.url=localhost

jndi.properties文件为所有的InitialContexts设置默认的属性,jndi.properties文件的搜索次序:·CLASSPATH,·$JAVA_HOME/lib;

二、Demo:

从JDK 1.4开始的版本又集成了用于DNS查询的JNDI服务程序,所以,如果我们使用JDK 1.4及更高的JDK版本来开发DNS信息查询程序时,不需要下载和安装JNDI API和用于DNS查询的JNDI服务程序。

下面是一个使用JNDI
API获取DNS信息的程序,运行这个程序时,需要指定一个或两个参数,第一个参数是必须的,为要查询的域名,第二个参数是可选的,为查询时所使用的DNS服务器的IP地址,如果没有指定第二个参数,DNS的JNDI服务程序将使用底层操作系统上设置的DNS服务器。

import java.util.Hashtable;

import javax.naming.Context;

import javax.naming.NamingEnumeration;

import javax.naming.NamingException;

import javax.naming.directory.Attribute;

import javax.naming.directory.Attributes;

import javax.naming.directory.DirContext;

import javax.naming.directory.InitialDirContext;

public class DNSQuery

{

    public static void main(String[] args) throws NamingException

    {

        /*第一个参数指定要查询的域或主机名,第二个参数指定查询的DNS服务器,

        为了程序的简单易读性,省略了严格的参数错误检查*/

        String domain = args[0];

        String dnsServer = args.length<2 ? "" : ("//" + args[1]);

        //通过环境属性来指定Context的工厂类

        Hashtable env = new Hashtable();

        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");

        env.put(Context.PROVIDER_URL, "dns:" + dnsServer);

        DirContext ctx = new InitialDirContext(env);

        //分别获取包含所有属性和只包含Mx属性的Attributes对象

        Attributes attrsAll = ctx.getAttributes(domain);   

        Attributes attrsMx = ctx.getAttributes(domain, new String[]{"MX"});

        /*上面的整段程序代码也可以用下面这段程序代码来替代,下面这段程序

        代码通过查询URL中的Schema信息来自动选择Context的工厂类*/

        /*

        DirContext ctx = new InitialDirContext();

        Attributes attrsAll = ctx.getAttributes("dns:" + dnsServer + "/" + domain);

        Attributes attrsMx = ctx.getAttributes(

            "dns:" + dnsServer + "/" + domain, new String[]{"MX"});        

        */

        System.out.println("打印出域" + domain +

                                "的Attributes对象中的信息:");

        System.out.println(attrsAll);

        System.out.println("--------------------------");

        System.out.println("打印只检索域" + domain +

                                "的MX记录的Attributes对象:");   

        System.out.println(attrsMx);

        System.out.println("--------------------------");

        System.out.println("逐一打印出Attributes对象中的各个属性:");         

        NamingEnumeration attributes = attrsAll.getAll();

        while(attributes.hasMore())

        {  

            System.out.println(attributes.next());

        }

        System.out.println("--------------------------");

        //直接调用get方法从attrsMx集合检索MX属性

        System.out.println("直接检索Attributes对象中的MX属性:");    

        Attribute attrMx = attrsAll.get("MX");

        System.out.println(attrMx);

        System.out.println("--------------------------");          

        //获取Mx属性中的第一个值:

        System.out.println("获取Mx属性中的第一个值:");

        String recordMx = (String)attrMx.get();

        System.out.println(recordMx);

        //从Mx属性的第一个值中提取邮件服务器地址

        System.out.println("从MX属性值中提取的邮件服务器地址:");

        String smtpServer = recordMx.substring(

                            recordMx.indexOf(" ") + 1);

        System.out.println(smtpServer);

} 

三、源码解析:

1、Java命名系统的接口是Context,JNDI API中提供了一个InitialContext类来创建用作JNDI命名操作的入口Context对象。

2、Java目录系统的接口是DirContext,DirContext是Context的子类,显然它除了能完成目录相关的操作外,也能完成所有的命名(Naming)操作。DirContext是对Context的扩展,它在Context的基础上增加了对目录属性的操作功能,可以在其中绑定对象的属性信息和查找对象的属性信息。JNDI API中提供了一个InitialDirContext类来创建用作JNDI命名与目录属性操作的入口DirContext对象。

3、InitialContext类中的myprops成员变量是一个Hashtable类型,存储属性名称和属性值的键值对,用于初始化Context时的相关参数,defaultInitCtx成员变量存储真正实现服务的Context对象,象Context的API接口(比如bind, lookup, rebind等)都是调用这个defaultInitCtx来实现的;

4、InitialContext对象的启动过程:

构造函数如果传入了一个hashtable对象,则克隆该hashtable对象(包括克隆原hashtable对象里面的数据),如果没传,则创建一个新的hashtable对象:

可以看到构造函数都会调用init函数,在init函数中又会调用ResourceManager.getInitialEnvironment函数,下面是getInitialEnvironment函数的实现:

可以看到getInitialEnvironment函数中会判断构造函数传过来的hashtable参数如果没传,则创建一个初始只有11个元素的hashtable对象;

在这个函数中,会读取VersionHelper.PROPS的所有属性,其定义如下:

在读取这些属性值时,首先优先从构造函数传递过来的hashtable中去读取,如果读取不到,再到java.naming.applet中去读取对应的属性值,如果还是读取不到,则从系统参数中去读取;如果这三个里面只要有任何一个独到属性值了,则将属性名和属性值的键值对存储到env变量中并返回这个env对象;

再来看看init函数:

可以看到init函数首先调用上面的getInitialEnvironment返回一个存储有服务对象实例化时需要的属性的hashtable对象,如果这个hashtable里面有java.naming.factory.initial属性时,则调用getDefaultInitCtx来构造服务对象并存储在defaultInitCtx变量里面;

可以看到getDefaultInitCtx调用NamingManager.getInitialContext来实例化服务对象,而在getInitialContext中根据java.naming.factory.initial属性值指定的类名实例化服务对象,然后调用factory.getInitialContext来初始化其他属性并返回实例化的Context对象;

5、InitialContext对象相关接口的实现:

可以看到InitialContext对象相关接口的实现都是调用了getURLOrDefaultInitCtx函数返回的服务示例的接口;

可以看到这个函数会根据传递的name参数查找到对应的子容器对象Context返回,如果查找不到则返回默认的Context对象defaultInitCtx;

6、InitialDirContext对象的启动过程:

可以看到InitialDirContext的构造函数什么也没做,只是调用父类InitialContext的构造函数;

7、InitialDirContext对象相关接口的实现:

bind/lookup/rebind等接口以及新增加的 getAttributes/modifyAttributes/getSchema等接口的实现是依赖于getURLOrDefaultInitDirCtx函数返回的DirContext对象,而getURLOrDefaultInitDirCtx函数又是调用父类的getURLOrDefaultInitCtx函数实现的;

Java JNDI 学习的更多相关文章

  1. 如何学习Java?学习Java顺序?

    Java相对于Asp.Net或Asp.Net MVC来讲,入门是比较困难和烦琐的!它不像.Net哪样有安装开发工具就可以跑程序了,不需要配置复杂的运行环境. 推荐的学习Java的学习顺序如下: 一.J ...

  2. Java的学习之路

    记事本 EditPlus eclipse Java的学习软件,已经系统性学习Java有一段时间了,接下来我想讲一下我在Java学习用到的软件. 1.第一个软件:记事本 记事本是Java学习中最基础的编 ...

  3. Java多线程学习笔记

    进程:正在执行中的程序,其实是应用程序在内存中运行的那片空间.(只负责空间分配) 线程:进程中的一个执行单元,负责进程汇总的程序的运行,一个进程当中至少要有一个线程. 多线程:一个进程中时可以有多个线 ...

  4. Java Web 学习路线

    实际上,如果时间安排合理的话,大概需要六个月左右,有些基础好,自学能力强的朋友,甚至在四个月左右就开始找工作了.大三的时候,我萌生了放弃本专业的念头,断断续续学 Java Web 累计一年半左右,总算 ...

  5. Java基础学习-- 继承 的简单总结

    代码参考:Java基础学习小记--多态 为什么要引入继承? 还是做一个媒体库,里面可以放CD,可以放DVD.如果把CD和DVD做成两个没有联系的类的话,那么在管理这个媒体库的时候,要单独做一个添加CD ...

  6. 20145213《Java程序设计学习笔记》第六周学习总结

    20145213<Java程序设计学习笔记>第六周学习总结 说在前面的话 上篇博客中娄老师指出我因为数据结构基础薄弱,才导致对第九章内容浅尝遏止地认知.在这里我还要自我批评一下,其实我事后 ...

  7. [原创]java WEB学习笔记95:Hibernate 目录

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  8. Java多线程学习(转载)

    Java多线程学习(转载) 时间:2015-03-14 13:53:14      阅读:137413      评论:4      收藏:3      [点我收藏+] 转载 :http://blog ...

  9. java基础学习总结——java环境变量配置

    前言 学习java的第一步就要搭建java的学习环境,首先是要安装JDK,JDK安装好之后,还需要在电脑上配置"JAVA_HOME”."path”."classpath& ...

随机推荐

  1. Neo4j视频教程 Neo4j 图数据库视频教程

    课程名称 课程发布地址 地址: 腾讯课堂<Neo4j 图数据库视频教程> https://ke.qq.com/course/327374?tuin=442d3e14 作者 庞国明,< ...

  2. C++面试问题详解

    1.定义一个全局变量放在.cpp文件还是.h文件,原因是什么 在cpp文件中定义变量,h文件用来声明变量的作用域,使用extern声明的变量可以在本编译单元或其他编译单元中使用. 举例如下: a.h文 ...

  3. HDU-3790 最短路径问题(双重权值)

    Problem Description 给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的.   Inp ...

  4. C# / .NET 在类中使用Server.MapPath

    直接在类中使用 Server.MapPath 会出现错误,这是由于类中不能直接使用 System.Web.UI.Page 的非静态函数造成的.解决方法有两种: 方法一.使类继承System.Web.U ...

  5. IOS8-人机界面指南

    [ISUX转译]iOS 8人机界面指南(一):UI设计基础 糖箔糊2014.09.23 文章索引 1.1 为iOS而设计(Designing for iOS) 1.1.1 以内容为核心(Defer t ...

  6. python中的对象

    一.python对象 python使用对象模型来存储数据.构造任何类型的值都是一个对象. 所有python对象都拥有三个特性:身份.类型.值 身份:每个对象都有一个唯一的身份标识自己,任何对象的身份可 ...

  7. JavaScript学习总结(二)——逻辑Not运算符详解

    在JavaScript 中,逻辑NOT运算符与C和Java中的逻辑 NOT 运算符相同,都由感叹号(!)表示.与逻辑 OR 和逻辑 AND 运算符不同的是,逻辑 NOT 运算符返回的一定是 Boole ...

  8. elasticsearch配置详解

    一.说明 使用的是新版本5.1,直接从官网下载rpm包进行安装,https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5 ...

  9. CSS中的长度单位及颜色表示

       CSS中的长度单位及颜色表示 长度单位: px:像素,绝对单位 em:相对单位,如果用于font-size属性本身,则是相对于父元素的font-size.若用于其他属性(width,height ...

  10. scrapy-redis(七):部署scrapy

    一般我们写好scrapy爬虫,如果需要启动的话,需要进入scrapy项目的根目录,然后运行以下命令: scrapy crawl {spidername} 这样我们就可以在终端查看到爬虫信息了.但爬虫运 ...