常見的資料源元件都實作了javax.sql.DataSource接口;
MyBatis不但要能內建第三方的資料源元件,自身也提供了資料源的實作;
一般情況下,資料源的初始化過程參數較多,比較複雜;
(采用工廠模式)

其實mybatis采用工廠模式,也是為了能适應更多的第三方資料源元件,如果有新的元件進來,隻需要寫類實作就行,工廠模式的優點很多,比如Spring的ioc,就是把對象的建立和對象的使用分離開來,解耦,new關鍵字耦合太多,對象的建立和管理由spring來做,而對象的使用是開發人員自己。
資料源子產品的類圖:
看一下DataSourceFactory接口:
這個接口的實作類有:
DataSourceFactory的實作類主要看 UnpooledDataSourceFactory這個類,也是一個工廠類。是一個不用連接配接池的資料連結工廠。
為什麼說資料源很複雜,因為屬性太多了,一般配置驅動。資料庫url。使用者名,密碼等等實際上還可以配置很多
來看屬性DataSource,這個也是一個接口
看DataSource接口的實作類PoolDataSource,使用連接配接池的資料源。
這個類還有很多初始化的構造函數,全是重載的。
是以才使用工廠來建立DataSource的對象/
再來回到這個圖,根據代碼也可以看到PooledDataSourceFactory繼承了UnPooledDataSourceFactory
注意把邏輯厘清楚:字尾帶Factory的都是工廠類,不過有的是Unpool,有的是pool,而且pool的工廠類繼承Unpool的工廠類,而Unpool的工廠類是用來生産Unpool的資料源,pool的工廠類是用來生産pool的資料源,看下圖由虛線箭頭的creat标志。
看UnpooledDataSource這個類,implements DataSource接口
這個問題來Debug一下:寫一個Demo類,打斷點
在UnpooledDataSource類的static的代碼塊中打上斷點:
在DriverManager類的static的代碼塊中打上斷點:
之後斷點一直進入到上圖LoadInitialDrivers();
然後進入這個load方法,打上斷點
因為參數是Driver.class,是以ServiceLoader的常量service = Driver.class
那麼來看構造方法
可以看到調用reload(),裡面就是調用了new LazyIterator(service,loader),這是ServiceLoader類的内部類。
然後跳過,一直回到
在這個while循環裡面跟進去, (上圖是跟完以後的,下圖是具體跟的步驟)
PREFIX 和 service 是什麼 看下圖:
然後 就去 這個目錄下
parse解析
傳回true ,然會while循環才進去
跟進next()
傳回true,繼續
就是在這塊進行的。Class.forName,
然後CopyOnWritelist裡面就儲存了mysql驅動
這塊ServiceLoader.load()屬于Java SPI的知識,其本質就是服務的提供者,提供了服務接口的一種實作之後,在jar包的META-INF/services/目錄裡同時建立一個以服務接口命名的檔案。該檔案裡就是實作該服務接口的具體實作類。而當外部程式裝配這個子產品的時候,就能通過該jar包META-INF/services/裡的配置檔案找到具體的實作類名,并裝載執行個體化,完成子產品的注入。 java.util.ServiceLoader工具類方法會使用ClassLoad類的getResources方法擷取指定目錄下的檔案,讀取檔案内容并解析擷取所有接口實作類的全限路徑,根據全限路徑使用class.forName裝載class檔案到JVM,然後通過c.newInstance()執行個體化類轉化接口類型并放入providers緩存提供者集合中,以供後面使用。上面這段話是摘自
JavaSPI,ServiceLoader.load方法詳解_hupoling的專欄-CSDN部落格_serviceloader.loadblog.csdn.net
有興趣的去看看,我的源碼解讀也很清楚
注意,這是mybatis的加載方式,jdbc的方式調用鍊還不太一樣。可以寫一個測試類用Class.forName()去debug看一下。
unpooledDatasource就是不用連接配接池的資料源,每次都是建立新的資料庫連接配接。而pooledDatasource則不一樣,會去從池子裡找。
接下來看一下UnpooledDatasource是怎麼産生資料庫連接配接的
當 <dataSource>的type屬性被配置成了”UNPOOLED”,MyBatis首先會執行個體化一個UnpooledDataSourceFactory工廠執行個體,然後通過.getDataSource()方法傳回一個UnpooledDataSource執行個體對象引用,使用UnpooledDataSource的getConnection(),每調用一次就會産生一個新的Connection執行個體對象。
看一下:UnpooledDataSource的getConnection()
1.
初始化驅動:判斷driver驅動是否已經加載到記憶體中,如果還沒有加載,則會動态地加載driver類,并執行個體化一個Driver對象,使用DriverManager.registerDriver()方法将其注冊到記憶體中,以供後續使用。
2.
建立Connection對象:使用DriverManager.getConnection()方法建立連接配接。
3.
配置Connection對象:設定是否自動送出autoCommit和隔離級别isolationLevel。
4. 傳回Connection對象。看上圖方法的第一行代碼: initializeDriver(),畫紅框的不用多說了。
接着第二行代碼:
Connection connection = DriverManager.getConnection(
url, properties);
下來看有連接配接池的資料源 PooledDataSource : 一個簡單,同步的、線程安全的資料庫連接配接池,基于PooledConnection和PoolState
有幾個對象需要知道:
1、PooledConnection:使用動态代理封裝了真正的資料庫連接配接對象Connection;
可以看到 PooledConnection 實作了 invocationhandler,動态代理。
真正的資料庫連接配接對象是 Connection,作用是:連接配接資料庫,擷取查詢資料,更新資料
那麼 PooledConnection 是想對Connection進行增強。直接看invoke方法
用于管理PooledConnection對象狀态的元件,通過兩個list分别 管理空閑狀态的連接配接資源和活躍狀态的連接配接資源
千萬記住這兩個ArrayList,以及ArrayList的remove方法的使用.
getConnection()擷取連接配接的流程圖
看一下擷取有連接配接池的資料源 -> 擷取 -> 資料庫連接配接的代碼:
PooledDataSource.getConnection()
擷取連接配接就是在這個紅框的 popConnection方法中做的。代碼很長,邏輯不難,if - else判斷比較多,和流程圖一樣,完全可以看懂,就不做分析了。
popConnection的代碼:
注意這個資料結構,list是有序的集合,而remove(0)就是移除第一個并且傳回。剩下的就不再分析了,代碼很簡單。關閉連接配接也有相應的代碼,具體就不做分析了。