如下图1所示主要分析的是BasicDataSource、GenericObjectPool、DriverConnectionFactory、PoolableConnectionFactory和PoolingDataSource类。
图1 dbcp几个重要实现类的关系
我们直接使用的最多的就是BasicDataSource类了,这个类主要是设置一些数据库连接池的参数,不过这些参数基本都是通过GenericObjectPool的实例来实现的。
在BasicDataSource中最重要的方法就是createDataSource方法了,这个方法中回创建一个GenericObjectPool实例来管理数据库连接(AbandonedObjectPool类已经不被推荐了,所以这里不考虑),最后这个方法返回的是PoolingDataSource的实例,这个类主要是对GenericObjectPool和数据库连接的一些代理实现。下面是createDataSource方法的实现。
protected synchronized DataSource createDataSource()
throws SQLException {
// 如果已经创建了DataSource则直接返回
if (dataSource != null) {
return (dataSource);
}
// 加载 数据库驱动类
if (driverClassName != null) {
try {
Class.forName(driverClassName);
} catch (Throwable t) {
String message = "Cannot load JDBC driver class '" +
driverClassName + "'";
logWriter.println(message);
t.printStackTrace(logWriter);
throw new SQLNestedException(message, t);
}
}
// 得到一个数据库驱动的实例
Driver driver = null;
try {
driver = DriverManager.getDriver(url);
} 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);
}
// 如果没有配置(设置)validationQuery参数,则不执行相应的测试
//下面的3个方法都是测试链接是否有效的
//setTestOnBorrow方法是在获得连接的时候测试链接时候有效
//setTestOnReturn是在数据库连接池使用完连接把它放入空闲链表中的时候测试连接是否有效
//setTestWhileIdle是在驱逐超时的空闲连接的时候测试链接是否有效
if (validationQuery == null) {
setTestOnBorrow(false);
setTestOnReturn(false);
setTestWhileIdle(false);
}
// 如果配置了abandonedConfig 相应的参数则使用AbandonedObjectPool,则是是不推荐的
if ((abandonedConfig != null) && (abandonedConfig.getRemoveAbandoned())) {
connectionPool = new AbandonedObjectPool(null,abandonedConfig);
}
//所以一般使用的是GenericObjectPool类,这个类的主要工作是管理数据库连接池相关的配置
//例如获取数据库连接,保证数据库连接池中的的空闲链接,驱逐空闲连接,
//就是和数据库连接池相关的配置都由它管理实现,它为很多配置提供了默认值
else {
connectionPool = new GenericObjectPool();
}
connectionPool.setMaxActive(maxActive);
connectionPool.setMaxIdle(maxIdle);
connectionPool.setMinIdle(minIdle);
connectionPool.setMaxWait(maxWait);
connectionPool.setTestOnBorrow(testOnBorrow);
connectionPool.setTestOnReturn(testOnReturn);
connectionPool.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
connectionPool.setNumTestsPerEvictionRun(numTestsPerEvictionRun);
connectionPool.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
connectionPool.setTestWhileIdle(testWhileIdle);
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);
}
if (username != null) {
connectionProperties.put("user", username);
} else {
log("DBCP DataSource configured without a 'username'");
}
if (password != null) {
connectionProperties.put("password", password);
} else {
log("DBCP DataSource configured without a 'password'");
}
//这个是实际获得数据库连接的工厂类
DriverConnectionFactory driverConnectionFactory =
new DriverConnectionFactory(driver, url, connectionProperties);
// 这个类实现了PoolableObjectFactory,它做的是封装一个DriverConnectionFactory
//来得到实际的连接,封装了一个GenericObjectPool来管理连接池,当然都是针对接口的
//重要的一点就是它把自身传递到了GenericObjectPool中,这样就只需要暴露GenericObjectPool
PoolableConnectionFactory connectionFactory = null;
try {
connectionFactory =
new PoolableConnectionFactory(driverConnectionFactory,
connectionPool,
statementPoolFactory,
validationQuery,
defaultReadOnly,
defaultAutoCommit,
defaultTransactionIsolation,
defaultCatalog,
abandonedConfig);
if (connectionFactory == null) {
throw new SQLException("Cannot create PoolableConnectionFactory");
}
validateConnectionFactory(connectionFactory);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new SQLNestedException("Cannot create PoolableConnectionFactory (" + e.getMessage() + ")", e);
}
// Create and return the pooling data source to manage the connections
dataSource = new PoolingDataSource(connectionPool);
((PoolingDataSource) dataSource).setAccessToUnderlyingConnectionAllowed(isAccessToUnderlyingConnectionAllowed());
dataSource.setLogWriter(logWriter);
try {
for (int i = 0 ; i < initialSize ; i++) {
connectionPool.addObject();
}
} catch (Exception e) {
throw new SQLNestedException("Error preloading the connection pool", e);
}
return dataSource;
}
GenericObjectPool类主要是管理数据库连接池的,它主要实现了对数据库连接池参数的管理,例如连接池的最大数,它的创建连接时通过PoolableConnectionFactory的实例来实现的,不过它做了代理在创建连接的时候先检查了数据库的活动连接是不是已经达到了最大值,如果没有就继续创建,如果达到了参数设置的最大值就按指定的策略执行,默认的策略是当数据库连接池中的连接达到的最大值是在创建连接就会被阻塞(wait),这就是在上一篇中程序被挂起的主要原因。GenericObjectPool还可以检查最小的空闲连接,最大的空闲连接,驱逐超时的空闲连接。下面将数据库连接池参数的时候再介绍。
DriverConnectionFactory是实际获取数据库连接的类,它调用的是底层的数据库驱动。
PoolableConnectionFactory主要实现了创建连接,关闭连接,验证连接,使连接钝化(关闭Statement,设置关闭标志,不是真的关闭),激活连接等操作。GenericObjectPool关于连接的操作基本就是通过它实现的。
图2 dbcp数据库连接池参数
如上图2所示是dbcp数据库连接池的一些重要的参数,除了重要部分所示的username、password、url、driverClassName参数使用BasicDataSource直接管理,其他基本都是通过GenericObjectPool来管理的。
这里只是介绍一些比较容易混淆,不易理解的参数。首先看基本的initialSize是表示数据库连接池初始化的时候创建多少个连接。maxTotal是数据库连接池中最多创建多少个连接,maxIdel和minIdel是指最大空闲连接和最小空闲连接的数量,空闲连接是指使用过后的连接关闭连接(并没有真正关闭连接,而是加入了GenericObjectPool空闲连接列表的数量(假设使用的是GenericObjectPool))。maxWaitMills参数是指当数据库连接池中的连接达到最大值的时候,被阻塞等待的时间(wait(maxWaitMills)),超时就会抛出异常。
验证连接有效性的4个参数:首先必须得设置validationQuery参数,这个参数就是验证连接有效性的SQL语句,可以设置为select 1 from dual。只有设置了这个参数其他的三个参数才会生效。这个从createDataSource方法的代码中可以轻易的看出来。testOnBorrow是指在获取连接的时候检查连接的有效性,testOnReturn是指在关闭连接(不是真正关闭,而是加入到空闲连接列表的时候)验证连接的有效性。testOnIdle是指在驱逐空闲连接的时候验证连接的有效性。
驱逐连接相关的3个参数,timeBetweenEvictionRunsMills参数是值多长时间执行一次驱逐算法minEvictableIdleMills参数是指空闲连接的超时时间(就是这个连接有多久没有用就可以被回收了)numTestsPerEvictionRun参数是值设置几个线程来执行驱逐算法。这个算法其实不是特别复杂,只是有一个Timer计时器,在构造的时候启动Evictor,其中Evictor实现了TimerTask。调用了GenericObjectPool 的public synchronized void evict() throws Exception方法。因为每一个连接在创建的时候又一个时间戳,对比一下就可以了。
至于自动回收泄露连接的那几个参数,因为类已经不被推荐了所以也没有仔细看,就不介绍了。
来源:oschina
链接:https://my.oschina.net/u/2474629/blog/690748