Mybatis數(shù)據(jù)源結(jié)構(gòu)解析

點(diǎn)擊上方藍(lán)字關(guān)注我!
正文共:?14030字?27圖
預(yù)計(jì)閱讀時(shí)間:?36分鐘
對于 ORM 框架而言,數(shù)據(jù)源的組織是一個(gè)非常重要的一部分,這直接影響到框架的性能問題。本文將通過對 MyBatis 框架的數(shù)據(jù)源結(jié)構(gòu)進(jìn)行詳盡的分析,找出什么時(shí)候創(chuàng)建 Connection ,并且深入解析 MyBatis 的連接池。
本章的組織結(jié)構(gòu):
零、什么是連接池和線程池 一、MyBatis 數(shù)據(jù)源 DataSource 分類 二、數(shù)據(jù)源 DataSource 的創(chuàng)建過程 三、 DataSource 什么時(shí)候創(chuàng)建 Connection 對象 四、不使用連接池的 UnpooledDataSource 五、使用了連接池的 PooledDataSource
連接池和線程池
連接池:(降低物理連接損耗)
1、連接池是面向數(shù)據(jù)庫連接的 2、連接池是為了優(yōu)化數(shù)據(jù)庫連接資源 3、連接池有點(diǎn)類似在客戶端做優(yōu)化
數(shù)據(jù)庫連接是一項(xiàng)有限的昂貴資源,一個(gè)數(shù)據(jù)庫連接對象均對應(yīng)一個(gè)物理數(shù)據(jù)庫連接,每次操作都打開一個(gè)物理連接,使用完都關(guān)閉連接,這樣造成系統(tǒng)的性能低下。數(shù)據(jù)庫連接池的解決方案是在應(yīng)用程序啟動(dòng)時(shí)建立足夠的數(shù)據(jù)庫連接,并將這些連接組成一個(gè)連接池,由應(yīng)用程序動(dòng)態(tài)地對池中的連接進(jìn)行申請、使用和釋放。對于多于連接池中連接數(shù)的并發(fā)請求,應(yīng)該在請求隊(duì)列中排隊(duì)等待。并且應(yīng)用程序可以根據(jù)池中連接的使用率,動(dòng)態(tài)增加或減少池中的連接數(shù)。
線程池:(降低線程創(chuàng)建銷毀損耗)
1、線程池是面向后臺程序的 2、線程池是是為了提高內(nèi)存和CPU效率 3、線程池有點(diǎn)類似于在服務(wù)端做優(yōu)化
線程池是一次性創(chuàng)建一定數(shù)量的線程(應(yīng)該可以配置初始線程數(shù)量的),當(dāng)用請求過來不用去創(chuàng)建新的線程,直接使用已創(chuàng)建的線程,使用后又放回到線程池中。避免了頻繁創(chuàng)建線程,及銷毀線程的系統(tǒng)開銷,提高是內(nèi)存和CPU效率。
相同點(diǎn):
都是事先準(zhǔn)備好資源,避免頻繁創(chuàng)建和銷毀的代價(jià)。
數(shù)據(jù)源的分類
在Mybatis體系中,分為3種DataSource
?打開Mybatis源碼找到datasource包,可以看到3個(gè)子package
?

UNPOOLED ? ?
不使用連接池的數(shù)據(jù)源POOLED ? ? ?使用
連接池的數(shù)據(jù)源JNDI ? ? ? ?使用
JNDI實(shí)現(xiàn)的數(shù)據(jù)源

?MyBatis內(nèi)部分別定義了實(shí)現(xiàn)了java.sql.DataSource接口的UnpooledDataSource,PooledDataSource類來表示UNPOOLED、POOLED類型的數(shù)據(jù)源。如下圖所示:
?

