【MyBatis】MyBatis Tomcat JNDI原理及源码分析
一、 Tomcat JNDI
JNDI(java nameing and drectory interface),是一组在Java应用中访问命名和服务的API,所谓命名服务,即将对象和名称联系起来,使得可以通过名称访问并获取对象。
简单原理介绍:点击访问
tomcat已经集成该服务(内置并默认使用DBCP连接池),简单来说就是键值对的mapping,而且在tomcat服务器启动的首页configuration中就已经有完成的示例代码。要想使用tomcat的JNDI服务,只需要导入相关的jar包,建立所需的配置文件,采用JDK的命名服务API根据配置名称即可获得相应的服务。每个步骤的详细解释以及范例如下文所述。
1. jar包导入
tomcat内置了DBCP并默认使用该连接池,在tomcat的lib包中已经DBCP的两个jar包,因此不需要导入,如果使用其他连接池技术,则需要重新拷贝连接池的jar包。拷贝数据库驱动包到tomcat lib目录下,完成jar包的导入。
细节:将所需jar包直接拷贝到tomcat的lib目录中,而不是在应用中build path导入jar包。这是因为JNDI的原理类似于windows的注册表,通过配置文件(context.xml:下节详细介绍)在tomcat启动的时候就告诉tomcat在其命名服务目录下对应着配置文件中的服务名字创建服务应用。这是tomcat对外提供的一个整体服务,而不是单独对某一个应用提供的服务。
2. 配置文件:comtext.xml
在META-INF目录下建立context.xml配置文件,在文件中需要配置资源名字name和资源类型type,建立文件的目的就是告诉服务器根据服务名字创建相应的服务应用。
如下示例(DBCP),服务名称是“jdbc/mybatis-jndi”,对应的服务类型是“javax.sql.DataSource”,即通过Tomcat提供的JNDI服务,根据name=“jdbc/mybatis-jndi”可以获取到type=“javax.sql.DataSource”的服务,至于type中还需要配置什么东西,则根据实际的type类型来进行配置即可。
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<!-- maxActive: Maximum number of database connections in pool. Make sure
you configure your mysqld max_connections large enough to handle all of your
db connections. Set to -1 for no limit. -->
<!-- maxIdle: Maximum number of idle database connections to retain in pool.
Set to -1 for no limit. See also the DBCP documentation on this and the minEvictableIdleTimeMillis
configuration parameter. -->
<!-- maxWait: Maximum time to wait for a database connection to become available
in ms, in this example 10 seconds. An Exception is thrown if this timeout
is exceeded. Set to -1 to wait indefinitely. -->
<!-- username and password: MySQL username and password for database connections -->
<!-- driverClassName: Class name for the old mm.mysql JDBC driver is org.gjt.mm.mysql.Driver
- we recommend using Connector/J though. Class name for the official MySQL
Connector/J driver is com.mysql.jdbc.Driver. -->
<!-- url: The JDBC connection url for connecting to your MySQL database. -->
<Resource name="jdbc/mybatis-jndi" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000" username="root"
password="51NByes!" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/javadb" />
</Context>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
3. API代码示例
通过JDK提供的命名服务API,可以通过name获取type服务,示例代码如下,其中tomcat将所有JNDI对应服务注册在/com/env目录之下(写法固定,除非不同版本的tomcat有不同的实现),因此应用想要获取服务,则需要先知道tomcat的jndi都提供了哪些服务,在根据某一个服务的name来获取具体的服务,而这个name对应着上一节中context.xml中配置的name。
Context initContext = new InitialContext();
Context envContext = (Context) initContext.lookup("java:/comp/env");
DataSource ds = (DataSource) envContext.lookup("jdbc/mybatis-jndi");
Connection conn = ds.getConnection();
- 1
- 2
- 3
- 4
总结 使用tomcat的JNDI服务需要以下三个步骤
1. 拷贝jar包到tomcat的lib目录中
2. 在应用的META-INF目录中建立context.xml配置文件,将KV的服务注册到tomcat的”java:/comp/env”目录下
3. 通过JDK naming API获取服务
二、MyBatis JNDI源码分析
MyBatis的dataSource类型有三种,其中JNDI的实现和tomcat JNDI一模一样,只是MyBatis的JNDI工厂(org.apache.ibatis.datasource.jndi.JndiDataSourceFactory)已经帮我们实现了第三步”通过API获取datasource“,但是还需要我们自己进行第一步导入jar包和第二步context.xml的配置。
细节:
1. 在进行context.xml配置的时候,其中服务名称是可变的,需要通过配置文件注入到MyBatis的JNDI工厂中;
2. 因为不同服务器的JNDI目录不一样,因此在context.xml中配置的时候也许要注入到MyBatis的JNDI工厂中;
1. 源码剖析
package cn.wxy.analysis;
import java.util.Map.Entry;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import org.apache.ibatis.datasource.DataSourceException;
import org.apache.ibatis.datasource.DataSourceFactory;
/**
* Mybatis JNDI源码剖析
* @author Administrator
*/
public class JndiDataSourceFactory implements DataSourceFactory {
/**
* initial_context: 服务器的JNDI目录,不同的服务器该值不同,因此需要在mybatis-config的配置文件中传入该值
* data_source:对应着META-INF/context.xml中注册的服务名称(name属性值),即键值对中的键值
*/
public static final String INITIAL_CONTEXT = "initial_context";
public static final String DATA_SOURCE = "data_source";
public static final String ENV_PREFIX = "env.";
private DataSource dataSource;
/**
* 该方法在初始化Mybatis的时候被调用,会将mybatis-config.xml中配置的属性注入进来
* 主要注入的是initial_context和data_source的值
*/
public void setProperties(Properties properties) {
/**
* 参照tomcat直接获取JNDI服务
* -------------------------
* 第一步:Context initContext = new InitialContext();
* 第二步:Context envContext = (Context) initContext.lookup("java:/comp/env");
* 第三步:DataSource ds = (DataSource) envContext.lookup("jdbc/mybatis-jndi");
*/
try {
//第一步:声明JAVA命名和目录接口的上下文类
InitialContext initCtx = null;
// properties在SqlSessionFactoryBuilder创建SqlSessionFactory的过程中收集<dataSource>标签下属性建立
// env不为null的条件是在mybatis-config.xml中配置的JNDI属性以"env."开头
// 其实不必要以"env."开头,在getEnvProperties方法中最终也会去掉"env."
Properties env = getEnvProperties(properties);
if (env == null) {
// 进入到这个流程,默认使用SqlSessionFactoryBuilder流程中的properties
initCtx = new InitialContext();
} else {
// 如果配置文件中配置的JNDI属性以"env."开头,则进入这个步骤
// 实际上有些冗余,鸡肋没有必要
initCtx = new InitialContext(env);
}
/**
* mybatis-config.xml中有两种方式可以进行JNDI数据源的配置
* 1. 第一种方式需要配置initial_context和data_source的值,本例中
* initial_context="java:/comp/env"
* data_source="jdbc/mybatis-jndi"
* 2. 第二种方式只需要配置data_source的值
* data_source="java:/comp/env/jdbc/mybatis-jndi"
*
* 结论:其实是一样的,请结合context.xml配置文件内容查看此处
*/
if (properties.containsKey(INITIAL_CONTEXT)
&& properties.containsKey(DATA_SOURCE)) {
//第一种方式
Context ctx = (Context) initCtx.lookup(properties
.getProperty(INITIAL_CONTEXT));
dataSource = (DataSource) ctx.lookup(properties
.getProperty(DATA_SOURCE));
} else if (properties.containsKey(DATA_SOURCE)) {
//第二种方式
dataSource = (DataSource) initCtx.lookup(properties
.getProperty(DATA_SOURCE));
}
} catch (NamingException e) {
throw new DataSourceException(
"There was an error configuring JndiDataSourceTransactionPool. Cause: " + e, e);
}
}
/**
* 直接返回数据源
* 因为数据源交由服务器托管,因此mybatis不需要再像pooled类型那样自己实现连接池并通过动态代理增强java.sql.Connection
*/
public DataSource getDataSource() {
return dataSource;
}
// 如果配置文件中配置的JNDI属性以"env."开头,那么就新建一个properties
// 最后再把"evn."去掉放入properties中
// 现在两个properties一模一样了,只是构造InitalContext的时候不同
// 这个感觉是比较鸡肋的方法,也许是javax.naming中有优化吧
private static Properties getEnvProperties(Properties allProps) {
final String PREFIX = ENV_PREFIX;
Properties contextProperties = null;
for (Entry<Object, Object> entry : allProps.entrySet()) {
String key = (String) entry.getKey();
String value = (String) entry.getValue();
if (key.startsWith(PREFIX)) {
if (contextProperties == null) {
contextProperties = new Properties();
}
contextProperties.put(key.substring(PREFIX.length()), value);
}
}
return contextProperties;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
2. 代码范例
MyBatis中使用tomcat的JNDI服务,操作步骤如下:
A. 导入jar包到tomat lib目录中,数据库驱动包需要导入,如果使用DBCP则无需导入(tomcat已经集成了),但是使用其他数据源技术则需要导入;
B. 配置/META-INF/context.xml文件,在tomcat中注册JNDI服务,配置文件内容和第一节tomcat jndi一模一样,不需要任何变化,配置项的详细解释请看第一节。
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Resource name="jdbc/mybatis-jndi" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000" username="root"
password="51NByes!" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/javadb" />
</Context>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
C. 配置mybatis-config.xml文件,告诉mybatis启用JNDI类型数据源,并将注册服务的名称以及对应服务器的JNDI目录注入mybatis JNDI工厂类中,完成注册。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="JNDI">
<property name="data_source" value="jdbc/mybatis-jndi"/>
<property name="initial_context" value="java:/comp/env"/>
</dataSource>
</environment>
</environments>
</configuration>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
细节:
a. 数据源无需手动获取,最后会被注入到SqlSessionFactory中,对使用mybatis的程序员来说透明,只需要以上三个步骤即可;
b. 如果想手动验证是否配置成功,除了直接使用SqlSessionFactory进行操作验证之外,本处还提供另一种方式直接获取数据源,在mybatis-config.xml中两种配置方式检测代码以及检测结果截图如下:
第一种方式
第二种方式
【MyBatis】MyBatis Tomcat JNDI原理及源码分析的更多相关文章
- Mybatis 懒加载使用及源码分析
Mybatis 懒加载的使用 什么是懒加载?懒加载的意思就是在使用的时候才去加载,不使用不去加载,相反的就叫饥饿加载或者立即加载.懒加载在Mybatis中一般是存在与联合查询的情况,比如查询一个对象的 ...
- Tomcat处理HTTP请求源码分析(下)
转载:http://www.infoq.com/cn/articles/zh-tomcat-http-request-2 很多开源应用服务器都是集成tomcat作为web container的,而且对 ...
- OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波
http://blog.csdn.net/chenyusiyuan/article/details/8710462 OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波 201 ...
- ConcurrentHashMap实现原理及源码分析
ConcurrentHashMap实现原理 ConcurrentHashMap源码分析 总结 ConcurrentHashMap是Java并发包中提供的一个线程安全且高效的HashMap实现(若对Ha ...
- HashMap和ConcurrentHashMap实现原理及源码分析
HashMap实现原理及源码分析 哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表, ...
- (转)ReentrantLock实现原理及源码分析
背景:ReetrantLock底层是基于AQS实现的(CAS+CHL),有公平和非公平两种区别. 这种底层机制,很有必要通过跟踪源码来进行分析. 参考 ReentrantLock实现原理及源码分析 源 ...
- 【转】HashMap实现原理及源码分析
哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景极其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出 ...
- 【OpenCV】SIFT原理与源码分析:DoG尺度空间构造
原文地址:http://blog.csdn.net/xiaowei_cqu/article/details/8067881 尺度空间理论 自然界中的物体随着观测尺度不同有不同的表现形态.例如我们形 ...
- 《深入探索Netty原理及源码分析》文集小结
<深入探索Netty原理及源码分析>文集小结 https://www.jianshu.com/p/239a196152de
随机推荐
- ASP.NET Core 依赖注入(DI)
ASP.NET Core的底层设计支持和使用依赖注入.ASP.NET Core 应用程序可以利用内置的框架服务将服务注入到启动类的方法中,并且应用程序服务也可以配置注入.由ASP.NET Core 提 ...
- python 生成器 generator
一.生成器定义 通过列表生成表达式,我们可以直接创建一个列表.但是,受到内存限制,列表容量肯定是有限的.所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢? ...
- 前端代码规范(转载 http://codeguide.bootcss.com/)
http://codeguide.bootcss.com/ HTML 语法 HTML5 doctype 语言属性(Language attribute) 字符编码 IE 兼容模式 引入 CSS 和 J ...
- php 获得上周数据
$lastMondy = date('Y-m-d', strtotime('-2 sunday +1 days', time()));$lastSundy = date('Y-m-d', strtot ...
- Python函数和面向对象题目
- 137 Single Number II 数组中除了一个数外,其他的数都出现了三次,找出这个只出现一次的数
给定一个整型数组,除了一个元素只出现一次外,其余每个元素都出现了三次.求出那个只出现一次的数.注意:你的算法应该具有线性的时间复杂度.你能否不使用额外的内存来实现?详见:https://leetcod ...
- 为RecyclerView打造通用Adapter 让RecyclerView更加好用
原文出处: 张鸿洋 (Granker,@鸿洋_ ) 一.概述 记得好久以前针对ListView类控件写过一篇打造万能的ListView GridView 适配器,如今RecyclerView异军突起, ...
- RAID 0、1、5、1+0总结
RAID(Redundant Array Of Independent Disk,独立磁盘冗余阵列),可以提供比普通磁盘更快的速度.更高的安全性,生产环境中服务器在安装时一般都会做RAID,RAID的 ...
- 访问github.com太慢的解决方法
修改 c:\windows\system32\drivers\etc\host文件添加 192.30.255.112 github.com 151.101.72.249 github.global.s ...
- 通过流传入excel解析的问题
做了个excel文件导入的功能,接收excel文件流,先读取文件流头部信息确定文件类型,然后调用poi方法 OPCPackage pkg = OPCPackage.open(inputStream) ...