druid源码解读-Druid源码分析其他类的代码分析DruidDataSourceC3P0Adapter、validConnectionChecker

Druid源码分析其他类的代码分析DruidDataSourceC3P0Adapter、validConnectionChecker

今天在看源码DataSource同目录下的类的时候留意到DruidDataSourceC3P0Adapter和DruidDataSourceC3P0AdapterMBean这两个类,发现在前面看的源码中没有关于这块的调用,随即就开始研究一下具体用来做什么的?

DruidDataSourceC3P0Adapter

//构造方法
public DruidDataSourceC3P0Adapter(){
   //注入数据源
    dataSource = new DruidDataSource();

    // 设置默认的初始化大小为3
    dataSource.setInitialSize(3);
    //acquireincrement表示当我们数据库连接池中没有空闲的连接时,它一次性创建的连接数量,我们设置几就一次性创建几个连接,你也可以理解为数据库中的连接都被使用了,没有了连接的时候或者是连接耗尽的时候我们可以一次性的创建n连接,这个n的值由该acquireincrement属性来决定。
    acquireIncrement = 3;
    //连接异常的时间间隔
    dataSource.setTimeBetweenConnectErrorMillis(1000);
    //自动提交
    dataSource.setDefaultAutoCommit(true);
    //logAbandoned=false,表示在回收连接的时候,不在日志中输出回收的连接的信息,包括是在哪用的这个连接,可以用来追踪连接溢出。
    dataSource.setLogAbandoned(false);
    dataSource.setMaxActive(15);
    dataSource.setMinIdle(3);
    //testOnBorrow、testOnReturn这些参数设置为true时,分别表示在获取连接时、归还连接时、空闲时检查连接的可用性,如果发生断开的情况则会重连。这边设置为false
    dataSource.setTestOnReturn(false);
    dataSource.setTestOnBorrow(false);
}

ComboPooledDataSource

//全局看了相关的源码 发现只有这ComboPooledDataSource调用了DruidDataSourceC3P0Adapter,但是却没有具体的实现,所以不太清楚是否还有别的作用。
public class ComboPooledDataSource extends DruidDataSourceC3P0Adapter implements PooledDataSource {

}

看过druid的源码就会发现,相比其他DBCP和C3P0,druid有以下特点:

  1. 更多地引入了JDK的特性,特别是concurrent包的工具。例如,CountDownLatchReentrantLockAtomicLongFieldUpdaterCondition等,也就是说,在分析druid源码之前,最好先学习下这些技术;
  2. 在类的设计上一切从简。例如,DBCP和C3P0都有一个池的类,而druid并没有,只用了一个简单的数组,且druid的核心逻辑几乎都堆积在DruidDataSource里面。另外,在对类或接口的抽象上,个人感觉,druid不是很“面向对象”,有的接口或类的方法很难统一成某种对象的行为,所以,本文不会去关注类的设计,更多地将分析一些重要功能的实现。

ValidConnectionChecker

校验连接,根据数据库方言不同,实现不同的校验方案。ValidConnectionChecker在DruidDataSource核心代码模块进行调用,具体相关的子类的类图如下。
druid源码解读-Druid源码分析其他类的代码分析DruidDataSourceC3P0Adapter、validConnectionChecker_第1张图片

validateConnection

//DruidDataSource 中的校验连接
public void validateConnection(Connection conn) throws SQLException {
    String query = getValidationQuery();
    //如果已关闭 抛出异常
    if (conn.isClosed()) {
        throw new SQLException("validateConnection: connection closed");
    }

    if (validConnectionChecker != null) {
        // 数据库方言对应的check不为空
        boolean result;
        Exception error = null;
        try {
           // 是否是有效连接。这个代码具体看下子类的实现
            result = validConnectionChecker.isValidConnection(conn, validationQuery, validationQueryTimeout);

            if (result && onFatalError) {
            //加锁 // 这块为什么要加锁呢??? 只是判断一个属性
                lock.lock();
                try {
                    if (onFatalError) {
                        onFatalError = false;
                    }
                } finally {
                    lock.unlock();
                }
            }
        } catch (SQLException ex) {
            throw ex;
        } catch (Exception ex) {
            result = false;
            error = ex;
        }
        
        if (!result) {
            SQLException sqlError = error != null ? //
                new SQLException("validateConnection false", error) //
                : new SQLException("validateConnection false");
            throw sqlError;
        }
        return;
    }

    if (null != query) {
            //查询不为空
            Statement stmt = null;
            ResultSet rs = null;
            try {
                //创建 statement
                stmt = conn.createStatement();
                if (getValidationQueryTimeout() > 0) {
                    //超时时间
                    stmt.setQueryTimeout(getValidationQueryTimeout());
                }
                //执行sql
                rs = stmt.executeQuery(query);
                if (!rs.next()) {
                    throw new SQLException("validationQuery didn't return a row");
                }

                if (onFatalError) {
                    //致命异常 
                    lock.lock();
                    try {
                        if (onFatalError) {
                            onFatalError = false;
                        }
                    }
                    finally {
                        lock.unlock();
                    }
                }
            } finally {
                //关闭
                JdbcUtils.close(rs);
                JdbcUtils.close(stmt);
            }
        }
}

