我們?cè)趧?chuàng)建鏈接的時(shí)候會(huì)發(fā)現(xiàn),返回給 Mybatis
的并不是一個(gè)簡(jiǎn)單的 connection
而是一個(gè) DruidPooledConnection
這里是一個(gè)我們需要注意點(diǎn)的點(diǎn)髓棋,會(huì)到正題卖子,我們拿到一個(gè)鏈接媳瞪,假如使用完成后我們最重要就是 close 掉這個(gè)鏈接因谎,那么 Mybatis
也是這樣做的亏推。我們可以看一下 SqlSessionInterceptor
這個(gè)類(lèi)的 Invoke
方法敛瓷,所有 Dao 都是通過(guò)這個(gè)攔截器生成代理對(duì)象叁巨。
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator
.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
我們看 finally
方法最終調(diào)用了 closeSqlSession
方法,我們根據(jù)最后調(diào)用鏈路呐籽,最終跟蹤到了DruidPooledConnection
的 recycle 方法锋勺,調(diào)用棧如下:
我們先看一下他的 recycle 方法。
public void recycle() throws SQLException {
if (this.disable) {
return;
}
DruidConnectionHolder holder = this.holder;
if (holder == null) {
if (dupCloseLogEnable) {
LOG.error("dup close");
}
return;
}
if (!this.abandoned) {
DruidAbstractDataSource dataSource = holder.getDataSource();
dataSource.recycle(this);
}
this.holder = null;
conn = null;
transactionInfo = null;
closed = true;
}
我們看一下主要的邏輯狡蝶,就是調(diào)用 DruiddataSource
的 recycle
方法, 兜兜轉(zhuǎn)轉(zhuǎn)終于回到了我們的 DruiddataSource
庶橱。
protected void recycle(DruidPooledConnection pooledConnection) throws SQLException {
final DruidConnectionHolder holder = pooledConnection.holder;
if (holder == null) {
LOG.warn("connectionHolder is null");
return;
}
// 假如當(dāng)前線(xiàn)程和獲取鏈接不是同一個(gè)線(xiàn)程,這里會(huì) warn 報(bào)錯(cuò)贪惹, 這里會(huì)涉及到事務(wù)相關(guān)的東西苏章,我們后面詳解。
if (logDifferentThread //
&& (!isAsyncCloseConnectionEnable()) //
&& pooledConnection.ownerThread != Thread.currentThread()//
) {
LOG.warn("get/close not same thread");
}
final Connection physicalConnection = holder.conn;
// 從 activeConnections 中移除當(dāng)前 connection
if (pooledConnection.traceEnable) {
Object oldInfo = null;
activeConnectionLock.lock();
try {
if (pooledConnection.traceEnable) {
oldInfo = activeConnections.remove(pooledConnection);
pooledConnection.traceEnable = false;
}
} finally {
activeConnectionLock.unlock();
}
if (oldInfo == null) {
if (LOG.isWarnEnabled()) {
LOG.warn("remove abandonded failed. activeConnections.size " + activeConnections.size());
}
}
}
final boolean isAutoCommit = holder.underlyingAutoCommit;
final boolean isReadOnly = holder.underlyingReadOnly;
final boolean testOnReturn = this.testOnReturn;
try {
// check need to rollback?
if ((!isAutoCommit) && (!isReadOnly)) {
pooledConnection.rollback();
}
// 假如不是同個(gè)線(xiàn)程馍乙,需要開(kāi)啟鎖后才能rest布近,rest 主要是做一些關(guān)于重置 holder setting 的事情垫释。
// reset holder, restore default settings, clear warnings
boolean isSameThread = pooledConnection.ownerThread == Thread.currentThread();
if (!isSameThread) {
final ReentrantLock lock = pooledConnection.lock;
lock.lock();
try {
holder.reset();
} finally {
lock.unlock();
}
} else {
holder.reset();
}
if (holder.discard) {
return;
}
// j檢查是否大于最大連接數(shù),是的話(huà)清除
if (phyMaxUseCount > 0 && holder.useCount >= phyMaxUseCount) {
discardConnection(holder);
return;
}
// 檢查線(xiàn)程是否關(guān)閉
if (physicalConnection.isClosed()) {
lock.lock();
try {
if (holder.active) {
activeCount--;
holder.active = false;
}
closeCount++;
} finally {
lock.unlock();
}
return;
}
// 檢查線(xiàn)程是否存活
if (testOnReturn) {
boolean validate = testConnectionInternal(holder, physicalConnection);
if (!validate) {
JdbcUtils.close(physicalConnection);
destroyCountUpdater.incrementAndGet(this);
lock.lock();
try {
if (holder.active) {
activeCount--;
holder.active = false;
}
closeCount++;
} finally {
lock.unlock();
}
return;
}
}
if (holder.initSchema != null) {
holder.conn.setSchema(holder.initSchema);
holder.initSchema = null;
}
if (!enable) {
discardConnection(holder);
return;
}
boolean result;
final long currentTimeMillis = System.currentTimeMillis();
// 檢查是否超過(guò)醉倒時(shí)長(zhǎng)
if (phyTimeoutMillis > 0) {
long phyConnectTimeMillis = currentTimeMillis - holder.connectTimeMillis;
if (phyConnectTimeMillis > phyTimeoutMillis) {
discardConnection(holder);
return;
}
}
// 獲取鎖撑瞧,最重要的邏輯開(kāi)始了
lock.lock();
try {
// 計(jì)時(shí)器操作
if (holder.active) {
activeCount--;
holder.active = false;
}
closeCount++;
// 將連接放回 connections
result = putLast(holder, currentTimeMillis);
recycleCount++;
} finally {
lock.unlock();
}
if (!result) {
JdbcUtils.close(holder.conn);
LOG.info("connection recyle failed.");
}
} catch (Throwable e) {
holder.clearStatementCache();
if (!holder.discard) {
discardConnection(holder);
holder.discard = true;
}
LOG.error("recyle error", e);
recycleErrorCountUpdater.incrementAndGet(this);
}
}
這里的邏輯比較簡(jiǎn)單棵譬,主要如下:
- 檢查當(dāng)前線(xiàn)程和獲取鏈接的線(xiàn)程是否同一個(gè),這里可能涉及到并發(fā)問(wèn)題和事務(wù)問(wèn)題预伺,我們后續(xù)進(jìn)行講解订咸。
- 件當(dāng)前線(xiàn)程從活躍線(xiàn)程組
activeConnections
中移除,主要是方便后面的丟棄或者回收的工作酬诀。 - 然后檢查是否需要進(jìn)行回滾脏嚷,不需要繼續(xù)往下走。
-
reset
當(dāng)前connection
的holder
的相關(guān)配置瞒御。 - 接下來(lái)是對(duì)各項(xiàng)信息進(jìn)行檢查父叙,主要是看連接是否還可以重用。
- 鎖住然后進(jìn)行真正的回收工作肴裙,這里回收交給了
putLast
方法趾唱。
最后我們來(lái)看一下putLast
方法。
boolean putLast(DruidConnectionHolder e, long lastActiveTimeMillis) {
if (poolingCount >= maxActive || e.discard || this.closed) {
return false;
}
e.lastActiveTimeMillis = lastActiveTimeMillis;
connections[poolingCount] = e;
incrementPoolingCount();
if (poolingCount > poolingPeak) {
poolingPeak = poolingCount;
poolingPeakTime = lastActiveTimeMillis;
}
notEmpty.signal();
notEmptySignalCount++;
return true;
}
這里主要是將連接放回到 connections
空閑連接數(shù)組的最后面蜻懦,這里我們還記得甜癞,獲取鏈從頭開(kāi)始取,然后釋放就放回到最后一位宛乃。最后發(fā)送信號(hào)悠咱,通知等待線(xiàn)程獲取連接。