PooledDataSource和UnpooledDataSrouce都實(shí)現(xiàn)了java.sql.DataSource接口. PooledDataSource持有一個(gè)UnPooledDataSource的引用,當(dāng)PooledDataSource要?jiǎng)?chuàng)建Connection實(shí)例時(shí),實(shí)際還是通過UnPooledDataSource來創(chuàng)建的.(PooledDataSource)只是提供一種 緩存連接池機(jī)制.
?JNDI類型的數(shù)據(jù)源DataSource,則是通過JNDI上下文中取值。
?
數(shù)據(jù)源 DataSource 的創(chuàng)建過程
?在mybatis的XML配置文件中,使用
?元素來配置數(shù)據(jù)源:
<dataSource?type="POOLED">?//這里?type?屬性的取值就是為POOLED、UNPOOLED、JNDI
??<property?name="driver"?value="${jdbc.driver}"/>
??<property?name="url"?value="${jdbc.url}"/>
??<property?name="username"?value="${jdbc.username}"/>
??<property?name="password"?value="${jdbc.password}"/>
dataSource>
MyBatis在初始化時(shí),解析此文件,根據(jù)的type屬性來創(chuàng)建相應(yīng)類型的的數(shù)據(jù)源DataSource,即:
type=”POOLED” ? ? :
創(chuàng)建PooledDataSource實(shí)例type=”UNPOOLED” ? :
創(chuàng)建UnpooledDataSource實(shí)例type=”JNDI” ? ? ? :
從JNDI服務(wù)上查找DataSource實(shí)例Mybatis是通過工廠模式來創(chuàng)建數(shù)據(jù)源對象的 我們來看看源碼:
public?interface?DataSourceFactory?{
void?setProperties(Properties?props);
DataSource?getDataSource();//生產(chǎn)DataSource
}上述3種類型的數(shù)據(jù)源,對應(yīng)有自己的工廠模式,都實(shí)現(xiàn)了這個(gè)
DataSourceFactory

?MyBatis創(chuàng)建了DataSource實(shí)例后,會將其放到Configuration對象內(nèi)的Environment對象中, 供以后使用。
?
?注意dataSource 此時(shí)只會保存好配置信息.連接池此時(shí)并沒有創(chuàng)建好連接.只有當(dāng)程序在調(diào)用操作數(shù)據(jù)庫的方法時(shí),才會初始化連接.
?
DataSource什么時(shí)候創(chuàng)建Connection對象
我們需要?jiǎng)?chuàng)建SqlSession對象并需要執(zhí)行SQL語句時(shí),這時(shí)候MyBatis才會去調(diào)用dataSource對象來創(chuàng)建java.sql.Connection對象。也就是說,java.sql.Connection對象的創(chuàng)建一直延遲到執(zhí)行SQL語句的時(shí)候。
例子:
??@Test
??public?void?testMyBatisBuild()?throws?IOException?{
??????Reader?reader?=?Resources.getResourceAsReader("mybatis-config.xml");
??????SqlSessionFactory?factory?=?new?SqlSessionFactoryBuilder().build(reader);
??????SqlSession?sqlSession?=?factory.openSession();
??????TestMapper?mapper?=?sqlSession.getMapper(TestMapper.class);
??????Ttest?one?=?mapper.getOne(1L);//直到這一行,才會去創(chuàng)建一個(gè)數(shù)據(jù)庫連接!
??????System.out.println(one);
??????sqlSession.close();
??}
口說無憑,跟進(jìn)源碼看看他們是在什么時(shí)候創(chuàng)建的...
跟進(jìn)源碼,驗(yàn)證Datasource 和Connection對象創(chuàng)建時(shí)機(jī)
驗(yàn)證Datasource創(chuàng)建時(shí)機(jī)
上面我們已經(jīng)知道,pooled數(shù)據(jù)源實(shí)際上也是使用的unpooled的實(shí)例,那么我們在UnpooledDataSourceFactory的 getDataSource方法的源碼中做一些修改 并運(yùn)行測試用例:
@Override
public?DataSource?getDataSource()?{//此方法是UnpooledDataSourceFactory實(shí)現(xiàn)DataSourceFactory復(fù)寫
??System.out.println("創(chuàng)建了數(shù)據(jù)源");
??System.out.println(dataSource.toString());
??return?dataSource;
}

