【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中两种配置方式检测代码以及检测结果截图如下:

第一种方式 
 
第二种方式 

时间: 2024-10-27 06:28:29

【MyBatis】MyBatis Tomcat JNDI原理及源码分析的相关文章

【Spring】Spring&amp;WEB整合原理及源码分析

表现层和业务层整合: 1. Jsp/Servlet整合Spring: 2. Spring MVC整合SPring: 3. Struts2整合Spring: 本文主要介绍Jsp/Servlet整合Spring原理及源码分析. 一.整合过程 Spring&WEB整合,主要介绍的是Jsp/Servlet容器和Spring整合的过程,当然,这个过程是Spring MVC或Strugs2整合Spring的基础. Spring和Jsp/Servlet整合操作很简单,使用也很简单,按部就班花不到2分钟就搞定了

【Spring】Spring&amp;WEB整合原理及源码分析(二)

一.整合过程 Spring&WEB整合,主要介绍的是Jsp/Servlet容器和Spring整合的过程,当然,这个过程是Spring MVC或Strugs2整合Spring的基础. Spring和Jsp/Servlet整合操作很简单,使用也很简单,按部就班花不到2分钟就搞定了,本节只讲操作不讲原理,更多细节.原理及源码分析后续过程陆续涉及. 1. 导入必须的jar包,本例spring-web-x.x.x.RELEASE.jar: 2. 配置web.xml,本例示例如下: <?xml vers

ConcurrentHashMap实现原理及源码分析

ConcurrentHashMap实现原理 ConcurrentHashMap源码分析 总结 ConcurrentHashMap是Java并发包中提供的一个线程安全且高效的HashMap实现(若对HashMap的实现原理还不甚了解,可参考我的另一篇文章HashMap实现原理及源码分析),ConcurrentHashMap在并发编程的场景中使用频率非常之高,本文就来分析下ConcurrentHashMap的实现原理,并对其实现原理进行分析(JDK1.7). ConcurrentHashMap实现原

OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波

http://blog.csdn.net/chenyusiyuan/article/details/8710462 OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波 2013-03-23 17:44 16963人阅读 评论(28) 收藏 举报 分类: 机器视觉(34) 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] KAZE系列笔记: OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波 OpenCV学习笔记(28)KA

caffe中HingeLossLayer层原理以及源码分析

输入: bottom[0]: NxKx1x1维,N为样本个数,K为类别数.是预测值. bottom[1]: Nx1x1x1维, N为样本个数,类别为K时,每个元素的取值范围为[0,1,2,-,K-1].是groundTruth. 输出: top[0]: 1x1x1x1维, 求得是hingeLoss. 关于HingeLoss: p: 范数,默认是L1范数,可以在配置中设置为L1或者L2范数. :指示函数,如果第n个样本的真实label为k,则为,否则为-1. tnk: bottom[0]中第n个样

深度理解Android InstantRun原理以及源码分析

深度理解Android InstantRun原理以及源码分析 @Author 莫川 Instant Run官方介绍 简单介绍一下Instant Run,它是Android Studio2.0以后新增的一个运行机制,能够显著减少你第二次及以后的构建和部署时间.简单通俗的解释就是,当你在Android Studio中改了你的代码,Instant Run可以很快的让你看到你修改的效果.而在没有Instant Run之前,你的一个小小的修改,都肯能需要几十秒甚至更长的等待才能看到修改后的效果. 传统的代

【OpenCV】SIFT原理与源码分析:关键点描述

<SIFT原理与源码分析>系列文章索引:http://www.cnblogs.com/tianyalu/p/5467813.html 由前一篇<方向赋值>,为找到的关键点即SIFT特征点赋了值,包含位置.尺度和方向的信息.接下来的步骤是关键点描述,即用用一组向量将这个关键点描述出来,这个描述子不但包括关键点,也包括关键点周围对其有贡献的像素点.用来作为目标匹配的依据(所以描述子应该有较高的独特性,以保证匹配率),也可使关键点具有更多的不变特性,如光照变化.3D视点变化等. SIFT

【OpenCV】SIFT原理与源码分析:方向赋值

<SIFT原理与源码分析>系列文章索引:http://www.cnblogs.com/tianyalu/p/5467813.html 由前一篇<关键点搜索与定位>,我们已经找到了关键点.为了实现图像旋转不变性,需要根据检测到的关键点局部图像结构为特征点方向赋值.也就是在findScaleSpaceExtrema()函数里看到的alcOrientationHist()语句: // 计算梯度直方图 float omax = calcOrientationHist(gauss_pyr[o

【OpenCV】SIFT原理与源码分析:关键点搜索与定位

<SIFT原理与源码分析>系列文章索引:http://www.cnblogs.com/tianyalu/p/5467813.html 由前一步<DoG尺度空间构造>,我们得到了DoG高斯差分金字塔: 如上图的金字塔,高斯尺度空间金字塔中每组有五层不同尺度图像,相邻两层相减得到四层DoG结果.关键点搜索就在这四层DoG图像上寻找局部极值点. DoG局部极值点 寻找DoG极值点时,每一个像素点和它所有的相邻点比较,当其大于(或小于)它的图像域和尺度域的所有相邻点时,即为极值点.如下图所