原創(chuàng)作品,可以轉(zhuǎn)載,但是請(qǐng)標(biāo)注出處地址:http://www.cnblogs.com/V1haoge/p/6675633.html
1 回顧
上一篇中我解說(shuō)了數(shù)據(jù)源接口DataSource與數(shù)據(jù)源工廠接口DataSourceFactory,這二者是MyBatis數(shù)據(jù)源模塊的基礎(chǔ),包括本文中的非池型非池型數(shù)據(jù)源(UnpooledDataSource)和之后的池型數(shù)據(jù)源(PooledDataSource)、托管型數(shù)據(jù)源(JndiDataSourceFactory)都是在這兩個(gè)接口上產(chǎn)生的。
本文解讀一下MyBatis中的非池型數(shù)據(jù)源,這是基礎(chǔ)的數(shù)據(jù)源,之后要解讀的池型數(shù)據(jù)源又是以此數(shù)據(jù)源為基礎(chǔ)產(chǎn)生的。
2 非池型數(shù)據(jù)源及其工廠
2.1 非池型數(shù)據(jù)源工廠:UnpooledDataSourceFactory
該數(shù)據(jù)源工廠實(shí)現(xiàn)了DataSourceFactory接口,源碼如下:
1 package org.apache.ibatis.datasource.unpooled; 2 import java.util.Iterator; 3 import java.util.Properties; 4 import java.util.Set; 5 import javax.sql.DataSource; 6 import org.apache.ibatis.datasource.DataSourceException; 7 import org.apache.ibatis.datasource.DataSourceFactory; 8 import org.apache.ibatis.reflection.MetaObject; 9 import org.apache.ibatis.reflection.SystemMetaObject; 10 11 public class UnpooledDataSourceFactory implements DataSourceFactory{ 12 private static final String DRIVER_PROPERTY_PREFIX = "driver.";//屬性前綴 13 private static final int DRIVER_PROPERTY_PREFIX_LENGTH = "driver.".length();//屬性前綴的長(zhǎng)度 14 protected DataSource dataSource; 15 /* 16 * 在工廠的構(gòu)造器中創(chuàng)建具體數(shù)據(jù)源的實(shí)例并賦值,這將用于供getDataSource()方法獲取數(shù)據(jù)源實(shí)例 17 */ 18 public UnpooledDataSourceFactory() 19 { 20 this.dataSource = new UnpooledDataSource(); 21 } 22 /* 23 * 設(shè)置數(shù)據(jù)源驅(qū)動(dòng)器屬性,有兩種情況 24 */ 25 public void setProperties(Properties properties) 26 { 27 Properties driverProperties = new Properties(); 28 MetaObject metaDataSource = SystemMetaObject.forObject(this.dataSource); 29 for (Iterator i$ = properties.keySet().iterator(); i$.hasNext(); ) { Object key = i$.next(); 30 String propertyName = (String)key; 31 if (propertyName.startsWith("driver.")) {//第一種情況,以driver.開(kāi)頭的屬性名 32 String value = properties.getProperty(propertyName); 33 driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value); 34 } else if (metaDataSource.hasSetter(propertyName)) {//元對(duì)象中擁有針對(duì)屬性名的set設(shè)置方法 35 String value = (String)properties.get(propertyName); 36 Object convertedValue = convertValue(metaDataSource, propertyName, value); 37 metaDataSource.setValue(propertyName, convertedValue); 38 } else { 39 throw new DataSourceException("Unknown DataSource property: " + propertyName); 40 } 41 } 42 if (driverProperties.size() > 0) 43 metaDataSource.setValue("driverProperties", driverProperties); 44 } 45 46 public DataSource getDataSource() 47 { 48 return this.dataSource; 49 } 50 /* 51 * 值類(lèi)型強(qiáng)轉(zhuǎn)方法 52 */ 53 private Object convertValue(MetaObject metaDataSource, String propertyName, String value) { 54 Object convertedValue = value; 55 Class targetType = metaDataSource.getSetterType(propertyName); 56 if ((targetType == Integer.class) || (targetType == Integer.TYPE)) 57 convertedValue = Integer.valueOf(value); 58 else if ((targetType == Long.class) || (targetType == Long.TYPE)) 59 convertedValue = Long.valueOf(value); 60 else if ((targetType == Boolean.class) || (targetType == Boolean.TYPE)) { 61 convertedValue = Boolean.valueOf(value); 62 } 63 return convertedValue; 64 }
以上源碼中,數(shù)據(jù)源工廠可通過(guò)無(wú)參構(gòu)造器創(chuàng)建具體的數(shù)據(jù)源實(shí)例,然后可通過(guò)getDataSource()方法,來(lái)獲取之前在構(gòu)造器中創(chuàng)建的數(shù)據(jù)源實(shí)例,getDataSource()方法是對(duì)外的。
該工廠類(lèi)的重點(diǎn)是設(shè)置屬性的方法:setProperties(Properties properties),我們繼續(xù)簡(jiǎn)單解析:
第27行:創(chuàng)建局部變量driverProperties,用于存放參數(shù)屬性列表中經(jīng)過(guò)過(guò)濾的屬性
第28行:在我們創(chuàng)建該工廠實(shí)例的基礎(chǔ)上(即字段dataSource已被賦值的基礎(chǔ)上),調(diào)用MyBatis提供的元對(duì)象MetaObject來(lái)生成針對(duì)dataSource實(shí)例的元實(shí)例metaDataSource。
解析:元實(shí)例,可以看成是針對(duì)具體實(shí)例的一批裝飾器,在包裝器核心是具體實(shí)例,外圍是多個(gè)裝飾器(包裝器),用于增強(qiáng)功能。
第29行:遍歷獲取的屬性列表Properties(參數(shù))
第31-33行:針對(duì)參數(shù)列表中屬性名是以driver.為前綴的屬性,獲取其值,保存在driverProperties中以備后用。
第34-37行:針對(duì)參數(shù)列表中屬性名在dataSource實(shí)例中存在set方法的屬性,獲取其值,將值經(jīng)過(guò)必要的轉(zhuǎn)換后,將其保存到metaDataSource元實(shí)例中
這個(gè)其實(shí)是最常使用的,我們?cè)谂渲梦募信渲玫膁river、url、username、password四個(gè)參數(shù),全部都是以此種方式保存到dataSource實(shí)例中的。
第42-44行:最后對(duì)driverProperties變量進(jìn)行null判斷,即判斷有無(wú)通過(guò)前綴方式配置的屬性,如果有則將這些配置同樣保存到metaDataSource元實(shí)例中
這樣到最后其實(shí)所有的配置信息都保存到了metaDataSource元實(shí)例中,這樣說(shuō)其實(shí)不對(duì),其實(shí)最后通過(guò)MetaObject的setValue()方法,將所有這些經(jīng)過(guò)過(guò)濾的屬性設(shè)置保存到了元實(shí)例的核心:dataSource中了(內(nèi)部原理,榮后稟明),對(duì)應(yīng)于UnpolledDataSource中的driver、url、username、password、driverProperties這五個(gè)字段(見(jiàn)下文)。
總結(jié)來(lái)看看:數(shù)據(jù)源工廠的目的就是講配置文件中的配置內(nèi)容配置到通過(guò)自己的構(gòu)造器創(chuàng)建的具體數(shù)據(jù)源實(shí)例中,再使用getDataSource()方法返回給調(diào)用方。
2.2 非池型數(shù)據(jù)源:UnpolledDataSource
該類(lèi)實(shí)現(xiàn)了DataSource數(shù)據(jù)源接口,實(shí)現(xiàn)了其中的所有抽象方法。
非池型是相對(duì)池型而言的,池型數(shù)據(jù)源統(tǒng)籌管理著一個(gè)數(shù)據(jù)庫(kù)連接池,在這個(gè)池中擁有指定數(shù)量的數(shù)據(jù)庫(kù)連接實(shí)例connection可供使用,其內(nèi)部采用一定的規(guī)則指定數(shù)據(jù)連接對(duì)象的使用、創(chuàng)建、銷(xiāo)毀規(guī)則,來(lái)為多線程數(shù)據(jù)庫(kù)訪問(wèn)提供及時(shí)有效的數(shù)據(jù)庫(kù)連接。
非池型數(shù)據(jù)源,即保持有一個(gè)數(shù)據(jù)庫(kù)連接實(shí)例的數(shù)據(jù)源。下面我們來(lái)看看這種數(shù)據(jù)源的實(shí)現(xiàn)方式:
首先我們來(lái)看看字段:
1 private ClassLoader driverClassLoader; 2 private Properties driverProperties; 3 private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<String, Driver>(); 4 private String driver; 5 private String url; 6 private String username; 7 private String password; 8 private Boolean autoCommit; 9 private Integer defaultTransactionIsolationLevel;
我們一一進(jìn)行解析:
ClassLoader driverClassLoader:這個(gè)是數(shù)據(jù)庫(kù)的驅(qū)動(dòng)類(lèi)加載器:目的正是用于從磁盤(pán)中加載數(shù)據(jù)庫(kù)驅(qū)動(dòng)類(lèi)到內(nèi)存
Properties driverProperties:驅(qū)動(dòng)器屬性,這個(gè)用于保存我們手動(dòng)設(shè)置的數(shù)據(jù)庫(kù)驅(qū)動(dòng)器屬性,如果我們不進(jìn)行設(shè)置,則會(huì)使用默認(rèn)值。這個(gè)屬性設(shè)置的鍵一般都會(huì)是以“driver.”為前綴。
Map<String,Driver> registeredDrivers = new ConcurrentHashMap<String,Driver>():表示數(shù)據(jù)庫(kù)驅(qū)動(dòng)注冊(cè)器,其內(nèi)部保存著所有已注冊(cè)的數(shù)據(jù)庫(kù)驅(qū)動(dòng)類(lèi)實(shí)例,這個(gè)字段是static修飾的,表示在數(shù)據(jù)源類(lèi)加載的時(shí)候就會(huì)創(chuàng)建,這個(gè)時(shí)候創(chuàng)建的其實(shí)是個(gè)空集合。再者使用ConcurrentHashMap集合,這是一個(gè)線程安全的鍵值對(duì)型集合,它幾乎可以與HashTable通用(二者都是線程安全的集合類(lèi))。這個(gè)靜態(tài)字段其實(shí)是和之后要提到的一個(gè)靜態(tài)代碼塊配合工作的(容后介紹)。
String driver:數(shù)據(jù)庫(kù)驅(qū)動(dòng)類(lèi)名
String url:數(shù)據(jù)庫(kù)服務(wù)器URL地址
String username:數(shù)據(jù)庫(kù)服務(wù)器連接用戶名
String password:數(shù)據(jù)庫(kù)服務(wù)器連接密碼
Boolean autoCommit:是否自動(dòng)提交,這是一個(gè)邏輯值,真表示使用自動(dòng)提交,假表示關(guān)閉自動(dòng)提交。上一文中提到了自動(dòng)提交,其實(shí)那里的自動(dòng)提交值就是在這里設(shè)置的。
Integer defaultTransactionIsolationLevel:表示默認(rèn)的事務(wù)級(jí)別
靜態(tài)代碼塊:
1 static { 2 Enumeration<Driver> drivers = DriverManager.getDrivers(); 3 while (drivers.hasMoreElements()) { 4 Driver driver = drivers.nextElement(); 5 registeredDrivers.put(driver.getClass().getName(), driver); 6 } 7 }
這個(gè)靜態(tài)塊中的內(nèi)容需要結(jié)合之前的靜態(tài)字段registeredDrivers來(lái)進(jìn)行解析。
DriverManager是JDK提供的驅(qū)動(dòng)器管理類(lèi),在該類(lèi)加載時(shí)會(huì)執(zhí)行其中的靜態(tài)塊,靜態(tài)塊中調(diào)用了一個(gè)loadInitialDrivers()方法,這是一個(gè)加載原始驅(qū)動(dòng)器的方法,他將JDK中的原始配置jdbc.drivers中的驅(qū)動(dòng)器名表示的驅(qū)動(dòng)類(lèi)加載到內(nèi)存,其會(huì)調(diào)用本地方法進(jìn)行驅(qū)動(dòng)類(lèi)進(jìn)行加載,然后調(diào)用DriverManager中的registerDriver()方法將驅(qū)動(dòng)類(lèi)實(shí)例一一保存到DriverManager中的registeredDrivers集合中。
我們這里調(diào)用DriverManager中g(shù)etDrivers()方法,將會(huì)獲取DriverManager中在集合registeredDrivers中的保存的驅(qū)動(dòng)器實(shí)例,在獲取的時(shí)候會(huì)進(jìn)行類(lèi)加載驗(yàn)證,驗(yàn)證的目的是確保使用本類(lèi)加載器獲取的驅(qū)動(dòng)器類(lèi)與在registeredDrivers中保存的對(duì)應(yīng)驅(qū)動(dòng)類(lèi)實(shí)例的類(lèi)是同一類(lèi)型(==)。最后獲取到的是驅(qū)動(dòng)實(shí)例的枚舉。
對(duì)這個(gè)枚舉進(jìn)行遍歷,將其中所有驅(qū)動(dòng)器實(shí)例保存到我們當(dāng)前的UnpooledDataSource中的靜態(tài)集合registeredDrivers(區(qū)別于DriverManager中的同名集合,二者類(lèi)型都不同)中。
這樣保證在該類(lèi)加載的時(shí)候就將默認(rèn)的驅(qū)動(dòng)器實(shí)例加載到靜態(tài)集合中以備用。該靜態(tài)代碼塊的作用就是為registeredDrivers集合賦值。
下面我們來(lái)看看實(shí)現(xiàn)的DataSource中的構(gòu)造方法:
1 public UnpooledDataSource() { 2 } 3 public UnpooledDataSource(String driver, String url, String username, String password) { 4 this.driver = driver; 5 this.url = url; 6 this.username = username; 7 this.password = password; 8 } 9 public UnpooledDataSource(String driver, String url, Properties driverProperties) { 10 this.driver = driver; 11 this.url = url; 12 this.driverProperties = driverProperties; 13 } 14 public UnpooledDataSource(ClassLoader driverClassLoader, String driver, String url, String username, String password) { 15 this.driverClassLoader = driverClassLoader; 16 this.driver = driver; 17 this.url = url; 18 this.username = username; 19 this.password = password; 20 } 21 public UnpooledDataSource(ClassLoader driverClassLoader, String driver, String url, Properties driverProperties) { 22 this.driverClassLoader = driverClassLoader; 23 this.driver = driver; 24 this.url = url; 25 this.driverProperties = driverProperties; 26 }
可見(jiàn)UnpooledDataSource提供了五個(gè)構(gòu)造器,第一個(gè)為無(wú)參構(gòu)造器,其余皆帶有賦值功能:以驅(qū)動(dòng)器類(lèi)全限定名driver、數(shù)據(jù)庫(kù)服務(wù)器地址url、數(shù)據(jù)庫(kù)登錄用戶名及密碼為參數(shù)是最為常用的數(shù)據(jù)源構(gòu)建方式,也可以將用戶名與密碼整合為以驅(qū)動(dòng)器參數(shù)driverProperties的形式傳參,甚至還可以指定驅(qū)動(dòng)類(lèi)的類(lèi)加載器driverClassLoader。
這里最常用的還是無(wú)參構(gòu)造器,這個(gè)構(gòu)造器在數(shù)據(jù)源工廠的無(wú)參構(gòu)造器中被調(diào)用,用于創(chuàng)建一個(gè)空的UnpolledDataSource實(shí)例,然后使用工廠類(lèi)中的setProperties()方法,為這個(gè)空實(shí)例中的各個(gè)字段進(jìn)行賦值(采用上面提到的第二種方式進(jìn)行讀取配置信息并保存到實(shí)例中),從而創(chuàng)建一個(gè)飽滿的實(shí)例,
下面看看實(shí)現(xiàn)自接口的方法
1 public void setLoginTimeout(int loginTimeout) throws SQLException 2 { 3 DriverManager.setLoginTimeout(loginTimeout); 4 } 5 public int getLoginTimeout() throws SQLException 6 { 7 return DriverManager.getLoginTimeout(); 8 } 9 public void setLogWriter(PrintWriter logWriter) throws SQLException 10 { 11 DriverManager.setLogWriter(logWriter); 12 } 13 public PrintWriter getLogWriter() throws SQLException 14 { 15 return DriverManager.getLogWriter(); 16 } 17 public Logger getParentLogger() 18 { 19 return Logger.getLogger("global"); 20 }
這五個(gè)方法繼承自CommonDataSource通用數(shù)據(jù)源接口,其中各方法的意義如下:
setLoginTimeout():表示設(shè)置數(shù)據(jù)源連接數(shù)據(jù)庫(kù)的最長(zhǎng)等待時(shí)間,以秒為單位
getLoginTimeout():表示獲取數(shù)據(jù)源連接到數(shù)據(jù)庫(kù)的最長(zhǎng)等待時(shí)間
setLogWriter():設(shè)置數(shù)據(jù)源的日志輸出者(log writer)為給定的一個(gè)PrintWriter實(shí)例
getLogWriter():獲取數(shù)據(jù)源的日志輸出者。
通過(guò)上面的源碼可以發(fā)現(xiàn),數(shù)據(jù)源類(lèi)中的這些方法的實(shí)現(xiàn)都是直接調(diào)用的DriverManager的對(duì)應(yīng)方法。
getParentLogger():獲取這個(gè)DataSource所使用的所有Logger的父Logger。
下面是UnpooledDataSource中最重要的方法:
1 @Override 2 public Connection getConnection() throws SQLException { 3 return doGetConnection(username, password); 4 } 5 @Override 6 public Connection getConnection(String username, String password) throws SQLException { 7 return doGetConnection(username, password); 8 }
這是兩個(gè)獲取數(shù)據(jù)源連接的方法,第一個(gè)是無(wú)參方法,它內(nèi)部使用默認(rèn)的用戶名與面目進(jìn)行數(shù)據(jù)庫(kù)連接,第二個(gè)提供了指定的用戶名與密碼,使用指定的用戶名與密碼進(jìn)行數(shù)據(jù)庫(kù)連接,并將該連接返回。二者內(nèi)部都調(diào)用了同一個(gè)方法doGetConnection(),這是真正執(zhí)行數(shù)據(jù)庫(kù)連接并獲取這個(gè)連接的方法。
1 private Connection doGetConnection(String username, String password) throws SQLException { 2 Properties props = new Properties(); 3 if (driverProperties != null) { 4 props.putAll(driverProperties); 5 } 6 if (username != null) { 7 props.setProperty("user", username); 8 } 9 if (password != null) { 10 props.setProperty("password", password); 11 } 12 return doGetConnection(props); 13 }
http://www.cnblogs.com/V1haoge/p/6675633.html