?結(jié)論:在創(chuàng)建完SqlsessionFactory時(shí),DataSource實(shí)例就創(chuàng)建了.
?
驗(yàn)證Connection創(chuàng)建時(shí)機(jī)
首先我們先查出現(xiàn)在數(shù)據(jù)庫的所有連接數(shù),在數(shù)據(jù)庫中執(zhí)行
SELECT?*?FROM?performance_schema.hosts;
?返回?cái)?shù)據(jù): ?顯示當(dāng)前連接數(shù)為1,總連接數(shù)70
?

show?full?processlist;?//顯示所有的任務(wù)列表
?返回:當(dāng)前只有一個(gè)查詢的連接在運(yùn)行
?

重新啟動(dòng)項(xiàng)目,在運(yùn)行到需要執(zhí)行實(shí)際的sql操作時(shí),可以看到他已經(jīng)被代理增強(qiáng)了

?直到此時(shí),連接數(shù)還是沒有變,說明連接還沒有創(chuàng)建,我們接著往下看.
?
?我們按F7進(jìn)入方法,可以看到,他被代理,,這時(shí)候會執(zhí)行到之前的代理方法中調(diào)用invoke方法.這里有一個(gè)判斷,但是并不成立,于是進(jìn)入cachedInvoker(method).invoke()方法代理執(zhí)行一下操作
?

cachedInvoker(method).invoke()方法
??
????@Override
????public?Object?invoke(Object?proxy,?Method?method,?Object[]?args,?SqlSession?sqlSession)?throws?Throwable?{
??????return?mapperMethod.execute(sqlSession,?args);
????}
?繼續(xù)F7進(jìn)入方法,由于我們是單條查詢select 所以會case進(jìn)入select塊中的selectOne
?

繼續(xù)F7

繼續(xù)F7
?通過configuration.getMappedStatement獲取MappedStatement
?

?單步步過,F8后,進(jìn)入executor.query方法
?
@Override
public??List?query(MappedStatement?ms,?Object?parameterObject,?RowBounds?rowBounds,?ResultHandler?resultHandler)?throws?SQLException? {
??BoundSql?boundSql?=?ms.getBoundSql(parameterObject);
??CacheKey?key?=?createCacheKey(ms,?parameterObject,?rowBounds,?boundSql);
??return?query(ms,?parameterObject,?rowBounds,?resultHandler,?key,?boundSql);
}
?繼續(xù)走到底,F7進(jìn)入query方法
?

?此時(shí),會去緩存中查詢,這里的緩存是二級緩存對象 ,生命周期是mapper級別的(一級緩存是一個(gè)session級別的),因?yàn)槲覀兇藭r(shí)是第一次運(yùn)行程序,所以肯定為Null,這時(shí)候會直接去查詢,調(diào)用
?delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql)方法,F7進(jìn)入這個(gè)方法

?二級緩存沒有獲取到,又去查詢了一級緩存,發(fā)現(xiàn)一級緩存也沒有,這個(gè)時(shí)候,才去查數(shù)據(jù)庫
?
queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);//沒有緩存則去db查
?F7進(jìn)入queryFromDatabase方法.看到是一些對一級緩存的操作,我們主要看
?doQuery方法F7進(jìn)入它.

?可以看到它準(zhǔn)備了一個(gè)空的Statement
?

?我們F7跟進(jìn)看一下prepareStatement方法 ,發(fā)現(xiàn)他調(diào)用了getConnection,哎!有點(diǎn)眼熟了,繼續(xù)F7進(jìn)入getConnection()方法,
?

?又是一個(gè)getConnection()....繼續(xù)F7進(jìn)入
?transaction.getConnection()方法

?又是一個(gè)getConnection()方法.判斷connection是否為空.為空openConnection()否則直接返回connection;我們F7繼續(xù)跟進(jìn)openConnection()方法
?

