以下圖1所示主要分析的是BasicDataSource、GenericObjectPool、DriverConnectionFactory、PoolableConnectionFactory和PoolingDataSource類。java
圖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還能夠檢查最小的空閒鏈接,最大的空閒鏈接,驅逐超時的空閒鏈接。下面將數據庫鏈接池參數的時候再介紹。url
DriverConnectionFactory是實際獲取數據庫鏈接的類,它調用的是底層的數據庫驅動。spa
PoolableConnectionFactory主要實現了建立鏈接,關閉鏈接,驗證鏈接,使鏈接鈍化(關閉Statement,設置關閉標誌,不是真的關閉),激活鏈接等操做。GenericObjectPool關於鏈接的操做基本就是經過它實現的。.net
圖2 dbcp數據庫鏈接池參數線程
如上圖2所示是dbcp數據庫鏈接池的一些重要的參數,除了重要部分所示的username、password、url、driverClassName參數使用BasicDataSource直接管理,其餘基本都是經過GenericObjectPool來管理的。代理
這裏只是介紹一些比較容易混淆,不易理解的參數。首先看基本的initialSize是表示數據庫鏈接池初始化的時候建立多少個鏈接。maxTotal是數據庫鏈接池中最多建立多少個鏈接,maxIdel和minIdel是指最大空閒鏈接和最小空閒鏈接的數量,空閒鏈接是指使用事後的鏈接關閉鏈接(並無真正關閉鏈接,而是加入了GenericObjectPool空閒鏈接列表的數量(假設使用的是GenericObjectPool))。maxWaitMills參數是指當數據庫鏈接池中的鏈接達到最大值的時候,被阻塞等待的時間(wait(maxWaitMills)),超時就會拋出異常。code
驗證鏈接有效性的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方法。由於每個鏈接在建立的時候又一個時間戳,對比一下就能夠了。
至於自動回收泄露鏈接的那幾個參數,由於類已經不被推薦了因此也沒有仔細看,就不介紹了。