原創(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接口,源碼如下:

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

 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ì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

  以上源碼中,數(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)看看字段:

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

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;

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

  我們一一進(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)代碼塊:

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

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   }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

  這個(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)造方法:

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

 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   }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

  可見(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)自接口的方法

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xù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   }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

  這五個(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中最重要的方法:

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

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   }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

  這是兩個(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è)連接的方法。

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

 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   }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁(yè)設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開(kāi)發(fā),動(dòng)畫(huà)培訓(xùn)

 http://www.cnblogs.com/V1haoge/p/6675633.html