1. 程式人生 > >dbcp BasicDataSource 連線池獲取連線過程原始碼分析

dbcp BasicDataSource 連線池獲取連線過程原始碼分析

通過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方法:

    /**
     * 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;
    }
首先是初始化jdbc驅動程式。

最後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()方法。