??protected?void?openConnection()?throws?SQLException?{
??if?(log.isDebugEnabled())?{
????log.debug("Opening?JDBC?Connection");
??}
??connection?=?dataSource.getConnection();//最終獲取連接的地方在這句.
??if?(level?!=?null)?{
????connection.setTransactionIsolation(level.getLevel());//設(shè)置隔離等級
??}
??setDesiredAutoCommit(autoCommit);//是否自動(dòng)提交,默認(rèn)false,update不會提交到數(shù)據(jù)庫,需要手動(dòng)commit
}
dataSource.getConnection()執(zhí)行完,至此一個(gè)connection才創(chuàng)建完成. 我們驗(yàn)證一下 在dataSource.getConnection()時(shí)打一下斷點(diǎn).

此時(shí)數(shù)據(jù)庫中的連接數(shù)依然沒變 還是1

我們按F8 執(zhí)行一步
在控制臺可以看到connection = com.mysql.jdbc.JDBC4Connection@1500b2f3 實(shí)例創(chuàng)建完畢 我們再去數(shù)據(jù)庫中看看連接數(shù)

兩個(gè)連接分別是:

不使用連接池的 UnpooledDataSource
?當(dāng)
?的type屬性被配置成了”UNPOOLED”,MyBatis首先會實(shí)例化一個(gè)UnpooledDataSourceFactory工廠實(shí)例,然后通過.getDataSource()方法返回一個(gè)UnpooledDataSource實(shí)例對象引用,我們假定為dataSource。使用UnpooledDataSource的getConnection(),每調(diào)用一次就會產(chǎn)生一個(gè)新的Connection實(shí)例對象。
UnPooledDataSource的getConnection()方法實(shí)現(xiàn)如下:
/*
UnpooledDataSource的getConnection()實(shí)現(xiàn)
*/
public?Connection?getConnection()?throws?SQLException
{
??return?doGetConnection(username,?password);
}
private?Connection?doGetConnection(String?username,?String?password)?throws?SQLException
{
??//封裝username和password成properties
??Properties?props?=?new?Properties();
??if?(driverProperties?!=?null)
??{
??????props.putAll(driverProperties);
??}
??if?(username?!=?null)
??{
??????props.setProperty("user",?username);
??}
??if?(password?!=?null)
??{
??????props.setProperty("password",?password);
??}
??return?doGetConnection(props);
}
/*
*??獲取數(shù)據(jù)連接
*/
private?Connection?doGetConnection(Properties?properties)?throws?SQLException
{
??//1.初始化驅(qū)動(dòng)
??initializeDriver();
??//2.從DriverManager中獲取連接,獲取新的Connection對象
??Connection?connection?=?DriverManager.getConnection(url,?properties);
??//3.配置connection屬性
??configureConnection(connection);
??return?connection;
}
?UnpooledDataSource會做以下幾件事情:
?
初始化驅(qū)動(dòng):? ?判斷driver驅(qū)動(dòng)是否已經(jīng)加載到內(nèi)存中,如果還沒有加載,則會動(dòng)態(tài)地加載driver類,并實(shí)例化一個(gè)Driver對象,使用DriverManager.registerDriver()方法將其注冊到內(nèi)存中,以供后續(xù)使用。 創(chuàng)建Connection對象:? ?使用DriverManager.getConnection()方法創(chuàng)建連接。 配置Connection對象:? ?設(shè)置是否自動(dòng)提交autoCommit和隔離級別isolationLevel。 返回Connection對象。

?我們每調(diào)用一次getConnection()方法,都會通過DriverManager.getConnection()返回新的java.sql.Connection實(shí)例,這樣當(dāng)然對于資源是一種浪費(fèi),為了防止重復(fù)的去創(chuàng)建和銷毀連接,于是引入了連接池的概念.
?
使用了連接池的 PooledDataSource
要理解連接池,首先要了解它對于connection的容器,它使用PoolState容器來管理所有的conncetion
在PoolState中,它將connection分為兩種狀態(tài),空閑狀態(tài)(idle)和活動(dòng)狀態(tài)(active),他們分別被存儲到PoolState容器內(nèi)的idleConnections和activeConnections兩個(gè)ArrayList中

