【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="密碼" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/javadb" />
</Context>
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();
總結 使用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;
}
}
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>
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>
細節:
a. 資料來源無需手動獲取,最後會被注入到SqlSessionFactory中,對使用mybatis的程式設計師來說透明,只需要以上三個步驟即可;
b. 如果想手動驗證是否配置成功,除了直接使用SqlSessionFactory進行操作驗證之外,本處還提供另一種方式直接獲取資料來源,在mybatis-config.xml中兩種配置方式檢測程式碼以及檢測結果截圖如下:
第一種方式
第二種方式
附註:
本文如有錯漏,煩請不吝指正,謝謝!
相關推薦
【MyBatis】MyBatis Tomcat JNDI原理及原始碼分析
一、 Tomcat JNDI JNDI(java nameing and drectory interface),是一組在Java應用中訪問命名和服務的API,所謂命名服務,即將物件和名稱聯絡起來,使得可以通過名稱訪問並獲取物件。 簡單
【java基礎】ConcurrentHashMap實現原理及原始碼分析
ConcurrentHashMap是Java併發包中提供的一個執行緒安全且高效的HashMap實現(若對HashMap的實現原理還不甚瞭解,可參考我的另一篇文章),ConcurrentHashMap在併發程式設計的場景中使用頻率非常之高,本文就來分析下Concurre
【Java】Java8 HashMap工作原理及實現
1 、概述 從本文你可以學到 什麼時候會使用HashMap?他有什麼特點? 你知道HashMap的工作原理嗎? 你知道get和put的原理嗎?equals()和hashCode()的都有什麼作用? 你知道hash的實現嗎?
【NLP】CNN文字分類原理及python程式碼實現
CNN分類模型架構 python程式碼實現: #!/usr/bin/python # -*- coding: utf-8 -*- import tensorflow as tf class TCNNConfig(object): #class TCNNConfig(
【原】高清顯示屏原理及設計方案
接近年底了,又到產品們趕KPI的時間,開發也跟著辛苦,於是連續加班了4個星期,專案總算有點起色,也終於擠出點時間,寫篇文章,just for fun ~ 高清顯示屏原理 ,之前在團隊內做過的一個類似的分享,因為上次有園友問了我 手機端css sprite 的設計原理,不知道手機端的圖片為什麼是用2倍大,背景
HashMap實現原理及原始碼分析(轉載)
作者: dreamcatcher-cx 出處: <http://www.cnblogs.com/chengxiao/> 雜湊表(hash table)也叫散列表,是一種非常重要的資料結構,應用場景及其豐富,
併發程式設計(三)—— ReentrantLock實現原理及原始碼分析
ReentrantLock是Java併發包中提供的一個可重入的互斥鎖。ReentrantLock和synchronized在基本用法,行為語義上都是類似的,同樣都具有可重入性。只不過相比原生的Synchronized,ReentrantLock增加了一些高階的擴充套件功能,比如它可以實現公平鎖,同時也可以
HashMap、ConcurrentHashMap實現原理及原始碼分析
HashMap:https://www.cnblogs.com/chengxiao/p/6059914.html ConcurrentHashMap:https://blog.csdn.net/dingjianmin/article/details/79776646 遺留問
ConcurrentHashMap JDK1.8中結構原理及原始碼分析
注:本文根據網路和部分書籍整理基於JDK1.7書寫,如有雷同敬請諒解 歡迎指正文中的錯誤之處。 資料結構 ConcurrentHashMap 1.8 拋棄了Segment分段鎖機制,採用Node + CAS + Synchronized來保證併發安全進行實現
HashMap實現原理及原始碼分析
雜湊表(hash table)也叫散列表,是一種非常重要的資料結構,應用場景及其豐富,許多快取技術(比如memcached)的核心其實就是在記憶體中維護一張大的雜湊表,而HashMap的實現原理也常常出現在各類的面試題中,重要性可見一斑。本文會對java集合框架中的對
ConcurrentHashMap實現原理及原始碼分析
ConcurrentHashMap是Java併發包中提供的一個執行緒安全且高效的HashMap實現(若對HashMap的實現原理還不甚瞭解,可參考我的另一篇文章),ConcurrentHashMap在併發程式設計的場景中使用頻率非常之高,本文就來分析下Concurrent
[轉]HashMap實現原理及原始碼分析
目錄: 一、什麼是雜湊表 二、HashMap實現原理 三、為何HashMap的陣列長度一定是2的次冪? 四、為什麼重寫equals方法需同時重寫hashCode方法 一、什麼是雜湊表 雜湊表(hash table)也叫散列表,是一
(轉)HashMap實現原理及原始碼分析
雜湊表(hash table)也叫散列表,是一種非常重要的資料結構,應用場景及其豐富,許多快取技術(比如memcached)的核心其實就是在記憶體中維護一張大的雜湊表,而HashMap的實現原理也常常出現在各類的面試題中,重要性可見一斑。本文會對java集合框架中的對應實
HashMap, ConcurrentHashMap 最詳細的原理及原始碼分析
網上關於 HashMap 和 ConcurrentHashMap 的文章確實不少,不過缺斤少兩的文章比較多,所以才想自己也寫一篇,把細節說清楚說透,尤其像 Java8 中的 ConcurrentHashMap,大部分文章都說不清楚。 終歸是希望能降低大家學習的成本,不希望大家到處找各種不是很
JDK8中的HashMap實現原理及原始碼分析
本篇所述原始碼基於JDK1.8.0_121 在寫上一篇線性表的文章的時候,筆者看的是Android原始碼中support24中的Java程式碼,當時發現這個ArrayList和LinkedList的原始碼和Java官方的沒有什麼區別,然而在閱讀HashMap原
springmvc工作原理及原始碼分析
一、JavaEE體系結構 二、mvc 設計模式|思想 Model 模型層 (javaBean元件 = 領域模型(javaBean) + 業務層 + 持久層) View 檢視層( html、jsp…) Controller 控制層(委託模型層進行資料處理) 複製程式
技術分享:springmvc工作原理及原始碼分析
一、JavaEE體系結構 二、mvc 設計模式|思想 Model 模型層 (javaBean元件 = 領域模型(javaBean) + 業務層 + 持久層) View
ReentrantLock實現原理及原始碼分析
ReentrantLock實現原理及原始碼分析 ReentrantLock是Java併發包中提供的一個可重入的互斥鎖。ReentrantLock和synchronized在基本用法,行為語
【Python】實現網站備份檔案掃描+原始碼分析
一開始我用的requests庫的get方法 但是這種方法會自動下載檔案,所以不可取 後來發現urllib2的庫相對來說不錯 原始碼如下# coding = utf-8 import urllib2 i
【6】以太坊Miner模組原始碼分析
Miner模組: 類結構如下: 主要結構體 Miner、Worker、Work、CpuAgent、Result、unconfiredBlocks Miner為挖礦的核心物件(礦工),核心成員包括註冊處理事件(mux)、工人(worker)、coinbase(預設賬戶地址hash