ValidConnectionCheckerAdapter

@Override
//校验连接
public boolean isValidConnection(Connection conn, String query, int validationQueryTimeout) throws Exception {
    if (query == null || query.length() == 0) {
        return true;
    }
    
    Statement stmt = null;
    ResultSet rs = null;
    try {
    //创建stament
        stmt = conn.createStatement();
        if (validationQueryTimeout > 0) {
        //超时时间
            stmt.setQueryTimeout(validationQueryTimeout);
        }
        //执行sql
        rs = stmt.executeQuery(query);
        //返回为true 
        return true;
    } finally {
        //关闭
        JdbcUtils.close(rs);
        JdbcUtils.close(stmt);
    }
}

MySqlValidConnectionChecker

//mysql 数据库校验连接的方式竟然通过ping的方式
public boolean isValidConnection(Connection conn, String validateQuery, int validationQueryTimeout) throws Exception {
    if (conn.isClosed()) {
        return false;
    }

    if (usePingMethod) {
    //获取连接
        if (conn instanceof DruidPooledConnection) {
            conn = ((DruidPooledConnection) conn).getConnection();
        }

        if (conn instanceof ConnectionProxy) {
            conn = ((ConnectionProxy) conn).getRawObject();
        }

        if (clazz.isAssignableFrom(conn.getClass())) {
            if (validationQueryTimeout <= 0) {
                validationQueryTimeout = DEFAULT_VALIDATION_QUERY_TIMEOUT;
            }

            try {
            // ping连接
                ping.invoke(conn, true, validationQueryTimeout * 1000);
            } catch (InvocationTargetException e) {
                Throwable cause = e.getCause();
                if (cause instanceof SQLException) {
                    throw (SQLException) cause;
                }
                throw e;
            }
            return true;
        }
    }

    String query = validateQuery;
    if (validateQuery == null || validateQuery.isEmpty()) {
        query = DEFAULT_VALIDATION_QUERY;
    }

    Statement stmt = null;
    ResultSet rs = null;
    try {
    //创建statement
        stmt = conn.createStatement();
        if (validationQueryTimeout > 0) {
            stmt.setQueryTimeout(validationQueryTimeout);
        }
        //执行查询
        rs = stmt.executeQuery(query);
        return true;
    } finally {
        JdbcUtils.close(rs);
        JdbcUtils.close(stmt);
    }

}

validationQuery什么时候会起作用?

当Druid遇到testWhileIdle,testOnBorrow,testOnReturn时,就会验证连接的有效性,验证规则如下:
如果有相关数据库的ValidConnectionChecker,则使用ValidConnectionChecker验证(Druid提供常用数据库的ValidConnectionChecker,包括MSSQLValidConnectionChecker,MySqlValidConnectionChecker,OracleValidConnectionChecker,PGValidConnectionChecker);

如果没有ValidConnectionChecker,则直接使用validationQuery验证;MySqlValidConnectionChecker会使用Mysql独有的ping方式进行验证,其他数据库其实也都是使用validationQuery进行验证

总结

今天把整体的大流程又过了一下,发现有一些之前没有关注到的细节点,也发现作者定义了一些尚未实现的方法,这些应该是为了以后拓展使用吧。后面整体输出一张脑图,把整个流程串起来。

你可能感兴趣的