dbcp BasicDataSource 連線池獲取連線過程原始碼分析
阿新 • • 發佈:2019-01-31
通過ibatis,獲取資料庫連線的debug跟蹤如下:
核心指出在於BasicDataSource.getConnection()方法。看下該方法:
/** * Create (if necessary) and return a connection to the database. * * @throws SQLException if a database access error occurs * @return a database connection */ public Connection getConnection() throws SQLException { return <strong>createDataSource().getConnection();</strong> }
createDataSource方法如下:
該方法是個同步方法。/** * <p>Create (if necessary) and return the internal data source we are * using to manage our connections.</p> * * <p><strong>IMPLEMENTATION NOTE</strong> - It is tempting to use the * "double checked locking" idiom in an attempt to avoid synchronizing * on every single call to this method. However, this idiom fails to * work correctly in the face of some optimizations that are legal for * a JVM to perform.</p> * * @throws SQLException if the object pool cannot be created. */ protected synchronized DataSource createDataSource() throws SQLException { if (closed) { throw new SQLException("Data source is closed"); } // Return the pool if we have already created it if (dataSource != null) { return (dataSource); } // create factory which returns raw physical connections <strong>ConnectionFactory driverConnectionFactory = createConnectionFactory();</strong> // create a pool for our connections <strong>createConnectionPool();</strong> // Set up statement pool, if desired GenericKeyedObjectPoolFactory statementPoolFactory = null; if (isPoolPreparedStatements()) { statementPoolFactory = new GenericKeyedObjectPoolFactory(null, -1, // unlimited maxActive (per key) GenericKeyedObjectPool.WHEN_EXHAUSTED_FAIL, 0, // maxWait 1, // maxIdle (per key) maxOpenPreparedStatements); } // Set up the poolable connection factory <strong>createPoolableConnectionFactory(driverConnectionFactory, statementPoolFactory, abandonedConfig);</strong> // Create and return the pooling data source to manage the connections <strong>createDataSourceInstance();</strong> try { for (int i = 0 ; i < initialSize ; i++) { connectionPool.addObject(); } } catch (Exception e) { throw new SQLNestedException("Error preloading the connection pool", e); } return dataSource; }
1.首先判斷是否連線池關閉。
2.判斷是不是已經存在dataSource,如果有直接返回。
3.通過createConnectionFactory()方法建立連線工廠。
createConnectionFactory方法:
首先是初始化jdbc驅動程式。/** * Creates a JDBC connection factory for this datasource. This method only * exists so subclasses can replace the implementation class. */ protected ConnectionFactory createConnectionFactory() throws SQLException { // Load the JDBC driver class Class driverFromCCL = null; if (driverClassName != null) { try { try { if (driverClassLoader == null) { Class.forName(driverClassName); } else { Class.forName(driverClassName, true, driverClassLoader); } } catch (ClassNotFoundException cnfe) { driverFromCCL = Thread.currentThread( ).getContextClassLoader().loadClass( driverClassName); } } catch (Throwable t) { String message = "Cannot load JDBC driver class '" + driverClassName + "'"; logWriter.println(message); t.printStackTrace(logWriter); throw new SQLNestedException(message, t); } } // Create a JDBC driver instance Driver driver = null; try { if (driverFromCCL == null) { driver = DriverManager.getDriver(url); } else { // Usage of DriverManager is not possible, as it does not // respect the ContextClassLoader driver = (Driver) driverFromCCL.newInstance(); if (!driver.acceptsURL(url)) { throw new SQLException("No suitable driver", "08001"); } } } catch (Throwable t) { String message = "Cannot create JDBC driver of class '" + (driverClassName != null ? driverClassName : "") + "' for connect URL '" + url + "'"; logWriter.println(message); t.printStackTrace(logWriter); throw new SQLNestedException(message, t); } // Can't test without a validationQuery if (validationQuery == null) { setTestOnBorrow(false); setTestOnReturn(false); setTestWhileIdle(false); } // Set up the driver connection factory we will use String user = username; if (user != null) { connectionProperties.put("user", user); } else { log("DBCP DataSource configured without a 'username'"); } String pwd = password; if (pwd != null) { connectionProperties.put("password", pwd); } else { log("DBCP DataSource configured without a 'password'"); } <strong> ConnectionFactory driverConnectionFactory = new DriverConnectionFactory(driver, url, connectionProperties);</strong> return driverConnectionFactory; }
最後2行程式碼是建立驅動連線工廠ConnectionFactory。
DriverConnectionFactory類很簡單,主要的方法是createConnection,方法如下:
public Connection createConnection() throws SQLException {
return <strong>_driver.connect(_connectUri,_props)</strong>;
}
使用jdbc的Driver實現類來建立資料庫連線。4.下一步是createConnectionPool(),即建立連線池,程式碼如下:
protected void createConnectionPool() {
// Create an object pool to contain our active connections
GenericObjectPool gop;
if ((abandonedConfig != null) && (abandonedConfig.getRemoveAbandoned())) {
gop = new AbandonedObjectPool(null,abandonedConfig);
}
else {
<strong>gop = new GenericObjectPool();</strong>
}
gop.setMaxActive(maxActive);
gop.setMaxIdle(maxIdle);
gop.setMinIdle(minIdle);
gop.setMaxWait(maxWait);
gop.setTestOnBorrow(testOnBorrow);
gop.setTestOnReturn(testOnReturn);
gop.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
gop.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
gop.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
gop.setTestWhileIdle(testWhileIdle);
<strong>connectionPool = gop;</strong>
}
5.再下一步是建立連線池中的連線工廠類
createPoolableConnectionFactory方法中的
/**
* Creates the PoolableConnectionFactory and attaches it to the connection pool. This method only exists
* so subclasses can replace the default implementation.
*
* @param driverConnectionFactory JDBC connection factory
* @param statementPoolFactory statement pool factory (null if statement pooling is turned off)
* @param configuration abandoned connection tracking configuration (null if no tracking)
* @throws SQLException if an error occurs creating the PoolableConnectionFactory
*/
protected void createPoolableConnectionFactory(ConnectionFactory driverConnectionFactory,
KeyedObjectPoolFactory statementPoolFactory, AbandonedConfig configuration) throws SQLException {
PoolableConnectionFactory connectionFactory = null;
try {
connectionFactory =
new PoolableConnectionFactory(driverConnectionFactory,
connectionPool,
statementPoolFactory,
validationQuery,
validationQueryTimeout,
connectionInitSqls,
defaultReadOnly,
defaultAutoCommit,
defaultTransactionIsolation,
defaultCatalog,
configuration);
validateConnectionFactory(connectionFactory);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new SQLNestedException("Cannot create PoolableConnectionFactory (" + e.getMessage() + ")", e);
}
}
PoolableConnectionFactory的構造方法中的程式碼如下:
public PoolableConnectionFactory(
ConnectionFactory connFactory,
ObjectPool pool,
KeyedObjectPoolFactory stmtPoolFactory,
String validationQuery,
int validationQueryTimeout,
Collection connectionInitSqls,
Boolean defaultReadOnly,
boolean defaultAutoCommit,
int defaultTransactionIsolation,
String defaultCatalog,
AbandonedConfig config) {
<strong> _connFactory = connFactory;
_pool = pool;</strong>
_config = config;
<strong> _pool.setFactory(this);</strong>
_stmtPoolFactory = stmtPoolFactory;
_validationQuery = validationQuery;
_validationQueryTimeout = validationQueryTimeout;
_connectionInitSqls = connectionInitSqls;
_defaultReadOnly = defaultReadOnly;
_defaultAutoCommit = defaultAutoCommit;
_defaultTransactionIsolation = defaultTransactionIsolation;
_defaultCatalog = defaultCatalog;
}
可以看到通過PoolableConnectionFactory的setFactory方法,將第3步建立的ConnectionFactory和第4步建立的connectionPool 關聯起來了。
6.接下來是createDataSourceInstance方法。
該方法如下:
protected void createDataSourceInstance() throws SQLException {
<strong> PoolingDataSource pds = new PoolingDataSource(connectionPool);</strong>
pds.setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
pds.setLogWriter(logWriter);
<strong> dataSource = pds;</strong>
}
該方法使用PoolingDataSource將connectionPool包裝了下,並將BasicDataSource的dataSource變數賦值為new PoolingDataSource(connectionPool)。
7.接下來是初始化一定數量的連線池。
8.然後到了PoolingDataSource的getConnection()方法,程式碼如下。
public Connection getConnection() throws SQLException {
try {
<strong>Connection conn = (Connection)(_pool.borrowObject());</strong>
if (conn != null) {
conn = new PoolGuardConnectionWrapper(conn);
}
return conn;
} catch(SQLException e) {
throw e;
} catch(NoSuchElementException e) {
throw new SQLNestedException("Cannot get a connection, pool error " + e.getMessage(), e);
} catch(RuntimeException e) {
throw e;
} catch(Exception e) {
throw new SQLNestedException("Cannot get a connection, general error", e);
}
}
關鍵在於GenericObjectPool的borrowObject()方法。