# Mybatis深入之DataSource實例化過程
### 簡介
主要介紹Mybatis啟動過程中DataSource實例化的過程、為后面解析一個完整SQL執行過程做個前章。
### Mybatis中DataSource體系
### MybatisDataSource整體簡介
Mybatis中關于數據庫的類都在`org.apache.ibatis.datasource`包中

Mybatis配置文件中關于數據庫的配置:
~~~
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
~~~
- 重點關注`<dataSource type="POOLED">`的type屬性、其有三種取值:
- POOLED:使用Mybatis自帶的數據庫連接池來管理數據庫連接
- UNPOOLED:不使用任何數據庫連接池來管理數據庫連接
- JNDI:jndi形式使用數據庫連接、主要用于項目正常使用的時候
類與類之間的關系:

每一條線都是一種關系、簡單解釋一下
1. PooledDataSource實現java.sql.DataSource接口
2. PooledDataSource內部持有一個DataSource引用
3. UnpooledDataSource實現java.sql.DataSource接口
4. PooledDataSource內部持有一個UnpooledDataSource引用
5.PooledDataSourceFactory無參構造方法體中將其父類UnpooledDataSourceFactory持有的引用DataSource實例化為PooledDataSource
6. PooledDataSourceFactory繼承UnpooledDataSourceFactory
7. UnpooledDataSourceFactory無參構造方法將其持有的引用DataSource實例化為UnpooledDataSource
8. UnpooledDataSourceFactory持有一個DataSource引用、用于返回實例化好的DataSource。
### Mybatis中DataSource實例化整體過程
這里以使用Mybatis自帶的數據庫連接池為例。也就是type為 “POOLED”類型的數據連接。
1. 根據配置文件中type的類型實例化具體的DataSourceFactory。這里是POOLED所以實例化的是PooledDataSourceFactory。
1. 通過PooledDataSourceFactory來獲取DataSource具體實例:PooledDataSource
對于第一步更詳細點的過程:
- 在Mybatis初始化Configuration對象時、Configuration中屬性TypeAliasRegistry同樣被實例化、并且在Configuration的無參構造方法中對TypeAliasRegistry注冊了許多常用的類(以鍵值對的形式保存在TypeAliasRegistry的屬性`private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();`中、也包括TypeAliasRegistry無參構造方法注冊的基本java類型。
- 通過配置文件中type指定的”POOLED”在TypeAliasRegistry中查找其對應的類:PooledDataSourceFactory
- 調用其newInstance()實例化
- PooledDataSourceFactory繼承自UnpooledDataSourceFactory、所以UnpooledDataSourceFactory先被實例化、
- UnpooledDataSourceFactory無參構造方法中實例化了其DataSource引用為UnpooledDataSource。
- 接著實例化PooledDataSourceFactory、其無參構造方法體將父類UnpooledDataSourceFactory持有的DataSource實例化為PooledDataSource。
- PooledDataSource實例化時初始化了一些關于數據庫連接池的配置信息
- PooledDataSource的無參構造方法中將其持有的UnpooledDataSource實例化。
- UnpooledDataSource中關于數據庫連接的屬性值在實例化DataSourceFactory之后讀取properties值設置到對應屬性上。
### 具體過程
從上一篇中知道Mybatis初始化過程是解析Mybatis配置文件并裝配Configuration對象。從Mybatis基礎使用中知道Mybatis數據庫連接信息的配置是在environments標簽中配置的:
~~~
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
~~~
所以想要了解DataSource初始化過程可以從XMLConfigBuilder中的parse方法入手:
~~~
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
if (isSpecifiedEnvironment(id)) {
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
~~~
- 這里同樣有關于數據庫事務的配置、具體事務有關的后面再說
-
主要看如何實例化DataSource、同樣從上面代碼中我們知道只是將DataSource實例化了而沒有進行任何操作、原因是只有具體執行某SQL語句的時候才會使用DataSource來獲取數據庫連接。
獲取DataSource關鍵代碼:
~~~
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
~~~
- 根據配置文件中dataSource標簽中內容實例化DataSourceFactory
- 通過DataSourceFactory獲取DataSource
下面首先看如何根據dataSource標簽內容實例化DataSourceFactory
~~~
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
//獲取數據庫連接池類型: POOLED-使用Mybatis自帶數據庫連接池。UNPOOL-不使用數據庫連接池。這里看POOLED的情況。
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
~~~
- 上面一段代碼關鍵點在于`resolveClass(type)`
- 經過一系列的方法調用、最終返回結果的方法是:`TypeAliasRegistry`
~~~
@SuppressWarnings("unchecked")
// throws class cast exception as well if types cannot be assigned
public <T> Class<T> resolveAlias(String string) {
try {
if (string == null) return null;
String key = string.toLowerCase(Locale.ENGLISH); // issue #748
Class<T> value;
if (TYPE_ALIASES.containsKey(key)) {
value = (Class<T>) TYPE_ALIASES.get(key);
} else {
value = (Class<T>) Resources.classForName(string);
}
return value;
} catch (ClassNotFoundException e) {
throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + e, e);
}
}
~~~
- 這里重點在于TypeAliasRegistry是何時實例化的
- 上一篇初始化過程中知道Configuration中有一個私有變量`protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();`
- 進一步看看Configuration實例化的時候其無參構造函數體就知道其緣由
~~~
public Configuration() {
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class);
typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
languageRegistry.register(RawLanguageDriver.class);
}
~~~
- 從上面可以看出TypeAliasRegistry在實例化之后并初始化了其內部私有變量`private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();`來保存一些Type alias(類型別名)供后面程序使用。
從上面代碼`typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);`可以看出最后返回的是`PooledDataSourceFactory`。
~~~
public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
public PooledDataSourceFactory() {
this.dataSource = new PooledDataSource();
}
}
~~~
- PooledDataSourceFactory繼承UnpooledDataSourceFactory
其中UnpooledDataSourceFactory擁有一個DataSource的protected級別的屬性`protected DataSource dataSource;`并且其構造函數:
~~~
public UnpooledDataSourceFactory() {
this.dataSource = new UnpooledDataSource();
}
~~~
對比PooledDataSourceFactory的構造函數:
~~~
public PooledDataSourceFactory() {
this.dataSource = new PooledDataSource();
}
~~~
知道最終UnpooledDataSourceFactory的`protected DataSource dataSource;`實例是:`PooledDataSource`。
到這里只要知道PooledDataSource是什么、那么返回的DataSource就是什么。
當然在這之前還需要一步、就是將配置文件中的數據庫連接信息設置到最后生成的DataSourceFactory(在這里就是PooledDataSourceFactory)中去。這個過程中使用了一個Mybatis很長用的用于操作反射的封裝類:MetaObject。提供了一些簡便的獲取、設置類等通過反射來操作類的方法、以后有時間專門看一眼。
下面的主要目標就是看PooledDataSource調用其無參構造方法時到底做了什么。
PooledDataSource是java.sql.DataSource的一個實現類、其屬性與無參構造方法如下:
~~~
private static final Log log = LogFactory.getLog(PooledDataSource.class);
private final PoolState state = new PoolState(this);
private final UnpooledDataSource dataSource;
// OPTIONAL CONFIGURATION FIELDS
protected int poolMaximumActiveConnections = 10;
protected int poolMaximumIdleConnections = 5;
protected int poolMaximumCheckoutTime = 20000;
protected int poolTimeToWait = 20000;
protected String poolPingQuery = "NO PING QUERY SET";
protected boolean poolPingEnabled = false;
protected int poolPingConnectionsNotUsedFor = 0;
private int expectedConnectionTypeCode;
public PooledDataSource() {
dataSource = new UnpooledDataSource();
}
~~~
- 設置了一些作為數據庫連接池初始化使用的參數
- 無參方法體中實例話了屬性`dataSource = new UnpooledDataSource();`
- UnpooledDataSource無參構造函數是空方法題
- 要注意的是:前面實例化DataSourceFactory的時候最后一步是設置屬性。其實就是通過MetaObject來將屬性值設置到UnpooledDataSource的數據庫連接屬性上了。
到這里、關于數據庫DataSource類的實例話也就結束了。
### 補充
### 對數據庫實例化做個總結:
當使用數據庫連接池時、即`<dataSource type="POOLED">`時、DataSourceFactory具體實例是PooledDataSourceFactory。返回的DataSource具體實例是內部持有UnpooledDataSource實例的PooledDataSource。
當不使用數據庫連接池時、即`<dataSource type="UNPOOLED">` 時、DataSourceFactory具體實例是UnpooledDataSourceFactory。返回的DataSource具體實例是UnpooledDataSource實例。
更多內容:[Mybatis 目錄](http://blog.csdn.net/crave_shy/article/details/45825599)