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对象。

img

上图中的每个方框分别代表以后个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)的结构示意图如下:

img

上图中的每个外层的方框分别代表一个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 {@Overrideprotected 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是一种广泛存在于各种J2EE容器的技术,而且也是J2EE的技术规范之一。但由于我们日常工作中其实很少会直接在代码层面使用到JNDI规范(当然了,JNDI配置肯定是少不了的),所以可能有时候会觉得有些陌生。本篇文章将对JNDI的概念及其作用进行介绍,同时通过一个小案例来介绍我们如何在真实开发中使用JNDI规范,希望对各位读者有所帮助。

一、什么是JNDI

JNDI的全称是Java Naming and Directory Interface,即Java命名和目录接口,作用是为JAVA应用程序提供命名和目录访问服务的API。
咋一看这个解释似乎让人看不懂这个JNDI规范的作用。我个人感觉,将JNDI规范看成是一个让配置参数和代码解耦的一种规范和思想会更好理解一些。比如说常见的在DAO层通过原始的JDBC来连接数据库,我们可以选择在代码中直接写入数据库的连接参数,但一旦数据源发生变更,我们就势必要改动代码后重新编译才能连接新的数据源。而如果我们将数据库连接参数改成外部配置的方式,那么也就实现了配置和代码之间的解耦。JNDI规范本质上就是上述的这种操作。

看完上面的介绍,相信大家对JNDI的作用已经有了一个初步的认知。但是对于其具体的应用场景和工作流程还不是特别清楚。下面我们先来对JNDI的两个单词的概念进行解释:NameDirectory

二、概念解析

Name

Name很好理解,就是命名。将Java对象以某个名称的形式绑定(binding)到一个容器环境(Context)中,以后调用容器环境(Context)的查找(lookup)方法又可以查找出某个名称所绑定的Java对象。简单来说,就是把一个Java对象和一个特定的名称关联在一起,方便容器后续使用。

Directory

**JNDI中的目录(Directory)是指将一个对象的所有属性信息保存到一个容器环境中。**JNDI的目录(Directory)原理与JNDI的命名(Naming)原理非常相似,主要的区别在于目录容器环境中保存的是对象的属性信息,而不是对象本身。举个例子,Name的作用是在容器环境中绑定一个Person对象,而Directory的作用是在容器环境中保存这个Person对象的属性,比如说age=10name=小明等等。实际上,二者往往是结合在一起使用的

三、JDNI的实际应用

下面我们来通过一个小案例来演示一下JNDI在实际项目中的应用

正如JNDI的名称一样,JNDI只是JDK定义的一个接口,具体的实现其实是由各个容器的厂商(或者称之为开发团队)去实现的。这里的话我们选择用Tomcat作为我们演示的容器环境,下面来看一下具体的使用吧。

1. 新建一个Web项目,在META-INF目录下新建context.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<Context><Resource name="jndi/person"auth="Container"type="javax.sql.DataSource"username="root"password="root"driverClassName="com.mysql.jdbc.Driver"url="jdbc:mysql://localhost:3306/test"maxTotal="8"maxIdle="4"/>
</Context>

Context.xml文件中我们可以定义数据库驱动,url、账号密码等关键信息,其中name这个字段的内容为自定义,后面的时候我们会使用到

2. 在具体的程序中使用配置的数据源

这里需要注意,使用InitialContext对象获取数据源的时候,java:comp/env/后面需要跟上步骤一中配置的数据源名称。

Connection conn=null; 
PreparedStatement ps = null;
ResultSet rs = null;
try { Context ctx=new InitialContext(); Object datasourceRef=ctx.lookup("java:comp/env/jndi/person"); //引用数据源 DataSource ds=(Datasource)datasourceRef; conn=ds.getConnection(); String sql = "select * from person where id = ?";ps = conn.prepareStatement(sql);ps.setString(1, "1");rs = ps.executeQuery();while(rs.next()){System.out.println("person name is "+rs.getString("name"));}c.close(); 
} catch(Exception e) { e.printStackTrace(); 
} finally { if(conn!=null) { try { conn.close(); } catch(SQLException e) { } } 
}
3. 将mysql驱动放到tomcat的lib目录中(重要)

img

需要注意,由于我们的数据源不再直接由自己提供,而是转为由Tomcat容器提供,所以我们要给tomcat提供连接池的驱动,否则会报错。

4. 启动tomcat进行测试

在这里插入图片描述

PS:这里关于Tomcat集成JNDI的用法,其实在启动tomcat后,访问http://localhost:8080/docs/jndi-resources-howto.html可以看到更加详细的使用介绍

说在最后

至此,本篇文章对于JDNI的介绍就到此结束了。简单来说,JNDI就是J2EE提出的一种能够降低耦合性的一种技术规范,并定义了接口。JNDI接口由各个容器开发团队去实现,有了JDNI之后,我们可以将一些与业务无关的配置转移到外部,更好的方便项目的维护。(从另一个角度理解的话,可以把JNDI看成是间接寻址,在编写代码的时候定义了某个对象的名称,等到实际使用的时候根据这个对象名称再去读取配置)

参考文章:
1、JNDI的原理和应用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/554939.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

BigDecimal保留两位小数

文章目录前言1.代码实现2.方法详解注释前言 在项目中经常会用到小数的一些计算&#xff0c;而float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算&#xff0c;这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而&#xff…

DataIntegrityViolationException: Error attempting to get column ‘xx‘——DataIntegrityViolationExceptio

一、解决办法 项目中在更新数据库时出现异常&#xff0c;org.springframework.dao.DataIntegrityViolationException&#xff0c;当然如果控制台直接报这个异常问题的解决估计也不至于让我写篇博客。 先说这个异常代表的含义吧&#xff1a; 这个异常的意思就是在更新&#xff…

Java中new Date插入mysql数据库,数据库时间多一秒问题

这是由于new Date()时&#xff0c;实际上是调用的System.currentTimeMillis()方法&#xff0c;即获得以毫秒为级别的时间戳。 一般数据库表的字段类型datetime/timestamp长度都是设置为0。 MySQL数据库对于毫秒大于500的数据进行进位&#xff0c;所以就造成的MySQL中的时间多一…

学php还是golang,学swoole还是golang

Swoole是一个面向生产环境的 PHP 异步网络通信引擎&#xff0c;使 PHP 开发人员可以编写高性能的异步并发 TCP、UDP、Unix Socket、HTTP&#xff0c;WebSocket 服务。 (推荐学习&#xff1a;swoole视频教程)Swoole 可以广泛应用于互联网、移动通信、企业软件、云计算、网络游戏…

java解析vue对象数组,Java数组

Java提供了一个数据结构&#xff0c;所述数组&#xff0c;其存储相同类型的元件的固定大小的连续集合。数组用于存储数据集合&#xff0c;但将数组视为相同类型变量的集合通常更为有用。您可以声明一个数组变量&#xff0c;例如数字和数字[0]&#xff0c;数字[1]和...&#xff…

Java中List的subList()方法及使用注意事项

List<Object> list new Arraylist<>();List<Object> subList list.subList(0, 5);其中subList(0, 5)取得的是下标为0到4的元素,不包含下标为5的元素. java.util.List中的subList方法返回列表中指定的 fromIndex&#xff08;包括 &#xff09;和 toIndex&a…

SpringBoot 实现SSE 服务器发送事件

SSE 全称Server Sent Event&#xff0c;直译一下就是服务器发送事件&#xff0c;一般的项目开发中&#xff0c;用到的机会不多&#xff0c;可能很多小伙伴不太清楚这个东西&#xff0c;到底是干啥的&#xff0c;有啥用 本文主要知识点如下&#xff1a; SSE 扫盲&#xff0c;应…

php多表递归查询,使用公用表表达式的递归查询

微软从SQL2005起引入了CTE(Common Table Expression)以强化T-SQL。公用表表达式 (CTE) 具有一个重要的优点&#xff0c;那就是能够引用其自身&#xff0c;从而创建递归 CTE。递归 CTE 是一个重复执行初始 CTE 以返回数据子集直到获取完整结果集的公用表表达式。当某个查询引用递…

Springboot之整合SSE实现消息推送

Springboot之整合SSE实现消息推送 前言 项目中涉及到部分请求&#xff0c;后端处理时间较长&#xff0c;使用常规Http请求&#xff0c;页面等待时间太长&#xff0c;对用户不友好&#xff0c;故考虑使用长链接进行消息推送&#xff0c;可选方案有WebSocket、SSE&#xff0c;We…

Vue中npm run dev 和 npm run serve区别

在运行vue文件时&#xff0c;需要进行npm操作&#xff0c;但我们发现&#xff0c;有时候用的是npm run serve&#xff0c;而有的时候用的是npm run dev&#xff0c;二者有什么区别 在我们运行一些 vue 项目的时候&#xff0c;输入npm run serve或者 npm run dev的其中一个时&a…

SpringBoot导出数据为PDF

一、SpringBoot导出数据为PDF 1、添加所需依赖 <dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.11</version> </dependency> <dependency><groupId>com.itextpdf.…

php安装mem+cache扩展,安装memcached及php扩展

用的是centos系统1、安装memcachedyum -y install memcached安装完成后&#xff0c;memcached -h应该会出现memcached 参数说明2、memcached配置文件vi /etc/sysconfig/memcachedPORT"11210"USER"memcached"MAXCONN"1024"CACHESIZE"64"…

Springboot集成支付宝沙箱支付(完整版)

开发前准备 easy支付官方文档&#xff1a;https://opendocs.alipay.com/open/009ys9 通用版文档&#xff1a;https://opendocs.alipay.com/open/02np94 支付宝沙箱的配置 注册支付宝开发者账户&#xff0c;进入开发者控制台 https://openhome.alipay.com/platform/developer…

Springboot集成支付宝沙箱支付(退款功能)

包括&#xff1a; 支付宝沙箱 支付 异步通知 退款功能 正式版本的sdk 通用版本SDK文档&#xff1a;https://opendocs.alipay.com/open/02np94 <dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-sdk-java</artifactId><…

Java递归生成树

1.建菜单表 CREATE TABLE t_menu (id int(11) NOT NULL AUTO_INCREMENT,pid int(11) NOT NULL,name varchar(255) DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT11 DEFAULT CHARSETutf8mb4;2.造一些数据 注意&#xff1a;根节点的pid0&#xff0c;其他节点的p…

Springboot获取公网IP和当前所在城市(非常简单)

最近我们发现各大社交平台都出现了一个新的功能&#xff1a;IP属地。 比如某乎&#xff1a; 这个IP属地是怎么做到的呢&#xff1f;今天我来教教你&#xff0c;保证你看完直呼Easy~ 百度搜索 打开百度&#xff0c;搜索IP&#xff0c;你就能看到你当前的IP地址&#xff0c;类…

线程死锁——死锁产生的条件

什么是线程死锁 线程死锁是指由于两个或者多个线程互相持有对方所需要的资源&#xff0c;导致这些线程处于相互等待状态&#xff0c;若无外力作用&#xff0c;它们将无法继续执行下去。 造成死锁的原因可以概括成三句话&#xff1a; 当前线程拥有其他线程需要的资源当前线程…

TortoiseGit的使用详解

Git是什么&#xff0c;相信大家都很清楚。Git不就是分布式版本控制系统嘛&#xff1f;那你知道TortoiseGit是什么吗&#xff1f;下面我们就介绍一下TortoiseGit它是什么&#xff1f;如何使用&#xff1f;   TortoiseGit其实是一款开源的git的版本控制系统&#xff0c;也叫海龟…

将项目上传到Gitee上(命令方式使用TortoiseGit方式)

如何将项目上传到Gitee上&#xff08;命令方式&#xff09; 目录 将项目上传到Gitee是我们经常需要使用到的操作&#xff0c;因此我们要熟悉这些步骤 一、首先保证本机已经安装了Git git官网安装完成之后&#xff0c;鼠标右键会出现Git GUI Here和Git Bash Here 二、上传代…

BufferedImage类、Image类、Graphics类

BufferedImage Image是一个抽象类&#xff0c;BufferedImage是其实现类&#xff0c;是一个带缓冲区图像类&#xff0c;主要作用是将一幅图片加载到内存中&#xff08;BufferedImage生成的图片在内存里有一个图像缓冲区&#xff0c;利用这个缓冲区我们可以很方便地操作这个图片&…