一:先看目录总览以及总体的类关系
二: pooled 包下的东西
PooledDataSource
-
popConnection方法,获取 org.apache.ibatis.datasource.pooled.PooledConnection 对象,这个方法很重要,这是这个方法的代码和我画的图,其他方法。里面都有详细的注释。
/**
* Copyright 2009-2020 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.datasource.pooled;
import org.apache.ibatis.datasource.unpooled.UnpooledDataSource;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import java.util.logging.Logger;
/**
* This is a simple, synchronous, thread-safe database connection pool.
* <p>
* 这是一个简单的,同步的,线程安全的数据库连接池
*
* @author Clinton Begin
*/
public class PooledDataSource implements DataSource {
private static final Log log = LogFactory.getLog(PooledDataSource.class);
/**
* PoolState 对象,记录池化的状态
*/
private final PoolState state = new PoolState(this);
/**
* UnpooledDataSource 对象
*/
private final UnpooledDataSource dataSource;
// 在任意时间可以存在的活动(也就是正在使用)连接数量,默认值:10
protected int poolMaximumActiveConnections = 10;
// 任意时间可能存在的空闲连接数。
protected int poolMaximumIdleConnections = 5;
// 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒)
protected int poolMaximumCheckoutTime = 20000;
// 这是一个底层设置,如果获取连接花费了相当长的时间,
// 连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直安静的失败),
// 默认值:20000 毫秒(即 20 秒)。
protected int poolTimeToWait = 20000;
//这是一个关于坏连接容忍度的底层设置, 作用于每一个尝试从缓存池获取连接的线程.
// 如果这个线程获取到的是一个坏的连接,
// 那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过
// poolMaximumIdleConnections 与 poolMaximumLocalBadConnectionTolerance 之和。 默认值:3 (新增于 3.4.5)
protected int poolMaximumLocalBadConnectionTolerance = 3;
// 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。
// 默认是“NO PING QUERY SET”,这会导致多数数据库驱动失败时带有一个恰当的错误消息。
protected String poolPingQuery = "NO PING QUERY SET";
// 是否启用侦测查询。若开启,需要设置 poolPingQuery 属性为一个可执行的 SQL 语句(
// 最好是一个速度非常快的 SQL 语句),默认值:false。
protected boolean poolPingEnabled;
// 配置 poolPingQuery 的频率。可以被设置为和数据库连接超时时间一样,来避免不必要的侦测,
// 默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。
protected int poolPingConnectionsNotUsedFor;
// 期望 Connection 的类型编码,通过 {@link #assembleConnectionTypeCode(String, String, String)} 计算。
private int expectedConnectionTypeCode;
/**
* dataSource 属性,UnpooledDataSource 对象。这样,就能重用 UnpooledDataSource 的代码了。
* 说白了,获取真正连接的逻辑,还是在 UnpooledDataSource 中实现。
*/
public PooledDataSource() {
dataSource = new UnpooledDataSource();
}
public PooledDataSource(UnpooledDataSource dataSource) {
this.dataSource = dataSource;
}
public PooledDataSource(String driver, String url, String username, String password) {
dataSource = new UnpooledDataSource(driver, url, username, password);
expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
}
public PooledDataSource(String driver, String url, Properties driverProperties) {
dataSource = new UnpooledDataSource(driver, url, driverProperties);
expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
}
public PooledDataSource(ClassLoader driverClassLoader, String driver, String url, String username, String password) {
dataSource = new UnpooledDataSource(driverClassLoader, driver, url, username, password);
expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
}
public PooledDataSource(ClassLoader driverClassLoader, String driver, String url, Properties driverProperties) {
dataSource = new UnpooledDataSource(driverClassLoader, driver, url, driverProperties);
expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
}
/**
* 获得 Connection 连接
*
* @return
* @throws SQLException
*/
@Override
public Connection getConnection() throws SQLException {
return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
}
/**
* 获得 Connection 连接
*
* @return
* @throws SQLException
*/
@Override
public Connection getConnection(String username, String password) throws SQLException {
// getProxyConnection() 方法,返回代理的 Connection 对象。这样,每次对数据库的操作,才能被 PooledConnection的代理拦截。
return popConnection(username, password).getProxyConnection();
}
@Override
public void setLoginTimeout(int loginTimeout) {
DriverManager.setLoginTimeout(loginTimeout);
}
@Override
public int getLoginTimeout() {
return DriverManager.getLoginTimeout();
}
@Override
public void setLogWriter(PrintWriter logWriter) {
DriverManager.setLogWriter(logWriter);
}
@Override
public PrintWriter getLogWriter() {
return DriverManager.getLogWriter();
}
public void setDriver(String driver) {
dataSource.setDriver(driver);
forceCloseAll();
}
public void setUrl(String url) {
dataSource.setUrl(url);
forceCloseAll();
}
public void setUsername(String username) {
dataSource.setUsername(username);
forceCloseAll();
}
public void setPassword(String password) {
dataSource.setPassword(password);
forceCloseAll();
}
public void setDefaultAutoCommit(boolean defaultAutoCommit) {
dataSource.setAutoCommit(defaultAutoCommit);
forceCloseAll();
}
public void setDefaultTransactionIsolationLevel(Integer defaultTransactionIsolationLevel) {
dataSource.setDefaultTransactionIsolationLevel(defaultTransactionIsolationLevel);
forceCloseAll();
}
public void setDriverProperties(Properties driverProps) {
dataSource.setDriverProperties(driverProps);
forceCloseAll();
}
/**
* Sets the default network timeout value to wait for the database operation to complete. See {@link Connection#setNetworkTimeout(java.util.concurrent.Executor, int)}
*
* @param milliseconds The time in milliseconds to wait for the database operation to complete.
* @since 3.5.2
*/
public void setDefaultNetworkTimeout(Integer milliseconds) {
dataSource.setDefaultNetworkTimeout(milliseconds);
forceCloseAll();
}
/**
* The maximum number of active connections.
*
* @param poolMaximumActiveConnections The maximum number of active connections
*/
public void setPoolMaximumActiveConnections(int poolMaximumActiveConnections) {
this.poolMaximumActiveConnections = poolMaximumActiveConnections;
forceCloseAll();
}
/**
* The maximum number of idle connections.
*
* @param poolMaximumIdleConnections The maximum number of idle connections
*/
public void setPoolMaximumIdleConnections(int poolMaximumIdleConnections) {
this.poolMaximumIdleConnections = poolMaximumIdleConnections;
forceCloseAll();
}
/**
* The maximum number of tolerance for bad connection happens in one thread
* which are applying for new {@link PooledConnection}.
*
* @param poolMaximumLocalBadConnectionTolerance max tolerance for bad connection happens in one thread
* @since 3.4.5
*/
public void setPoolMaximumLocalBadConnectionTolerance(
int poolMaximumLocalBadConnectionTolerance) {
this.poolMaximumLocalBadConnectionTolerance = poolMaximumLocalBadConnectionTolerance;
}
/**
* The maximum time a connection can be used before it *may* be
* given away again.
*
* @param poolMaximumCheckoutTime The maximum time
*/
public void setPoolMaximumCheckoutTime(int poolMaximumCheckoutTime) {
this.poolMaximumCheckoutTime = poolMaximumCheckoutTime;
forceCloseAll();
}
/**
* The time to wait before retrying to get a connection.
*
* @param poolTimeToWait The time to wait
*/
public void setPoolTimeToWait(int poolTimeToWait) {
this.poolTimeToWait = poolTimeToWait;
forceCloseAll();
}
/**
* The query to be used to check a connection.
*
* @param poolPingQuery The query
*/
public void setPoolPingQuery(String poolPingQuery) {
this.poolPingQuery = poolPingQuery;
forceCloseAll();
}
/**
* Determines if the ping query should be used.
*
* @param poolPingEnabled True if we need to check a connection before using it
*/
public void setPoolPingEnabled(boolean poolPingEnabled) {
this.poolPingEnabled = poolPingEnabled;
forceCloseAll();
}
/**
* If a connection has not been used in this many milliseconds, ping the
* database to make sure the connection is still good.
*
* @param milliseconds the number of milliseconds of inactivity that will trigger a ping
*/
public void setPoolPingConnectionsNotUsedFor(int milliseconds) {
this.poolPingConnectionsNotUsedFor = milliseconds;
forceCloseAll();
}
public String getDriver() {
return dataSource.getDriver();
}
public String getUrl() {
return dataSource.getUrl();
}
public String getUsername() {
return dataSource.getUsername();
}
public String getPassword() {
return dataSource.getPassword();
}
public boolean isAutoCommit() {
return dataSource.isAutoCommit();
}
public Integer getDefaultTransactionIsolationLevel() {
return dataSource.getDefaultTransactionIsolationLevel();
}
public Properties getDriverProperties() {
return dataSource.getDriverProperties();
}
/**
* Gets the default network timeout.
*
* @return the default network timeout
* @since 3.5.2
*/
public Integer getDefaultNetworkTimeout() {
return dataSource.getDefaultNetworkTimeout();
}
public int getPoolMaximumActiveConnections() {
return poolMaximumActiveConnections;
}
public int getPoolMaximumIdleConnections() {
return poolMaximumIdleConnections;
}
public int getPoolMaximumLocalBadConnectionTolerance() {
return poolMaximumLocalBadConnectionTolerance;
}
public int getPoolMaximumCheckoutTime() {
return poolMaximumCheckoutTime;
}
public int getPoolTimeToWait() {
return poolTimeToWait;
}
public String getPoolPingQuery() {
return poolPingQuery;
}
public boolean isPoolPingEnabled() {
return poolPingEnabled;
}
public int getPoolPingConnectionsNotUsedFor() {
return poolPingConnectionsNotUsedFor;
}
/**
* Closes all active and idle connections in the pool.
*
* 关闭所有的 activeConnections 和 idleConnections 的连接
*/
public void forceCloseAll() {
synchronized (state) {
// 期望 Connection 的类型编码,通过-assembleConnectionTypeCode 计算得来。
expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
// 循环处理活跃的连接集合
for (int i = state.activeConnections.size(); i > 0; i--) {
try {
// 把活跃的连接移除。
PooledConnection conn = state.activeConnections.remove(i - 1);
// 把连接置为无效。
conn.invalidate();
// 获取数据库真实的连接。
Connection realConn = conn.getRealConnection();
// 回滚的相关操作
if (!realConn.getAutoCommit()) {
realConn.rollback();
}
// 关掉数据库真实的连接
realConn.close();
} catch (Exception e) {
// ignore
}
}
// 循环处理 闲置的连接集合
for (int i = state.idleConnections.size(); i > 0; i--) {
try {
PooledConnection conn = state.idleConnections.remove(i - 1);
conn.invalidate();
// 获取数据库真实的连接。然后再关了。
Connection realConn = conn.getRealConnection();
if (!realConn.getAutoCommit()) {
realConn.rollback();
}
realConn.close();
} catch (Exception e) {
// ignore
}
}
}
if (log.isDebugEnabled()) {
log.debug("PooledDataSource forcefully closed/removed all connections.");
}
}
public PoolState getPoolState() {
return state;
}
private int assembleConnectionTypeCode(String url, String username, String password) {
return ("" + url + username + password).hashCode();
}
/**
* 将使用完的连接,添加到闲置连接池中
*
* @param conn 连接
* @throws SQLException
*/
protected void pushConnection(PooledConnection conn) throws SQLException {
synchronized (state) {
// 从激活的连接集合中移除该连接
state.activeConnections.remove(conn);
// 通过 ping 来测试连接是否有效
if (conn.isValid()) {
// 连接是有效的
// 空闲连接数是否已达到上限,并且和当前连接池的标识匹配
if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
// 空闲连接数没达到上限。
state.accumulatedCheckoutTime += conn.getCheckoutTime();
// 回滚事务,避免使用方未提交或者回滚事务
if (!conn.getRealConnection().getAutoCommit()) {
conn.getRealConnection().rollback();
}
// 将连接添加到 idleConnections 中
PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
state.idleConnections.add(newConn);
newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
// 为什么这里要创建新的 PooledConnection 对象呢?避免使用方还在使用 conn ,通过将它设置为失效,万一再次调用,会抛出异常
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++;
}
}
}
/**
* 获取 org.apache.ibatis.datasource.pooled.PooledConnection 对象,
* 这是一个池化的连接。非常关键的一个方法
*
* 1:闲置和活跃的集合中,一个连接都没有。
* 第一次进来拿连接,:进入的是3(创建一个连接)——》把连接放到“活跃”的连接集合中去。
* 当连接使用完后,如果调用pushConnection方法,会把连接放到“闲置的”连接集合中去
*
* 2:闲置的连接池中,没有一个连接,但是活跃的连接池中,大于等于连接池允许的最大连接。
* 那么从活跃的连接池中,取第一个连接,如果取的过程没有超时,等待一会,进入while循环。如果超时了。
* 我们把取的这个连接置为无效,再新建一个连接。然后把连接放到“活跃” 连接池集合中去。
* 当连接使用完后,如果调用pushConnection方法,会把连接放到“闲置的”连接集合中去
*
* 3:如果“闲置的”连接池中有连接,从集合中取出第一个(remove)。然后放到活跃的集合中去。
* 当连接使用完后,如果调用pushConnection方法,会把连接放到“闲置的”连接集合中去。
*
* 基于上面。说明。闲置的连接是活跃的连接使用完后,做了还的操作。才放到了闲置的集合池中。
*
*
* @param username
* @param password
* @return
* @throws SQLException
*/
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) {
// 同步锁住 当前的 PoolState 对象。PoolState 对象把当前对象进行了存储。
synchronized (state) {
// 1: 如果当前 空闲连接非空
if (!state.idleConnections.isEmpty()) {
// Pool has available connection
// 连接池有连接,移除第个可用连接。并且把第一个可用连接返回给conn对象。
conn = state.idleConnections.remove(0);
if (log.isDebugEnabled()) {
log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
}
} else {
// 2:连接池没有可用的连接
// Pool does not have available connection
// 活跃的连接数,少于连接池最大的连接时,说明可以创建连接。那么我们创建一个连接
if (state.activeConnections.size() < poolMaximumActiveConnections) {
//3:Can create new connection-》能 创建一个新的连接。
conn = new PooledConnection(dataSource.getConnection(), this);
if (log.isDebugEnabled()) {
log.debug("Created connection " + conn.getRealHashCode() + ".");
}
} else {
// 4: 活跃的连接数,大于等于连接池允许的最大连接了。
// 不能创建新的连接
// 从 连接池仓库取活跃的第一个连接
PooledConnection oldestActiveConnection = state.activeConnections.get(0);
// 检查该连接是否超时
long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
if (longestCheckoutTime > poolMaximumCheckoutTime) {
// 连接获取超时了
// Can claim overdue connection
// // 对连接超时的时间的统计
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
// 向服务器ping下,检查下连接是否可用。
if (conn.isValid()) {
// 走到这里,说明连接是可用的
// 当自动模式被禁用时,使用 rollback 回滚模式
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;
// poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance 坏连接容忍次数。本地坏连接超过这个就异常
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.");
}
}
}
// 同步代码块结束
}
// while 循环结束
// 如果conn 是空,会继续进行循环。
}
// 到这里如果连接还是空话,那么抛出异常。
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;
}
/**
* Method to check to see if a connection is still usable
* 通过向数据库发起 poolPingQuery 语句来发起“ping”操作,以判断数据库连接是否有效
* @param conn - the connection to check
* @return True if the connection is still usable
*/
protected boolean pingConnection(PooledConnection conn) {
// 记录是否 ping 成功
boolean result = true;
// 判断真实的连接是否已经关闭。若已关闭,就意味着 ping 肯定是失败的。
try {
result = !conn.getRealConnection().isClosed();
} catch (SQLException e) {
if (log.isDebugEnabled()) {
log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());
}
result = false;
}
// poolPingEnabled启用了侦测查询,长时间未使用 才发起ping
if (result && poolPingEnabled && poolPingConnectionsNotUsedFor >= 0
&& conn.getTimeElapsedSinceLastUse() > poolPingConnectionsNotUsedFor) {
try {
if (log.isDebugEnabled()) {
log.debug("Testing connection " + conn.getRealHashCode() + " ...");
}
// 通过执行 poolPingQuery 语句来发起 ping
Connection realConn = conn.getRealConnection();
try (Statement statement = realConn.createStatement()) {
statement.executeQuery(poolPingQuery).close();
}
if (!realConn.getAutoCommit()) {
realConn.rollback();
}
// 标记执行成功
result = true;
if (log.isDebugEnabled()) {
log.debug("Connection " + conn.getRealHashCode() + " is GOOD!");
}
} catch (Exception e) {
log.warn("Execution of ping query '" + poolPingQuery + "' failed: " + e.getMessage());
try {
// 关闭数据库真实的连接
conn.getRealConnection().close();
} catch (Exception e2) {
// ignore
}
// 标记执行失败
result = false;
if (log.isDebugEnabled()) {
log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());
}
}
}
return result;
}
/**
* Unwraps a pooled connection to get to the 'real' connection
* 获取真实的数据库连接
* @param conn - the pooled connection to unwrap
* @return The 'real' connection
*/
public static Connection unwrapConnection(Connection conn) {
// 如果传入的是被代理的连接
if (Proxy.isProxyClass(conn.getClass())) {
// 如果传入的是被代理的连接
InvocationHandler handler = Proxy.getInvocationHandler(conn);
// 如果是 PooledConnection 对象,则获取真实的连接
if (handler instanceof PooledConnection) {
return ((PooledConnection) handler).getRealConnection();
}
}
return conn;
}
@Override
protected void finalize() throws Throwable {
forceCloseAll();
super.finalize();
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
throw new SQLException(getClass().getName() + " is not a wrapper.");
}
@Override
public boolean isWrapperFor(Class<?> iface) {
return false;
}
@Override
public Logger getParentLogger() {
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
}
}
PoolState
-
/** * Copyright 2009-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.ibatis.datasource.pooled; import java.util.ArrayList; import java.util.List; /** * @author Clinton Begin */ public class PoolState { /** * 所属的 PooledDataSource 对象 */ protected PooledDataSource dataSource; /** * 闲置连接集合 */ protected final List<PooledConnection> idleConnections = new ArrayList<>(); /** * 活跃连接集合 */ protected final List<PooledConnection> activeConnections = new ArrayList<>(); /** * 全局统计 - 获取连接的次数 */ protected long requestCount = 0; /** * 全局统计 - 获取连接的时间 */ protected long accumulatedRequestTime = 0; /** * 全局统计 - 获取到连接非超时 + 超时的占用时长 * * 所以,包括 {@link #accumulatedCheckoutTimeOfOverdueConnections} 部分 */ protected long accumulatedCheckoutTime = 0; /** * 全局统计 - 获取到连接超时的次数 */ protected long claimedOverdueConnectionCount = 0; /** * 全局统计 - 获取到连接超时的占用时长 */ protected long accumulatedCheckoutTimeOfOverdueConnections = 0; /** * 全局统计 - 等待连接的时间 */ protected long accumulatedWaitTime = 0; /** * 全局统计 - 等待连接的次数 */ protected long hadToWaitCount = 0; /** * 全局统计 - 获取到坏的连接的次数 */ protected long badConnectionCount = 0; public PoolState(PooledDataSource dataSource) { this.dataSource = dataSource; } public synchronized long getRequestCount() { return requestCount; } public synchronized long getAverageRequestTime() { return requestCount == 0 ? 0 : accumulatedRequestTime / requestCount; } public synchronized long getAverageWaitTime() { return hadToWaitCount == 0 ? 0 : accumulatedWaitTime / hadToWaitCount; } public synchronized long getHadToWaitCount() { return hadToWaitCount; } public synchronized long getBadConnectionCount() { return badConnectionCount; } public synchronized long getClaimedOverdueConnectionCount() { return claimedOverdueConnectionCount; } public synchronized long getAverageOverdueCheckoutTime() { return claimedOverdueConnectionCount == 0 ? 0 : accumulatedCheckoutTimeOfOverdueConnections / claimedOverdueConnectionCount; } public synchronized long getAverageCheckoutTime() { return requestCount == 0 ? 0 : accumulatedCheckoutTime / requestCount; } public synchronized int getIdleConnectionCount() { return idleConnections.size(); } public synchronized int getActiveConnectionCount() { return activeConnections.size(); } @Override public synchronized String toString() { StringBuilder builder = new StringBuilder(); builder.append("\n===CONFINGURATION=============================================="); builder.append("\n jdbcDriver ").append(dataSource.getDriver()); builder.append("\n jdbcUrl ").append(dataSource.getUrl()); builder.append("\n jdbcUsername ").append(dataSource.getUsername()); builder.append("\n jdbcPassword ").append(dataSource.getPassword() == null ? "NULL" : "************"); builder.append("\n poolMaxActiveConnections ").append(dataSource.poolMaximumActiveConnections); builder.append("\n poolMaxIdleConnections ").append(dataSource.poolMaximumIdleConnections); builder.append("\n poolMaxCheckoutTime ").append(dataSource.poolMaximumCheckoutTime); builder.append("\n poolTimeToWait ").append(dataSource.poolTimeToWait); builder.append("\n poolPingEnabled ").append(dataSource.poolPingEnabled); builder.append("\n poolPingQuery ").append(dataSource.poolPingQuery); builder.append("\n poolPingConnectionsNotUsedFor ").append(dataSource.poolPingConnectionsNotUsedFor); builder.append("\n ---STATUS-----------------------------------------------------"); builder.append("\n activeConnections ").append(getActiveConnectionCount()); builder.append("\n idleConnections ").append(getIdleConnectionCount()); builder.append("\n requestCount ").append(getRequestCount()); builder.append("\n averageRequestTime ").append(getAverageRequestTime()); builder.append("\n averageCheckoutTime ").append(getAverageCheckoutTime()); builder.append("\n claimedOverdue ").append(getClaimedOverdueConnectionCount()); builder.append("\n averageOverdueCheckoutTime ").append(getAverageOverdueCheckoutTime()); builder.append("\n hadToWait ").append(getHadToWaitCount()); builder.append("\n averageWaitTime ").append(getAverageWaitTime()); builder.append("\n badConnectionCount ").append(getBadConnectionCount()); builder.append("\n==============================================================="); return builder.toString(); } }
PooledConnection
/**
* Copyright 2009-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.datasource.pooled;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
import org.apache.ibatis.reflection.ExceptionUtil;
/**
* @author Clinton Begin
*/
class PooledConnection implements InvocationHandler {
/**
* 关闭 Connection 方法名
*/
private static final String CLOSE = "close";
/**
* JDK Proxy 的接口
*/
private static final Class<?>[] IFACES = new Class<?>[] { Connection.class };
/**
* 对象的标识,基于 {@link #realConnection} 求 hashCode
*/
private final int hashCode;
/**
* 所属的 PooledDataSource 对象
*/
private final PooledDataSource dataSource;
/**
* 真实的 Connection 连接
*/
private final Connection realConnection;
/**
* 代理的 Connection 连接,即 {@link PooledConnection} 这个动态代理的 Connection 对象
*/
private final Connection proxyConnection;
/**
* 从连接池中,获取走的时间戳
*/
private long checkoutTimestamp;
/**
* 对象创建时间
*/
private long createdTimestamp;
/**
* 最后更新时间
*/
private long lastUsedTimestamp;
/**
* 连接的标识,即 {@link PooledDataSource#expectedConnectionTypeCode}
*/
private int connectionTypeCode;
/**
* 是否有效
*/
private boolean valid;
/**
* Constructor for SimplePooledConnection that uses the Connection and PooledDataSource passed in.
*
* @param connection
* - the connection that is to be presented as a pooled connection
* @param dataSource
* - the dataSource that the connection is from
*/
public PooledConnection(Connection connection, PooledDataSource dataSource) {
this.hashCode = connection.hashCode();
this.realConnection = connection;
this.dataSource = dataSource;
this.createdTimestamp = System.currentTimeMillis();
this.lastUsedTimestamp = System.currentTimeMillis();
this.valid = true;
// 代理的 Connection 连接, 基于 JDK Proxy 创建 Connection 对象,并且 handler 对象就是 this 也就是自己。
// 意味着后续对 proxyConnection 的所有方法调用,都会委托给 PooledConnection#invoke
this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
}
/**
* Invalidates the connection.
*/
public void invalidate() {
valid = false;
}
/**
* Method to see if the connection is usable.
*
* 检查连接释放可用的方法
* @return True if the connection is usable
*/
public boolean isValid() {
return valid && realConnection != null && dataSource.pingConnection(this);
}
/**
* Getter for the *real* connection that this wraps.
*
* @return The connection
*/
public Connection getRealConnection() {
return realConnection;
}
/**
* Getter for the proxy for the connection.
*
* @return The proxy
*/
public Connection getProxyConnection() {
return proxyConnection;
}
/**
* Gets the hashcode of the real connection (or 0 if it is null).
*
* @return The hashcode of the real connection (or 0 if it is null)
*/
public int getRealHashCode() {
return realConnection == null ? 0 : realConnection.hashCode();
}
/**
* Getter for the connection type (based on url + user + password).
*
* @return The connection type
*/
public int getConnectionTypeCode() {
return connectionTypeCode;
}
/**
* Setter for the connection type.
*
* 连接类型的set方法
* @param connectionTypeCode
* - the connection type
*/
public void setConnectionTypeCode(int connectionTypeCode) {
this.connectionTypeCode = connectionTypeCode;
}
/**
* Getter for the time that the connection was created.
*
* @return The creation timestamp
*/
public long getCreatedTimestamp() {
return createdTimestamp;
}
/**
* Setter for the time that the connection was created.
*
* @param createdTimestamp
* - the timestamp
*/
public void setCreatedTimestamp(long createdTimestamp) {
this.createdTimestamp = createdTimestamp;
}
/**
* Getter for the time that the connection was last used.
*
* @return - the timestamp
*/
public long getLastUsedTimestamp() {
return lastUsedTimestamp;
}
/**
* Setter for the time that the connection was last used.
*
* @param lastUsedTimestamp
* - the timestamp
*/
public void setLastUsedTimestamp(long lastUsedTimestamp) {
this.lastUsedTimestamp = lastUsedTimestamp;
}
/**
* Getter for the time since this connection was last used.
*
* @return - the time since the last use
*/
public long getTimeElapsedSinceLastUse() {
return System.currentTimeMillis() - lastUsedTimestamp;
}
/**
* Getter for the age of the connection.
*
* @return the age
*/
public long getAge() {
return System.currentTimeMillis() - createdTimestamp;
}
/**
* Getter for the timestamp that this connection was checked out.
*
* @return the timestamp
*/
public long getCheckoutTimestamp() {
return checkoutTimestamp;
}
/**
* Setter for the timestamp that this connection was checked out.
*
* @param timestamp
* the timestamp
*/
public void setCheckoutTimestamp(long timestamp) {
this.checkoutTimestamp = timestamp;
}
/**
* Getter for the time that this connection has been checked out.
*
* @return the time
*/
public long getCheckoutTime() {
return System.currentTimeMillis() - checkoutTimestamp;
}
@Override
public int hashCode() {
return hashCode;
}
/**
* Allows comparing this connection to another.
*
* @param obj
* - the other connection to test for equality
* @see Object#equals(Object)
*/
@Override
public boolean equals(Object obj) {
if (obj instanceof PooledConnection) {
return realConnection.hashCode() == ((PooledConnection) obj).realConnection.hashCode();
} else if (obj instanceof Connection) {
return hashCode == obj.hashCode();
} else {
return false;
}
}
/**
* Required for InvocationHandler implementation.
*
*
* TODO 没太理解。
*
* 重点分析invoke方法,这里代理了Connection对象的close方法,是实现连接池功能的关键
*
* @param proxy
* - not used 代理对象
* @param method
* - the method to be executed 方法
* @param args
* - the parameters to be passed to the method 参数
* @see java.lang.reflect.InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// <1> 判断是否为 CLOSE 方法,则将连接放回到连接池中,避免连接被关闭
String methodName = method.getName();
if (CLOSE.equals(methodName)) {
dataSource.pushConnection(this);
return null;
}
try {
// <2.1> 判断非 Object 的方法,则先检查连接是否可用
if (!Object.class.equals(method.getDeclaringClass())) {
// issue #579 toString() should never fail
// throw an SQLException instead of a Runtime
checkConnection();
}
// <2.2> 反射调用对应的方法
return method.invoke(realConnection, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
private void checkConnection() throws SQLException {
if (!valid) {
throw new SQLException("Error accessing PooledConnection. Connection is invalid.");
}
}
}
PooledDataSourceFactory
package org.apache.ibatis.datasource.pooled;
import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
/**
*
* 池化的DataSourceFactory 类
*
*
* @author Clinton Begin
*/
public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
public PooledDataSourceFactory() {
this.dataSource = new PooledDataSource();
}
}
总结:package org.apache.ibatis.datasource.pooled 这个包的重点是什么。
类总共有4个,
PooledDataSourceFactory 类就只是提供了默认的构造方法,创建了dataSource。
PooledDataSource(这是一个简单的,同步的,线程安全的数据库连接池),比较主要的是popConnection方法,里面把创建的线程往活跃线程对象进行了设置。pushConnection方法,把使用完了线程进行了归还,归还后的线程放到了全局的闲置线程连接对象中。popConnection拿的是一个“PooledConnection”对象,拿连接的时候。
PooledConnection 这个的构造方法中。全局属性中设置了一个代理对象。当前类实现了InvocationHandler 接口,这个接口是做动态代理用的。目录我没有想明白,这个地方为什么要用动态代理。
三: PooledConnection 对象中使用了jkd的动态代理。
实话说。我昨天看的时候,又忘记动态代理是什么。看了下这篇文章。略有所获,分享给大家。https://www.cnblogs.com/liujiarui/p/12408742.html
不能说自己完全看懂了。至少知道动态代理生成是基于接口的,通过字节码生成的代理对象和proxy对象在同一个目录。invoke方法是会在每个代理方法中都进行植入的。如
//抽象主题
interface AbstractSubject
{
void request();
}
//JDK动态代理最终会为我们生成如下结构的代理类
class Proxy0 extends Proxy implements AbstractSubject {
//第一步生成构造器函数
protected Proxy0(InvocationHandler h) {
super(h);
}
//第二步, 生成静态域
private static Method m1; //hashCode方法
private static Method m2; //equals方法
private static Method m3; //toString方法
private static Method m4; //抽象接口中的request方法
//第三步, 生成代理方法(四个)
@Override
public int hashCode() {
try {
return (int) h.invoke(this, m1, null);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public boolean equals(Object obj) {
try {
Object[] args = new Object[] {obj};
return (boolean) h.invoke(this, m2, args);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public String toString() {
try {
return (String) h.invoke(this, m3, null);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public void request() {
try {
//构造参数数组, 如果request方法有多个参数,直接向{}里面添加参数就行了
Object[] args = new Object[] {};
h.invoke(this, m4, args);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
//第四步, 生成静态初始化方法
static {
try {
Class c1 = Class.forName(Object.class.getName());
Class c2 = Class.forName(AbstractSubject.class.getName());
m1 = c1.getMethod("hashCode", null);
m2 = c1.getMethod("equals", new Class[]{Object.class});
m3 = c1.getMethod("toString", null);
//注意参数request方法有多个参数,直接向{}里面添加参数就行了,因为request方法中没有参数,直接传一个null就行
m4 = c2.getMethod("save", new Class[]{});
//...
} catch (Exception e) {
e.printStackTrace();
}
}
}
1.代理类默认继承Porxy类,因为Java中只支持单继承,所以JDK动态代理只能去实现接口。
2.代理方法都会去调用InvocationHandler的invoke()方法,因此我们需要重写InvocationHandler的invoke()方法。
3.调用invoke()方法时会传入代理实例本身,目标方法和目标方法参数。解释了invoke()方法的参数是怎样来的。
四: unpooled 模块
UnpooledDataSourceFactory
非池化的DataSourceFactory实现类
/**
* Copyright 2009-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.datasource.unpooled;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.ibatis.datasource.DataSourceException;
import org.apache.ibatis.datasource.DataSourceFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
/**
*
* 非池化的DataSourceFactory实现类
*
* 这个数据源的实现知识每次被请求时打开和关闭连接,虽然有点慢,但对于在数据库连接可用性不高的应用程序,
* 这是个很好的选择,不同的数据库在性能方面的表现也是不一样的,对于某些数据库来说,使用连接池不是很重要。
* 那么这个数据源就很适合。
*
* @author Clinton Begin
*/
public class UnpooledDataSourceFactory implements DataSourceFactory {
private static final String DRIVER_PROPERTY_PREFIX = "driver.";
private static final int DRIVER_PROPERTY_PREFIX_LENGTH = DRIVER_PROPERTY_PREFIX.length();
protected DataSource dataSource;
public UnpooledDataSourceFactory() {
// 创建 UnpooledDataSource 对象,啥属性也没有。
this.dataSource = new UnpooledDataSource();
}
/**
* 设置数据源的属性
* @param properties
*/
@Override
public void setProperties(Properties properties) {
Properties driverProperties = new Properties();
MetaObject metaDataSource = SystemMetaObject.forObject(dataSource);
for (Object key : properties.keySet()) {
String propertyName = (String) key;
if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) {
// 初始化到 driverProperties 中
String value = properties.getProperty(propertyName);
driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
} else if (metaDataSource.hasSetter(propertyName)) {
// 初始化到 MetaObject 中
String value = (String) properties.get(propertyName);
// 转成字段对应的类型后,把值填充到属性中。
Object convertedValue = convertValue(metaDataSource, propertyName, value);
metaDataSource.setValue(propertyName, convertedValue);
} else {
throw new DataSourceException("Unknown DataSource property: " + propertyName);
}
}
// 设置 driverProperties 到 MetaObject 中
if (driverProperties.size() > 0) {
metaDataSource.setValue("driverProperties", driverProperties);
}
}
@Override
public DataSource getDataSource() {
return dataSource;
}
/**
* 根据
* @param metaDataSource mybatis 发射器拿到的 MetaObject对象
* @param propertyName 属性字段
* @param value 值
* @return 把value转成属性类型的值对象。
*/
private Object convertValue(MetaObject metaDataSource, String propertyName, String value) {
Object convertedValue = value;
Class<?> targetType = metaDataSource.getSetterType(propertyName);
if (targetType == Integer.class || targetType == int.class) {
convertedValue = Integer.valueOf(value);
} else if (targetType == Long.class || targetType == long.class) {
convertedValue = Long.valueOf(value);
} else if (targetType == Boolean.class || targetType == boolean.class) {
convertedValue = Boolean.valueOf(value);
}
return convertedValue;
}
}
UnpooledDataSource
/**
* Copyright 2009-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.datasource.unpooled;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
import javax.sql.DataSource;
import org.apache.ibatis.io.Resources;
/**
*
*
*
* @author Clinton Begin
* @author Eduardo Macarron
*/
public class UnpooledDataSource implements DataSource {
private ClassLoader driverClassLoader;
private Properties driverProperties;
/**
* 已注册的 Driver 映射
*
* KEY:Driver 类名
* VALUE:Driver 对象
*/
private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<>();
/**
* Driver 类加载器
*/
private String driver;
/**
* 这是数据库的 JDBC URL 地址。
*/
private String url;
/**
* 登录数据库的用户名。
*/
private String username;
/**
* 登录数据库的密码。
*/
private String password;
private Boolean autoCommit;
/**
* 默认的连接事务隔离级别
*/
private Integer defaultTransactionIsolationLevel;
/**
*/
private Integer defaultNetworkTimeout;
/**
* 静态块,启动的时候加载
*/
static {
// 拿到应用程序启动后,所有的driver 对象。
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver driver = drivers.nextElement();
registeredDrivers.put(driver.getClass().getName(), driver);
}
}
public UnpooledDataSource() {
}
public UnpooledDataSource(String driver, String url, String username, String password) {
this.driver = driver;
this.url = url;
this.username = username;
this.password = password;
}
public UnpooledDataSource(String driver, String url, Properties driverProperties) {
this.driver = driver;
this.url = url;
this.driverProperties = driverProperties;
}
public UnpooledDataSource(ClassLoader driverClassLoader, String driver, String url, String username, String password) {
this.driverClassLoader = driverClassLoader;
this.driver = driver;
this.url = url;
this.username = username;
this.password = password;
}
public UnpooledDataSource(ClassLoader driverClassLoader, String driver, String url, Properties driverProperties) {
this.driverClassLoader = driverClassLoader;
this.driver = driver;
this.url = url;
this.driverProperties = driverProperties;
}
@Override
public Connection getConnection() throws SQLException {
return doGetConnection(username, password);
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return doGetConnection(username, password);
}
@Override
public void setLoginTimeout(int loginTimeout) {
DriverManager.setLoginTimeout(loginTimeout);
}
@Override
public int getLoginTimeout() {
return DriverManager.getLoginTimeout();
}
@Override
public void setLogWriter(PrintWriter logWriter) {
DriverManager.setLogWriter(logWriter);
}
@Override
public PrintWriter getLogWriter() {
return DriverManager.getLogWriter();
}
public ClassLoader getDriverClassLoader() {
return driverClassLoader;
}
public void setDriverClassLoader(ClassLoader driverClassLoader) {
this.driverClassLoader = driverClassLoader;
}
public Properties getDriverProperties() {
return driverProperties;
}
public void setDriverProperties(Properties driverProperties) {
this.driverProperties = driverProperties;
}
public synchronized String getDriver() {
return driver;
}
public synchronized void setDriver(String driver) {
this.driver = driver;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Boolean isAutoCommit() {
return autoCommit;
}
public void setAutoCommit(Boolean autoCommit) {
this.autoCommit = autoCommit;
}
public Integer getDefaultTransactionIsolationLevel() {
return defaultTransactionIsolationLevel;
}
public void setDefaultTransactionIsolationLevel(Integer defaultTransactionIsolationLevel) {
this.defaultTransactionIsolationLevel = defaultTransactionIsolationLevel;
}
/**
* Gets the default network timeout.
*
* @return the default network timeout
* @since 3.5.2
*/
public Integer getDefaultNetworkTimeout() {
return defaultNetworkTimeout;
}
/**
* Sets the default network timeout value to wait for the database operation to complete. See {@link Connection#setNetworkTimeout(java.util.concurrent.Executor, int)}
*
* @param defaultNetworkTimeout
* The time in milliseconds to wait for the database operation to complete.
* @since 3.5.2
*/
public void setDefaultNetworkTimeout(Integer defaultNetworkTimeout) {
this.defaultNetworkTimeout = defaultNetworkTimeout;
}
/**
* 获取 Connection 连接
*
* @param username 用户名
* @param password 密码
* @return 数据库连接
* @throws SQLException 异常
*/
private Connection doGetConnection(String username, String password) throws SQLException {
// 创建 Properties 对象
Properties props = new Properties();
// 设置 driverProperties 到 props 中
if (driverProperties != null) {
props.putAll(driverProperties);
}
// 设置 user 和 password 到 props 中
if (username != null) {
props.setProperty("user", username);
}
if (password != null) {
props.setProperty("password", password);
}
// 执行获得 Connection 连接
return doGetConnection(props);
}
private Connection doGetConnection(Properties properties) throws SQLException {
// <1> 初始化 Driver
initializeDriver();
// <2> 获得 Connection 对象
Connection connection = DriverManager.getConnection(url, properties);
// <3> 配置 Connection 对象
configureConnection(connection);
return connection;
}
/**
* 初始化 Driver
* @throws SQLException
*/
private synchronized void initializeDriver() throws SQLException {
// 判断 registeredDrivers 是否已经存在该 driver ,若不存在,进行初始化
if (!registeredDrivers.containsKey(driver)) {
Class<?> driverType;
try {
// <2> 获得 driver 类
if (driverClassLoader != null) {
driverType = Class.forName(driver, true, driverClassLoader);
} else {
driverType = Resources.classForName(driver);
}
// <3> 创建 Driver 对象
// DriverManager requires the driver to be loaded via the system ClassLoader.
// http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
Driver driverInstance = (Driver) driverType.getDeclaredConstructor().newInstance();
// 创建 DriverProxy 对象,并注册到 DriverManager 中
DriverManager.registerDriver(new DriverProxy(driverInstance));
registeredDrivers.put(driver, driverInstance);
} catch (Exception e) {
throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
}
}
}
/**
* 配置 Connection 对象
* @param conn
* @throws SQLException
*/
private void configureConnection(Connection conn) throws SQLException {
// Executors.newSingleThreadExecutor() 用来处理setNetworkTimeout的操作。defaultNetworkTimeout 等待数据库操作的时间
if (defaultNetworkTimeout != null) {
conn.setNetworkTimeout(Executors.newSingleThreadExecutor(), defaultNetworkTimeout);
}
// 设置自动提交
if (autoCommit != null && autoCommit != conn.getAutoCommit()) {
conn.setAutoCommit(autoCommit);
}
// 设置事务隔离级别
if (defaultTransactionIsolationLevel != null) {
conn.setTransactionIsolation(defaultTransactionIsolationLevel);
}
}
/**
* 为什么此处会有使用 DriverProxy 呢?
*
*
* 因为 <4> 处,使用 MyBatis 自定义的 Logger 对象。
* 其他方法,实际就是直接调用 driver 对应的方法。
*/
private static class DriverProxy implements Driver {
private Driver driver;
DriverProxy(Driver d) {
this.driver = d;
}
@Override
public boolean acceptsURL(String u) throws SQLException {
return this.driver.acceptsURL(u);
}
@Override
public Connection connect(String u, Properties p) throws SQLException {
return this.driver.connect(u, p);
}
@Override
public int getMajorVersion() {
return this.driver.getMajorVersion();
}
@Override
public int getMinorVersion() {
return this.driver.getMinorVersion();
}
@Override
public DriverPropertyInfo[] getPropertyInfo(String u, Properties p) throws SQLException {
return this.driver.getPropertyInfo(u, p);
}
@Override
public boolean jdbcCompliant() {
return this.driver.jdbcCompliant();
}
@Override
public Logger getParentLogger() {
// 因为 <4> 处,使用 MyBatis 自定义的 Logger 对象。只有这一块的是不一样的。我猜这就是他没有直接用driver的原因。
// 可是为啥非要重写这个不可呢?我还没找到原因。todo
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
}
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
throw new SQLException(getClass().getName() + " is not a wrapper.");
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public Logger getParentLogger() {
// requires JDK version 1.6
return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
}
}
总结下:这个模块的功能作用是什么?
这个模块的dataSource 其实在PooledDataSource中是有被引用到的,PooledDataSource 这样做是为了提高代码的重用性。这个模块是非池化的数据库连接。说明这个模块的数据库连接适用那些获取数据库连接较小的应用。
五:jndi 模块
嘻嘻。我一看jndi感觉很熟悉,但却又感觉到陌生。我这里把记录对比下一下。
* JDBC(Java Database Connectivity)是由数据库中间服务商提供的,用于连接数据库的Java API。一组类和接口(对接数据库)。
* JNDI(Java Name Directory Interface)是为应用服务器(Tomcat)管理资源所设置的目录样式的唯一标识。(数据库、网页、文档等)
/**
* Copyright 2009-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.datasource.jndi;
import java.util.Map.Entry;
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import org.apache.ibatis.datasource.DataSourceException;
import org.apache.ibatis.datasource.DataSourceFactory;
/**
* JDBC(Java Database Connectivity)是由数据库中间服务商提供的,用于连接数据库的Java API。一组类和接口(对接数据库)。
* JNDI(Java Name Directory Interface)是为应用服务器(Tomcat)管理资源所设置的目录样式的唯一标识。(数据库、网页、文档等)
*
* 这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,
* 容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。这种数据源配置只需要两个属性
*
* @author Clinton Begin
*/
public class JndiDataSourceFactory implements DataSourceFactory {
// 这个属性用来在 InitialContext 中寻找上下文
// (即,initialContext.lookup(initial_context))。这是个可选属性,
// 如果忽略,那么 data_source 属性将会直接从 InitialContext 中寻找。
public static final String INITIAL_CONTEXT = "initial_context";
// 这是引用数据源实例位置的上下文的路径。提供了 initial_context
// 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。
public static final String DATA_SOURCE = "data_source";
public static final String ENV_PREFIX = "env.";
private DataSource dataSource;
/**
* 在此方法中构造 dataSource
*
* 设置 DataSource 对象的属性
* @param properties 属性
*/
@Override
public void setProperties(Properties properties) {
try {
InitialContext initCtx;
Properties env = getEnvProperties(properties);
// 创建 InitialContext 对象
if (env == null) {
initCtx = new InitialContext();
} else {
initCtx = new InitialContext(env);
}
// 从 InitialContext 上下文中,获取 DataSource 对象
if (properties.containsKey(INITIAL_CONTEXT) && properties.containsKey(DATA_SOURCE)) {
Context ctx = (Context) initCtx.lookup(properties.getProperty(INITIAL_CONTEXT));
dataSource = (DataSource) ctx.lookup(properties.getProperty(DATA_SOURCE));
} else if (properties.containsKey(DATA_SOURCE)) {
dataSource = (DataSource) initCtx.lookup(properties.getProperty(DATA_SOURCE));
}
} catch (NamingException e) {
throw new DataSourceException("There was an error configuring JndiDataSourceTransactionPool. Cause: " + e, e);
}
}
/**
* 获得 DataSource 对象
*
* @return DataSource 对象
*/
@Override
public DataSource getDataSource() {
return dataSource;
}
/**
* 获取系统的Properties 对象
* @param allProps
* @return
*/
private static Properties getEnvProperties(Properties allProps) {
final String PREFIX = ENV_PREFIX;
Properties contextProperties = null;
for (Entry<Object, Object> entry : allProps.entrySet()) {
String key = (String) entry.getKey();
String value = (String) entry.getValue();
if (key.startsWith(PREFIX)) {
if (contextProperties == null) {
contextProperties = new Properties();
}
contextProperties.put(key.substring(PREFIX.length()), value);
}
}
return contextProperties;
}
}
六: 其他的类。都是比较简单的
DataSourceException (异常类),
DataSourceFactory(数据源接口)
看完后,我不知道我学到了什么。但是我感觉pooled 包下的稍微有点点难。可能现在看这些是为了后续其他模块作铺垫也不一定。
今天看jdk动态代理的,那个动态代理的一二级缓存。没太懂。但是想想目前的总方向是了解jdk动态代理是怎么回事,然后需要继续看mybatis的代码。我就想先放一放。明天就是大年30,你们回家了吗?我没有。我此刻还在工作岗位。但是值得开心的事是,今天下午3点可以走了的。嘻嘻。更开心的事。昨天把数据源的代码看完了。今天把这一块的博客写了。觉得这块心上石放下了。
“小的时候痛会哭,因为有父母的庇护,有他们在我就是个孩子。往后的岁月不知道自己会怎么样。我想自己努力给家人最好的模样。”