idleConnections:空閑(idle)狀態(tài)PooledConnection對象被放置到此集合中,表示當(dāng)前閑置的沒有被使用的PooledConnection集合,調(diào)用PooledDataSource的getConnection()方法時(shí),會優(yōu)先從此集合中取PooledConnection對象。當(dāng)用完一個(gè)java.sql.Connection對象時(shí),MyBatis會將其包裹成PooledConnection對象放到此集合中。activeConnections:活動(dòng)(active)狀態(tài)的PooledConnection對象被放置到名為activeConnections的ArrayList中,表示當(dāng)前正在被使用的PooledConnection集合,調(diào)用PooledDataSource的getConnection()方法時(shí),會優(yōu)先從idleConnections集合中取PooledConnection對象,如果沒有,則看此集合是否已滿,如果未滿,PooledDataSource會創(chuàng)建出一個(gè)PooledConnection,添加到此集合中,并返回。
從連接池中獲取一個(gè)連接對象的過程
下面讓我們看一下PooledDataSource 的popConnection方法獲取Connection對象的實(shí)現(xiàn):
private?PooledConnection?popConnection(String?username,?String?password)?throws?SQLException?{
????boolean?countedWait?=?false;
????PooledConnection?conn?=?null;
????long?t?=?System.currentTimeMillis();
????int?localBadConnectionCount?=?0;
????while?(conn?==?null)?{
??????synchronized?(state)?{//給state對象加鎖
????????if?(!state.idleConnections.isEmpty())?{//如果空閑列表不空,就從空閑列表中拿connection
??????????//?Pool?has?available?connection
??????????conn?=?state.idleConnections.remove(0);//拿出空閑列表中的第一個(gè),去驗(yàn)證連接是否還有效
??????????if?(log.isDebugEnabled())?{
????????????log.debug("Checked?out?connection?"?+?conn.getRealHashCode()?+?"?from?pool.");
??????????}
????????}?else?{
??????????//?空閑連接池中沒有可用的連接,就來看看活躍連接列表中是否有..先判斷活動(dòng)連接總數(shù)?是否小于?最大可用的活動(dòng)連接數(shù)
??????????if?(state.activeConnections.size()?????????????//?如果連接數(shù)小于list.size?直接創(chuàng)建新的連接.
????????????conn?=?new?PooledConnection(dataSource.getConnection(),?this);
????????????if?(log.isDebugEnabled())?{
??????????????log.debug("Created?connection?"?+?conn.getRealHashCode()?+?".");
????????????}
??????????}?else?{
????????????//?此時(shí)連接數(shù)也滿了,不能創(chuàng)建新的連接.?找到最老的那個(gè),檢查它是否過期
????????????//計(jì)算它的校驗(yàn)時(shí)間,如果校驗(yàn)時(shí)間大于連接池規(guī)定的最大校驗(yàn)時(shí)間,則認(rèn)為它已經(jīng)過期了
????????????//?利用這個(gè)PoolConnection內(nèi)部的realConnection重新生成一個(gè)PooledConnection
????????????PooledConnection?oldestActiveConnection?=?state.activeConnections.get(0);
????????????long?longestCheckoutTime?=?oldestActiveConnection.getCheckoutTime();
????????????if?(longestCheckoutTime?>?poolMaximumCheckoutTime)?{
??????????????//?可以要求過期這個(gè)連接.
??????????????state.claimedOverdueConnectionCount++;
??????????????state.accumulatedCheckoutTimeOfOverdueConnections?+=?longestCheckoutTime;
??????????????state.accumulatedCheckoutTime?+=?longestCheckoutTime;
??????????????state.activeConnections.remove(oldestActiveConnection);
??????????????if?(!oldestActiveConnection.getRealConnection().getAutoCommit())?{
????????????????try?{
??????????????????oldestActiveConnection.getRealConnection().rollback();
????????????????}?catch?(SQLException?e)?{
??????????????????/*
?????????????????????Just?log?a?message?for?debug?and?continue?to?execute?the?following
?????????????????????statement?like?nothing?happened.
?????????????????????Wrap?the?bad?connection?with?a?new?PooledConnection,?this?will?help
?????????????????????to?not?interrupt?current?executing?thread?and?give?current?thread?a
?????????????????????chance?to?join?the?next?competition?for?another?valid/good?database
?????????????????????connection.?At?the?end?of?this?loop,?bad?{@link?@conn}?will?be?set?as?null.
???????????????????*/
??????????????????log.debug("Bad?connection.?Could?not?roll?back");
????????????????}
??????????????}
??????????????conn?=?new?PooledConnection(oldestActiveConnection.getRealConnection(),?this);
??????????????conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
??????????????conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
??????????????oldestActiveConnection.invalidate();
??????????????if?(log.isDebugEnabled())?{
????????????????log.debug("Claimed?overdue?connection?"?+?conn.getRealHashCode()?+?".");
??????????????}
????????????}?else?{
??????????????//如果不能釋放,則必須等待
??????????????//?Must?wait
??????????????try?{
????????????????if?(!countedWait)?{
??????????????????state.hadToWaitCount++;
??????????????????countedWait?=?true;
????????????????}
????????????????if?(log.isDebugEnabled())?{
??????????????????log.debug("Waiting?as?long?as?"?+?poolTimeToWait?+?"?milliseconds?for?connection.");
????????????????}
????????????????long?wt?=?System.currentTimeMillis();
????????????????state.wait(poolTimeToWait);
????????????????state.accumulatedWaitTime?+=?System.currentTimeMillis()?-?wt;
??????????????}?catch?(InterruptedException?e)?{
????????????????break;
??????????????}
????????????}
??????????}
????????}
????????if?(conn?!=?null)?{
??????????//?ping?to?server?and?check?the?connection?is?valid?or?not
??????????if?(conn.isValid())?{//去驗(yàn)證連接是否還有效.
????????????if?(!conn.getRealConnection().getAutoCommit())?{
??????????????conn.getRealConnection().rollback();
????????????}
????????????conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(),?username,?password));
????????????conn.setCheckoutTimestamp(System.currentTimeMillis());
????????????conn.setLastUsedTimestamp(System.currentTimeMillis());
????????????state.activeConnections.add(conn);
????????????state.requestCount++;
????????????state.accumulatedRequestTime?+=?System.currentTimeMillis()?-?t;
??????????}?else?{
????????????if?(log.isDebugEnabled())?{
??????????????log.debug("A?bad?connection?("?+?conn.getRealHashCode()?+?")?was?returned?from?the?pool,?getting?another?connection.");
????????????}
????????????state.badConnectionCount++;
????????????localBadConnectionCount++;
????????????conn?=?null;
????????????if?(localBadConnectionCount?>?(poolMaximumIdleConnections?+?poolMaximumLocalBadConnectionTolerance))?{
??????????????if?(log.isDebugEnabled())?{
????????????????log.debug("PooledDataSource:?Could?not?get?a?good?connection?to?the?database.");
??????????????}
??????????????throw?new?SQLException("PooledDataSource:?Could?not?get?a?good?connection?to?the?database.");
????????????}
??????????}
????????}
??????}
????}
????if?(conn?==?null)?{
??????if?(log.isDebugEnabled())?{
????????log.debug("PooledDataSource:?Unknown?severe?error?condition.??The?connection?pool?returned?a?null?connection.");
??????}
??????throw?new?SQLException("PooledDataSource:?Unknown?severe?error?condition.??The?connection?pool?returned?a?null?connection.");
????}
????return?conn;
??}

