JNDI 详解:它是什么,为什么用,怎么用?
一、JNDI 是什么?(核心定义)
JNDI 的全称是 Java Naming and Directory Interface(Java命名和目录接口)。
通俗地说,它是 Java 的一个"电话簿"或"字典" ,专门用来查找各种资源对象(如数据库连接池、消息队列、EJB组件等)。
在 MyBatis 的配置中,<dataSource type="JNDI"> 表示:"我不在这里配置数据库连接信息,而是让 MyBatis 去 JNDI 这个'电话簿'里根据名字查一个现成的数据源来用。"
二、JNDI 的设计思想与工作原理
2.1 类比:DNS 域名系统
- DNS :你输入
www.baidu.com,DNS 服务帮你找到对应的 IP 地址(220.181.38.251)。 - JNDI :你告诉程序"我要找名叫
jdbc/mydb的数据源",JNDI 服务帮你找到实际的数据库连接池对象(包含 IP、端口、用户名、密码等)。
2.2 核心机制:绑定(Bind)与查找(Lookup)
JNDI 基于一个树形目录结构,类似文件系统的文件夹:
java:comp/env
└── jdbc
└── mydb (指向一个 DataSource 对象)
- 绑定(Bind) :由应用服务器(如 Tomcat、WebLogic)管理员提前把数据源对象注册到 JNDI 目录中,并起一个名字(如
jdbc/mydb)。 - 查找(Lookup) :应用程序通过 JNDI API,根据名字(
java:comp/env/jdbc/mydb)从目录中把数据源对象取出来。
三、JNDI 在 MyBatis 中的具体用法
3.1 MyBatis 配置文件中的写法
xml
<dataSource type="JNDI">
<property name="data_source" value="java:comp/env/jdbc/mydb"/>
<!-- 或简写为 -->
<property name="data_source" value="jdbc/mydb"/>
</dataSource>
type="JNDI":告诉 MyBatis 不要自己创建数据源,而是去 JNDI 容器中查找。data_source属性:指定要查找的 JNDI 名称。
注意 :
java:comp/env是 J2EE 标准的环境命名上下文(ENC),推荐写法是把资源放在这个前缀下,便于容器统一管理。
3.2 对比 MyBatis 的其他数据源类型
| 类型 | 行为 | 适用场景 |
|---|---|---|
POOLED |
MyBatis 自己创建连接池,配置写在 XML 里 | 独立应用、Spring Boot 默认 |
UNPOOLED |
MyBatis 自己创建普通连接,每次新建 | 临时测试 |
JNDI |
MyBatis 去容器里查找现成的连接池 | Java EE 应用服务器环境(如 Tomcat、WebLogic) |
四、为什么要在应用服务器里配置 JNDI 数据源?
4.1 优点
- 集中管理:数据库连接池由应用服务器统一管理,多个应用可以共享同一个连接池。
- 运维友好 :DBA 或运维人员可以直接在服务器管理控制台修改数据库密码、连接数等参数,无需改代码和重新部署。
- 避免密码泄漏 :密码不出现在项目的配置文件(如
application.yml或mybatis-config.xml)中,降低安全风险。 - 容器级优化:应用服务器(如 Tomcat、WebLogic)的连接池实现通常经过多年优化(如 Tomcat 自带的 DBCP 或 HikariCP 集成),性能和监控能力更强。
4.2 缺点
- 环境依赖 :应用必须部署在支持 JNDI 的容器中(Tomcat、Jetty、WebLogic 等),不能在纯
java -jar的独立运行环境下使用(除非借助 Spring Boot 等模拟 JNDI)。 - 配置复杂 :需要额外在服务器端配置(如 Tomcat 的
context.xml),增加了部署步骤。 - 本地开发不便:开发人员需要在自己的电脑上配置好 JNDI 资源,否则运行报错。
五、完整配置示例(以 Tomcat 为例)
5.1 第一步:在 Tomcat 中配置 JNDI 数据源
在 META-INF/context.xml(项目内部)或 $CATALINA_HOME/conf/context.xml(全局)中添加:
xml
<Context>
<Resource name="jdbc/mydb"
auth="Container"
type="javax.sql.DataSource"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://localhost:3306/xie"
username="root"
password="1234"
maxTotal="20"
maxIdle="10"
maxWaitMillis="10000"/>
</Context>
5.2 第二步:在 MyBatis 中引用它
xml
<dataSource type="JNDI">
<property name="data_source" value="java:comp/env/jdbc/mydb"/>
</dataSource>
5.3 第三步(可选):在 web.xml 中声明引用
xml
<resource-ref>
<description>MySQL Datasource</description>
<res-ref-name>jdbc/mydb</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
六、Java 代码里直接使用 JNDI 的样子(不依赖 MyBatis)
java
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
public class JNDIExample {
public static void main(String[] args) throws Exception {
// 1. 创建 JNDI 上下文
Context initContext = new InitialContext();
// 2. 查找环境命名上下文
Context envContext = (Context) initContext.lookup("java:comp/env");
// 3. 根据名称获取数据源
DataSource ds = (DataSource) envContext.lookup("jdbc/mydb");
// 4. 获取数据库连接
Connection conn = ds.getConnection();
// ... 执行 SQL
conn.close();
}
}
七、总结:一句话记住 JNDI
JNDI 是 Java 应用与外部资源(如数据库连接池)之间的"中间商",应用通过"名字"去拿资源,而不是自己"造"资源。它在服务器容器环境中极为常见,是 Java EE 规范的重要组成部分。
在 MyBatis 中,如果你在非容器环境 (如 Spring Boot 内嵌 Tomcat 以 jar 包形式运行),通常用 POOLED 或 HikariCP 直连即可,无需配置 JNDI;只有在传统 Java EE 应用(部署到外部 Tomcat/WebLogic 的 war 包) 或需要与 DBA 运维解耦时,才建议使用 JNDI 类型。