当前位置:Gxlcms > 数据库问题 > DBCP数据源连接池实现原理分析

DBCP数据源连接池实现原理分析

时间:2021-07-01 10:21:17 帮助过:10人阅读

package cn.com.chnsys.dbcpDataSource; 2 3 import javax.sql.DataSource; 4 import java.sql.Connection; 5 import java.sql.Statement; 6 import java.sql.ResultSet; 7 import java.sql.SQLException; 8 import org.apache.commons.dbcp.BasicDataSource; 9 /** 10 * 11 * <p>dbcp数据源连接池分析</p> 12 * 13 * 类说明 14 * 15 * @author duanxj 16 * @version 1.0 17 */ 18 public class BasicDataSourceExample { 19 20 public static void main(String[] args) { 21 //设置数据源基本配置项 22 DataSource dataSource = setupDataSource(args[0]); 23 //创建连接 24 Connection conn = null; 25 Statement stmt = null; 26 ResultSet rset = null; 27 try { 28 //创建连接对象 29 conn = dataSource.getConnection(); 30 //创建Statement 对象,这里我们使用Statement prepareStatement也是一样的 31 stmt = conn.createStatement(); 32 //创建结果返回集 33 rset = stmt.executeQuery(args[1]); 34 //得到查询影响记录数 35 int numcols = rset.getMetaData().getColumnCount(); 36 while(rset.next()) { 37 for(int i=1;i<=numcols;i++) { 38 System.out.print("\t" + rset.getString(i)); 39 } 40 System.out.println(""); 41 } 42 } catch(SQLException e) { 43 e.printStackTrace(); 44 } finally { 45 //关闭资源 46 try { if (rset != null) rset.close(); } catch(Exception e) { } 47 try { if (stmt != null) stmt.close(); } catch(Exception e) { } 48 try { if (conn != null) conn.close(); } catch(Exception e) { } 49 } 50 } 51 52 /** 53 * 创建数据源,并设置数据源基本配置项 54 * @param connectURI 55 * @return 56 */ 57 public static DataSource setupDataSource(String connectURI) { 58 BasicDataSource ds = new BasicDataSource(); 59 ds.setDriverClassName("oracle.jdbc.driver.OracleDriver"); //设置驱动 60 ds.setUsername("root"); //设置用户名 61 ds.setPassword("root"); //设置密码 62 ds.setUrl(connectURI);//设置连接url 63 return ds; 64 } 65 /** 66 * 打印创建的数据源的配置项 67 * @param ds 68 */ 69 public static void printDataSourceStats(DataSource ds) { 70 BasicDataSource bds = (BasicDataSource) ds; 71 System.out.println("NumActive: " + bds.getNumActive()); 72 System.out.println("NumIdle: " + bds.getNumIdle()); 73 } 74 /** 75 * 关闭销毁 76 * @param ds 77 * @throws SQLException 78 */ 79 public static void shutdownDataSource(DataSource ds) throws SQLException { 80 BasicDataSource bds = (BasicDataSource) ds; 81 bds.close(); 82 } 83 }

上面的代码大家应该能看懂,我们只说其中比较关键的。首先哟啊创建一个dataSource,并对这个dataSource设置一些必须的配置项(数据库驱动,URL,用户名,密码)等,然后关键的操作是在dataSource.getConnection();处,我们查看dbcp源码的实现就可以知道,datasource的配置只是配置一些有关的配置信息,真正的创建连接池pool的操作是在用户第一次去获取connection的时候创建的,下面是源码getConnection()的实现:

public Connection getConnection()
    throws SQLException
  {
    return createDataSource().getConnection();
  }
 protected synchronized DataSource createDataSource()
    throws SQLException
  {
    if (this.closed) {
      throw new SQLException("Data source is closed");
    }

    if (this.dataSource != null) {
      return this.dataSource;
    }

    ConnectionFactory driverConnectionFactory = createConnectionFactory();

    createConnectionPool();

    GenericKeyedObjectPoolFactory statementPoolFactory = null;
    if (isPoolPreparedStatements()) {
      statementPoolFactory = new GenericKeyedObjectPoolFactory(null, -1, (byte)0, 0L, 1, this.maxOpenPreparedStatements);
    }

    createPoolableConnectionFactory(driverConnectionFactory, statementPoolFactory, this.abandonedConfig);

    createDataSourceInstance();
    try
    {
      for (int i = 0; i < this.initialSize; i++)
        this.connectionPool.addObject();
    }
    catch (Exception e) {
      throw new SQLNestedException("Error preloading the connection pool", e);
    }

    return this.dataSource;
  }

上面的代码我做如下的分析:

首先我们看到这个createDataSource()方法是用synchronized修饰的我们知道这是线程安全的。

1  首先是在getConnection()方法中先是去创建一个createDataSource()。在createDataSource(),的方法中先判断该数据源是否关闭,如果关闭直接抛出异常,如果这个数据源已经创建成功则直接返回,否则去创建这个数据源,

2 创建数据源之前先创建数据库的连接工厂createConnectionFactory();在这个方法中主要是通过反射CLASS.FORNAME()创建一个数据库连接对象,其中涉及到的参数主要是driverClassName,url,validationQuery,username,password(对这些链接数据库的配置应该不陌生,这些参数的作用就是创建一个连接工厂)。

 ConnectionFactory driverConnectionFactory = new DriverConnectionFactory(driver, this.url, this.connectionProperties);并返回创建成功的连接工厂示例在下面创建连接池createConnectionPool使用。

3 下面是真正的创建连接池的核心部分createConnectionPool();我们先看一下这个方法的具体实现,这里的前提是我们已经创建成功一个与数据库的连接对象,

 1  protected void createConnectionPool()
 2   {
 3     GenericObjectPool gop;
 4     GenericObjectPool gop;
 5     if ((this.abandonedConfig != null) && (this.abandonedConfig.getRemoveAbandoned())) {
 6       gop = new AbandonedObjectPool(null, this.abandonedConfig);
 7     }
 8     else {
 9       gop = new GenericObjectPool();
10     }
11     gop.setMaxActive(this.maxActive);
12     gop.setMaxIdle(this.maxIdle);
13     gop.setMinIdle(this.minIdle);
14     gop.setMaxWait(this.maxWait);
15     gop.setTestOnBorrow(this.testOnBorrow);
16     gop.setTestOnReturn(this.testOnReturn);
17     gop.setTimeBetweenEvictionRunsMillis(this.timeBetweenEvictionRunsMillis);
18     gop.setNumTestsPerEvictionRun(this.numTestsPerEvictionRun);
19     gop.setMinEvictableIdleTimeMillis(this.minEvictableIdleTimeMillis);
20     gop.setTestWhileIdle(this.testWhileIdle);
21     this.connectionPool = gop;
22   }

在源码中我们可以看到使用了GenericObjectPool对象,这个是使用commons-pool的GenericObjectPool 实现的,创建一个GenericObjectPool对象并设置连接池的配置参数信息,(这里知道我们再配置文件中配置的选项是如何在源码中起作用的了吧~~)。然后返回这个GenericObjectPool(这个commons-pool的GenericObjectPool的源码后续我们再分析,大家只要知道这里是使用的commons-pool的GenericObjectPool创建了一个池 )。

3.1 创建成功  this.connectionPool后

 就是创建GenericKeyedObjectPoolFactory(暂时不知道这个是个什么东东?????)

 // 设置连接池工厂  createPoolableConnectionFactory(driverConnectionFactory, statementPoolFactory, abandonedConfig);  

// 建立数据库连接池实例   createDataSourceInstance();  

然后是根据我们配置文件中配置的初始的数据库连接池的大小去设置连接池的初始个数

 // 根据配置,初始化建立一些数据库连接  =

try {  

for (int i = 0 ; i < initialSize ; i++) {  

             connectionPool.addObject();  

         }  

     } catch (Exception e) {  

throw new SQLNestedException("Error preloading the connection pool", e);  

   }  

这样一个完成的数据源dataSource和连接池就创建完成了。

 

 

DBCP数据源连接池实现原理分析

标签:

人气教程排行