?如上所示,對于PooledDataSource的getConnection()方法內(nèi),先是調(diào)用類PooledDataSource的popConnection()方法返回了一個(gè)PooledConnection對象,然后調(diào)用了PooledConnection的getProxyConnection()來返回Connection對象。
?
復(fù)用連接的過程
?如果我們使用了連接池,我們在用完了Connection對象時(shí),需要將它放在連接池中,該怎樣做呢?如果讓我們來想的話,應(yīng)該是通過代理Connection對象,在調(diào)用close時(shí),并不真正關(guān)閉,而是丟到管理連接的容器中去. 要驗(yàn)證這個(gè)想法 那么 來看看Mybatis幫我們怎么實(shí)現(xiàn)復(fù)用連接的.
?
class?PooledConnection?implements?InvocationHandler?{
??//......
??//所創(chuàng)建它的datasource引用
??private?PooledDataSource?dataSource;
??//真正的Connection對象
??private?Connection?realConnection;
??//代理自己的代理Connection
??private?Connection?proxyConnection;
??//......
}
?PooledConenction實(shí)現(xiàn)了InvocationHandler接口,并且,proxyConnection對象也是根據(jù)這個(gè)它來生成的代理對象:
?
public?PooledConnection(Connection?connection,?PooledDataSource?dataSource)?{
????this.hashCode?=?connection.hashCode();
????this.realConnection?=?connection;//真實(shí)連接
????this.dataSource?=?dataSource;
????this.createdTimestamp?=?System.currentTimeMillis();
????this.lastUsedTimestamp?=?System.currentTimeMillis();
????this.valid?=?true;
????this.proxyConnection?=?(Connection)?Proxy.newProxyInstance(Connection.class.getClassLoader(),?IFACES,?this);
??}
?實(shí)際上,我們調(diào)用PooledDataSource的getConnection()方法返回的就是這個(gè)proxyConnection對象。當(dāng)我們調(diào)用此proxyConnection對象上的任何方法時(shí),都會調(diào)用PooledConnection對象內(nèi)invoke()方法。讓我們看一下PooledConnection類中的invoke()方法定義:
?
public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
????String?methodName?=?method.getName();
????//當(dāng)調(diào)用關(guān)閉的時(shí)候,回收此Connection到PooledDataSource中
????if?(CLOSE.hashCode()?==?methodName.hashCode()?&&?CLOSE.equals(methodName))?{
??????dataSource.pushConnection(this);
??????return?null;
????}?else?{
??????try?{
????????if?(!Object.class.equals(method.getDeclaringClass()))?{
??????????checkConnection();
????????}
????????return?method.invoke(realConnection,?args);
??????}?catch?(Throwable?t)?{
????????throw?ExceptionUtil.unwrapThrowable(t);
??????}
????}
??}
?結(jié)論:當(dāng)我們使用了pooledDataSource.getConnection()返回的Connection對象的close()方法時(shí),不會調(diào)用真正Connection的close()方法,而是將此Connection對象放到連接池中。調(diào)用
?dataSource.pushConnection(this)實(shí)現(xiàn)
protected?void?pushConnection(PooledConnection?conn)?throws?SQLException?{
????synchronized?(state)?{
??????state.activeConnections.remove(conn);
??????if?(conn.isValid())?{
????????if?(state.idleConnections.size()???????????state.accumulatedCheckoutTime?+=?conn.getCheckoutTime();
??????????if?(!conn.getRealConnection().getAutoCommit())?{
????????????conn.getRealConnection().rollback();
??????????}
??????????PooledConnection?newConn?=?new?PooledConnection(conn.getRealConnection(),?this);
??????????state.idleConnections.add(newConn);
??????????newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
??????????newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
??????????conn.invalidate();
??????????if?(log.isDebugEnabled())?{
????????????log.debug("Returned?connection?"?+?newConn.getRealHashCode()?+?"?to?pool.");
??????????}
??????????state.notifyAll();
????????}?else?{
??????????state.accumulatedCheckoutTime?+=?conn.getCheckoutTime();
??????????if?(!conn.getRealConnection().getAutoCommit())?{
????????????conn.getRealConnection().rollback();
??????????}
??????????conn.getRealConnection().close();
??????????if?(log.isDebugEnabled())?{
????????????log.debug("Closed?connection?"?+?conn.getRealHashCode()?+?".");
??????????}
??????????conn.invalidate();
????????}
??????}?else?{
????????if?(log.isDebugEnabled())?{
??????????log.debug("A?bad?connection?("?+?conn.getRealHashCode()?+?")?attempted?to?return?to?the?pool,?discarding?connection.");
????????}
????????state.badConnectionCount++;
??????}
????}
??}

