大龄菜鸟-》 mybatis 源码阅读-》 datasource 数据源模块

 一:先看目录总览以及总体的类关系

二: pooled 包下的东西

PooledDataSource

  •   popConnection方法,获取 org.apache.ibatis.datasource.pooled.PooledConnection 对象,这个方法很重要,这是这个方法的代码和我画的图,其他方法。里面都有详细的注释。
/**
 * Copyright 2009-2020 the original author or authors.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.ibatis.datasource.pooled;

import org.apache.ibatis.datasource.unpooled.UnpooledDataSource;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import java.util.logging.Logger;

/**
 * This is a simple, synchronous, thread-safe database connection pool.
 * <p>
 * 这是一个简单的,同步的,线程安全的数据库连接池
 *
 * @author Clinton Begin
 */
public class PooledDataSource implements DataSource {

  private static final Log log = LogFactory.getLog(PooledDataSource.class);
  /**
   * PoolState 对象,记录池化的状态
   */
  private final PoolState state = new PoolState(this);
  /**
   * UnpooledDataSource 对象
   */
  private final UnpooledDataSource dataSource;

  //  在任意时间可以存在的活动(也就是正在使用)连接数量,默认值:10
  protected int poolMaximumActiveConnections = 10;

  // 任意时间可能存在的空闲连接数。
  protected int poolMaximumIdleConnections = 5;
  // 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒)
  protected int poolMaximumCheckoutTime = 20000;
  //  这是一个底层设置,如果获取连接花费了相当长的时间,
  //  连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直安静的失败),
  //  默认值:20000 毫秒(即 20 秒)。
  protected int poolTimeToWait = 20000;
  //这是一个关于坏连接容忍度的底层设置, 作用于每一个尝试从缓存池获取连接的线程.
  // 如果这个线程获取到的是一个坏的连接,
  // 那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过
  // poolMaximumIdleConnections 与 poolMaximumLocalBadConnectionTolerance 之和。 默认值:3 (新增于 3.4.5)
  protected int poolMaximumLocalBadConnectionTolerance = 3;
  // 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。
  // 默认是“NO PING QUERY SET”,这会导致多数数据库驱动失败时带有一个恰当的错误消息。
  protected String poolPingQuery = "NO PING QUERY SET";
  // 是否启用侦测查询。若开启,需要设置 poolPingQuery 属性为一个可执行的 SQL 语句(
  // 最好是一个速度非常快的 SQL 语句),默认值:false。
  protected boolean poolPingEnabled;
  // 配置 poolPingQuery 的频率。可以被设置为和数据库连接超时时间一样,来避免不必要的侦测,
  // 默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。
  protected int poolPingConnectionsNotUsedFor;
  // 期望 Connection 的类型编码,通过 {@link #assembleConnectionTypeCode(String, String, String)} 计算。
  private int expectedConnectionTypeCode;

  /**
   * dataSource 属性,UnpooledDataSource 对象。这样,就能重用 UnpooledDataSource 的代码了。
   * 说白了,获取真正连接的逻辑,还是在 UnpooledDataSource 中实现。
   */
  public PooledDataSource() {
    dataSource = new UnpooledDataSource();
  }

  public PooledDataSource(UnpooledDataSource dataSource) {
    this.dataSource = dataSource;
  }

  public PooledDataSource(String driver, String url, String username, String password) {
    dataSource = new UnpooledDataSource(driver, url, username, password);
    expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  }

  public PooledDataSource(String driver, String url, Properties driverProperties) {
    dataSource = new UnpooledDataSource(driver, url, driverProperties);
    expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  }

  public PooledDataSource(ClassLoader driverClassLoader, String driver, String url, String username, String password) {
    dataSource = new UnpooledDataSource(driverClassLoader, driver, url, username, password);
    expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  }

  public PooledDataSource(ClassLoader driverClassLoader, String driver, String url, Properties driverProperties) {
    dataSource = new UnpooledDataSource(driverClassLoader, driver, url, driverProperties);
    expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
  }

  /**
   * 获得 Connection 连接
   *
   * @return
   * @throws SQLException
   */
  @Override
  public Connection getConnection() throws SQLException {
    return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
  }

  /**
   * 获得 Connection 连接
   *
   * @return
   * @throws SQLException
   */
  @Override
  public Connection getConnection(String username, String password) throws SQLException {
    // getProxyConnection() 方法,返回代理的 Connection 对象。这样,每次对数据库的操作,才能被 PooledConnection的代理拦截。
    return popConnection(username, password).getProxyConnection();
  }

  @Override
  public void setLoginTimeout(int loginTimeout) {
    DriverManager.setLoginTimeout(loginTimeout);
  }

  @Override
  public int getLoginTimeout() {
    return DriverManager.getLoginTimeout();
  }

  @Override
  public void setLogWriter(PrintWriter logWriter) {
    DriverManager.setLogWriter(logWriter);
  }

  @Override
  public PrintWriter getLogWriter() {
    return DriverManager.getLogWriter();
  }

  public void setDriver(String driver) {
    dataSource.setDriver(driver);
    forceCloseAll();
  }

  public void setUrl(String url) {
    dataSource.setUrl(url);
    forceCloseAll();
  }

  public void setUsername(String username) {
    dataSource.setUsername(username);
    forceCloseAll();
  }

  public void setPassword(String password) {
    dataSource.setPassword(password);
    forceCloseAll();
  }

  public void setDefaultAutoCommit(boolean defaultAutoCommit) {
    dataSource.setAutoCommit(defaultAutoCommit);
    forceCloseAll();
  }

  public void setDefaultTransactionIsolationLevel(Integer defaultTransactionIsolationLevel) {
    dataSource.setDefaultTransactionIsolationLevel(defaultTransactionIsolationLevel);
    forceCloseAll();
  }

  public void setDriverProperties(Properties driverProps) {
    dataSource.setDriverProperties(driverProps);
    forceCloseAll();
  }

  /**
   * Sets the default network timeout value to wait for the database operation to complete. See {@link Connection#setNetworkTimeout(java.util.concurrent.Executor, int)}
   *
   * @param milliseconds The time in milliseconds to wait for the database operation to complete.
   * @since 3.5.2
   */
  public void setDefaultNetworkTimeout(Integer milliseconds) {
    dataSource.setDefaultNetworkTimeout(milliseconds);
    forceCloseAll();
  }

  /**
   * The maximum number of active connections.
   *
   * @param poolMaximumActiveConnections The maximum number of active connections
   */
  public void setPoolMaximumActiveConnections(int poolMaximumActiveConnections) {
    this.poolMaximumActiveConnections = poolMaximumActiveConnections;
    forceCloseAll();
  }

  /**
   * The maximum number of idle connections.
   *
   * @param poolMaximumIdleConnections The maximum number of idle connections
   */
  public void setPoolMaximumIdleConnections(int poolMaximumIdleConnections) {
    this.poolMaximumIdleConnections = poolMaximumIdleConnections;
    forceCloseAll();
  }

  /**
   * The maximum number of tolerance for bad connection happens in one thread
   * which are applying for new {@link PooledConnection}.
   *
   * @param poolMaximumLocalBadConnectionTolerance max tolerance for bad connection happens in one thread
   * @since 3.4.5
   */
  public void setPoolMaximumLocalBadConnectionTolerance(
    int poolMaximumLocalBadConnectionTolerance) {
    this.poolMaximumLocalBadConnectionTolerance = poolMaximumLocalBadConnectionTolerance;
  }

  /**
   * The maximum time a connection can be used before it *may* be
   * given away again.
   *
   * @param poolMaximumCheckoutTime The maximum time
   */
  public void setPoolMaximumCheckoutTime(int poolMaximumCheckoutTime) {
    this.poolMaximumCheckoutTime = poolMaximumCheckoutTime;
    forceCloseAll();
  }

  /**
   * The time to wait before retrying to get a connection.
   *
   * @param poolTimeToWait The time to wait
   */
  public void setPoolTimeToWait(int poolTimeToWait) {
    this.poolTimeToWait = poolTimeToWait;
    forceCloseAll();
  }

  /**
   * The query to be used to check a connection.
   *
   * @param poolPingQuery The query
   */
  public void setPoolPingQuery(String poolPingQuery) {
    this.poolPingQuery = poolPingQuery;
    forceCloseAll();
  }

  /**
   * Determines if the ping query should be used.
   *
   * @param poolPingEnabled True if we need to check a connection before using it
   */
  public void setPoolPingEnabled(boolean poolPingEnabled) {
    this.poolPingEnabled = poolPingEnabled;
    forceCloseAll();
  }

  /**
   * If a connection has not been used in this many milliseconds, ping the
   * database to make sure the connection is still good.
   *
   * @param milliseconds the number of milliseconds of inactivity that will trigger a ping
   */
  public void setPoolPingConnectionsNotUsedFor(int milliseconds) {
    this.poolPingConnectionsNotUsedFor = milliseconds;
    forceCloseAll();
  }

  public String getDriver() {
    return dataSource.getDriver();
  }

  public String getUrl() {
    return dataSource.getUrl();
  }

  public String getUsername() {
    return dataSource.getUsername();
  }

  public String getPassword() {
    return dataSource.getPassword();
  }

  public boolean isAutoCommit() {
    return dataSource.isAutoCommit();
  }

  public Integer getDefaultTransactionIsolationLevel() {
    return dataSource.getDefaultTransactionIsolationLevel();
  }

  public Properties getDriverProperties() {
    return dataSource.getDriverProperties();
  }

  /**
   * Gets the default network timeout.
   *
   * @return the default network timeout
   * @since 3.5.2
   */
  public Integer getDefaultNetworkTimeout() {
    return dataSource.getDefaultNetworkTimeout();
  }

  public int getPoolMaximumActiveConnections() {
    return poolMaximumActiveConnections;
  }

  public int getPoolMaximumIdleConnections() {
    return poolMaximumIdleConnections;
  }

  public int getPoolMaximumLocalBadConnectionTolerance() {
    return poolMaximumLocalBadConnectionTolerance;
  }

  public int getPoolMaximumCheckoutTime() {
    return poolMaximumCheckoutTime;
  }

  public int getPoolTimeToWait() {
    return poolTimeToWait;
  }

  public String getPoolPingQuery() {
    return poolPingQuery;
  }

  public boolean isPoolPingEnabled() {
    return poolPingEnabled;
  }

  public int getPoolPingConnectionsNotUsedFor() {
    return poolPingConnectionsNotUsedFor;
  }

  /**
   * Closes all active and idle connections in the pool.
   *
   * 关闭所有的 activeConnections 和 idleConnections 的连接
   */
  public void forceCloseAll() {
    synchronized (state) {
      // 期望 Connection 的类型编码,通过-assembleConnectionTypeCode 计算得来。
      expectedConnectionTypeCode = assembleConnectionTypeCode(dataSource.getUrl(), dataSource.getUsername(), dataSource.getPassword());
      // 循环处理活跃的连接集合
      for (int i = state.activeConnections.size(); i > 0; i--) {
        try {
          // 把活跃的连接移除。
          PooledConnection conn = state.activeConnections.remove(i - 1);
          // 把连接置为无效。
          conn.invalidate();
          // 获取数据库真实的连接。
          Connection realConn = conn.getRealConnection();
          // 回滚的相关操作
          if (!realConn.getAutoCommit()) {
            realConn.rollback();
          }
          // 关掉数据库真实的连接
          realConn.close();
        } catch (Exception e) {
          // ignore
        }
      }
      // 循环处理 闲置的连接集合
      for (int i = state.idleConnections.size(); i > 0; i--) {
        try {
          PooledConnection conn = state.idleConnections.remove(i - 1);
          conn.invalidate();
          // 获取数据库真实的连接。然后再关了。
          Connection realConn = conn.getRealConnection();
          if (!realConn.getAutoCommit()) {
            realConn.rollback();
          }
          realConn.close();
        } catch (Exception e) {
          // ignore
        }
      }
    }
    if (log.isDebugEnabled()) {
      log.debug("PooledDataSource forcefully closed/removed all connections.");
    }
  }

  public PoolState getPoolState() {
    return state;
  }

  private int assembleConnectionTypeCode(String url, String username, String password) {
    return ("" + url + username + password).hashCode();
  }

  /**
   * 将使用完的连接,添加到闲置连接池中
   *
   * @param conn 连接
   * @throws SQLException
   */
  protected void pushConnection(PooledConnection conn) throws SQLException {

    synchronized (state) {
      // 从激活的连接集合中移除该连接
      state.activeConnections.remove(conn);
      // 通过 ping 来测试连接是否有效
      if (conn.isValid()) {
        // 连接是有效的
        // 空闲连接数是否已达到上限,并且和当前连接池的标识匹配
        if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
          // 空闲连接数没达到上限。
          state.accumulatedCheckoutTime += conn.getCheckoutTime();
         // 回滚事务,避免使用方未提交或者回滚事务
          if (!conn.getRealConnection().getAutoCommit()) {
            conn.getRealConnection().rollback();
          }
          // 将连接添加到 idleConnections 中
          PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
          state.idleConnections.add(newConn);
          newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
          newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
          // 为什么这里要创建新的 PooledConnection 对象呢?避免使用方还在使用 conn ,通过将它设置为失效,万一再次调用,会抛出异常
          conn.invalidate();
          if (log.isDebugEnabled()) {
            log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");
          }
          // 唤醒正在等待连接的线程
          state.notifyAll();
        } else {
          // 空闲连接数达到上限 ,关闭真正的数据库连接。
          state.accumulatedCheckoutTime += conn.getCheckoutTime();
          if (!conn.getRealConnection().getAutoCommit()) {
            conn.getRealConnection().rollback();
          }
          conn.getRealConnection().close();
          if (log.isDebugEnabled()) {
            log.debug("Closed connection " + conn.getRealHashCode() + ".");
          }
          conn.invalidate();
        }
      } else {
        if (log.isDebugEnabled()) {
          log.debug("A bad connection (" + conn.getRealHashCode() + ") attempted to return to the pool, discarding connection.");
        }
        // 统计 无效的 连接对象个数
        state.badConnectionCount++;
      }
    }
  }

  /**
   * 获取 org.apache.ibatis.datasource.pooled.PooledConnection 对象,
   * 这是一个池化的连接。非常关键的一个方法
   *
   * 1:闲置和活跃的集合中,一个连接都没有。
   * 第一次进来拿连接,:进入的是3(创建一个连接)——》把连接放到“活跃”的连接集合中去。
   * 当连接使用完后,如果调用pushConnection方法,会把连接放到“闲置的”连接集合中去
   *
   * 2:闲置的连接池中,没有一个连接,但是活跃的连接池中,大于等于连接池允许的最大连接。
   *  那么从活跃的连接池中,取第一个连接,如果取的过程没有超时,等待一会,进入while循环。如果超时了。
   *  我们把取的这个连接置为无效,再新建一个连接。然后把连接放到“活跃” 连接池集合中去。
   *  当连接使用完后,如果调用pushConnection方法,会把连接放到“闲置的”连接集合中去
   *
   * 3:如果“闲置的”连接池中有连接,从集合中取出第一个(remove)。然后放到活跃的集合中去。
   * 当连接使用完后,如果调用pushConnection方法,会把连接放到“闲置的”连接集合中去。
   *
   *  基于上面。说明。闲置的连接是活跃的连接使用完后,做了还的操作。才放到了闲置的集合池中。
   *
   *
   * @param username
   * @param password
   * @return
   * @throws SQLException
   */
  private PooledConnection popConnection(String username, String password) throws SQLException {
    // 标记,获取连接时,是否进行了等待
    boolean countedWait = false;
    // 最终获取到的链接对象
    PooledConnection conn = null;
    // 记录当前时间
    long t = System.currentTimeMillis();
    // 记录当前方法,获取到坏连接的次数
    int localBadConnectionCount = 0;
    // 当连接等于空的时候。进入循环
    while (conn == null) {
      // 同步锁住 当前的 PoolState 对象。PoolState 对象把当前对象进行了存储。
      synchronized (state) {
        // 1: 如果当前 空闲连接非空
        if (!state.idleConnections.isEmpty()) {
          // Pool has available connection
          // 连接池有连接,移除第个可用连接。并且把第一个可用连接返回给conn对象。
          conn = state.idleConnections.remove(0);
          if (log.isDebugEnabled()) {
            log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
          }
        } else {
          // 2:连接池没有可用的连接
          // Pool does not have available connection
          // 活跃的连接数,少于连接池最大的连接时,说明可以创建连接。那么我们创建一个连接
          if (state.activeConnections.size() < poolMaximumActiveConnections) {
            //3:Can create new connection-》能 创建一个新的连接。
            conn = new PooledConnection(dataSource.getConnection(), this);
            if (log.isDebugEnabled()) {
              log.debug("Created connection " + conn.getRealHashCode() + ".");
            }
          } else {
            // 4: 活跃的连接数,大于等于连接池允许的最大连接了。
            // 不能创建新的连接
            // 从 连接池仓库取活跃的第一个连接
            PooledConnection oldestActiveConnection = state.activeConnections.get(0);
            // 检查该连接是否超时
            long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
            if (longestCheckoutTime > poolMaximumCheckoutTime) {
              // 连接获取超时了
              // Can claim overdue connection
              //  // 对连接超时的时间的统计
              state.claimedOverdueConnectionCount++;
              state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
              state.accumulatedCheckoutTime += longestCheckoutTime;
              // 把刚获取到的那个超时的连接从活跃的连接集合中移除。
              state.activeConnections.remove(oldestActiveConnection);
              //  如果非自动提交的,需要进行回滚。即将原有执行中的事务,全部回滚。
              if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
                try {
                  oldestActiveConnection.getRealConnection().rollback();
                } catch (SQLException e) {
                  /*
                     Just log a message for debug and continue to execute the following
                     statement like nothing happened.
                     Wrap the bad connection with a new PooledConnection, this will help
                     to not interrupt current executing thread and give current thread a
                     chance to join the next competition for another valid/good database
                     connection. At the end of this loop, bad {@link @conn} will be set as null.
                   */
                  log.debug("Bad connection. Could not roll back");
                }
              }
              // 创建新的连接
              conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
              conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
              conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
              // 把刚获取超时的那个连接置为无效
              oldestActiveConnection.invalidate();
              if (log.isDebugEnabled()) {
                log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
              }
            } else {
              // Must wait
              // 获取连接没有超时。继续等待。
              try {
                if (!countedWait) {
                  state.hadToWaitCount++;
                  countedWait = true;
                }
                if (log.isDebugEnabled()) {
                  log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
                }
                long wt = System.currentTimeMillis();
                state.wait(poolTimeToWait);
                state.accumulatedWaitTime += System.currentTimeMillis() - wt;
              } catch (InterruptedException e) {
                break;
              }
            }
          }
          // 可用连接池中没有可用连接逻辑结束
        }

        // 如果连接不为空。
        if (conn != null) {
          // ping to server and check the connection is valid or not
          // 向服务器ping下,检查下连接是否可用。
          if (conn.isValid()) {
            // 走到这里,说明连接是可用的
            // 当自动模式被禁用时,使用 rollback 回滚模式
            if (!conn.getRealConnection().getAutoCommit()) {
              conn.getRealConnection().rollback();
            }
            //
            conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
            conn.setCheckoutTimestamp(System.currentTimeMillis());
            conn.setLastUsedTimestamp(System.currentTimeMillis());
            // 把当前连接,假如到活跃连接中
            state.activeConnections.add(conn);
            // 请求数++
            state.requestCount++;
            // 实际累计处理时间。
            state.accumulatedRequestTime += System.currentTimeMillis() - t;
          } else {
            // 连接不可用
            if (log.isDebugEnabled()) {
              log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");
            }
            // 设置坏的连接次数++
            state.badConnectionCount++;
            // 当前坏请求也++
            localBadConnectionCount++;
            // 把连接置为空。
            conn = null;
            // poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance 坏连接容忍次数。本地坏连接超过这个就异常
            if (localBadConnectionCount > (poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance)) {
              if (log.isDebugEnabled()) {
                log.debug("PooledDataSource: Could not get a good connection to the database.");
              }
              throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
            }
          }
        }
        // 同步代码块结束
      }
      // while 循环结束
      // 如果conn 是空,会继续进行循环。
    }
    // 到这里如果连接还是空话,那么抛出异常。
    if (conn == null) {
      if (log.isDebugEnabled()) {
        log.debug("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
      }
      throw new SQLException("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
    }

    return conn;
  }

  /**
   * Method to check to see if a connection is still usable
   * 通过向数据库发起 poolPingQuery 语句来发起“ping”操作,以判断数据库连接是否有效
   * @param conn - the connection to check
   * @return True if the connection is still usable
   */
  protected boolean pingConnection(PooledConnection conn) {
    // 记录是否 ping 成功
    boolean result = true;
    // 判断真实的连接是否已经关闭。若已关闭,就意味着 ping 肯定是失败的。
    try {
      result = !conn.getRealConnection().isClosed();
    } catch (SQLException e) {
      if (log.isDebugEnabled()) {
        log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());
      }
      result = false;
    }
    // poolPingEnabled启用了侦测查询,长时间未使用 才发起ping
    if (result && poolPingEnabled && poolPingConnectionsNotUsedFor >= 0
      && conn.getTimeElapsedSinceLastUse() > poolPingConnectionsNotUsedFor) {
      try {
        if (log.isDebugEnabled()) {
          log.debug("Testing connection " + conn.getRealHashCode() + " ...");
        }
        // 通过执行 poolPingQuery 语句来发起 ping
        Connection realConn = conn.getRealConnection();
        try (Statement statement = realConn.createStatement()) {
          statement.executeQuery(poolPingQuery).close();
        }
        if (!realConn.getAutoCommit()) {
          realConn.rollback();
        }
        // 标记执行成功
        result = true;
        if (log.isDebugEnabled()) {
          log.debug("Connection " + conn.getRealHashCode() + " is GOOD!");
        }
      } catch (Exception e) {
        log.warn("Execution of ping query '" + poolPingQuery + "' failed: " + e.getMessage());
        try {
          // 关闭数据库真实的连接
          conn.getRealConnection().close();
        } catch (Exception e2) {
          // ignore
        }
        // 标记执行失败
        result = false;
        if (log.isDebugEnabled()) {
          log.debug("Connection " + conn.getRealHashCode() + " is BAD: " + e.getMessage());
        }
      }
    }
    return result;
  }

  /**
   * Unwraps a pooled connection to get to the 'real' connection
   * 获取真实的数据库连接
   * @param conn - the pooled connection to unwrap
   * @return The 'real' connection
   */
  public static Connection unwrapConnection(Connection conn) {
    // 如果传入的是被代理的连接
    if (Proxy.isProxyClass(conn.getClass())) {
      // 如果传入的是被代理的连接
      InvocationHandler handler = Proxy.getInvocationHandler(conn);
      // 如果是 PooledConnection 对象,则获取真实的连接
      if (handler instanceof PooledConnection) {
        return ((PooledConnection) handler).getRealConnection();
      }
    }
    return conn;
  }

  @Override
  protected void finalize() throws Throwable {
    forceCloseAll();
    super.finalize();
  }

  @Override
  public <T> T unwrap(Class<T> iface) throws SQLException {
    throw new SQLException(getClass().getName() + " is not a wrapper.");
  }

  @Override
  public boolean isWrapperFor(Class<?> iface) {
    return false;
  }

  @Override
  public Logger getParentLogger() {
    return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
  }

}

PoolState

  • /**
     *    Copyright 2009-2020 the original author or authors.
     *
     *    Licensed under the Apache License, Version 2.0 (the "License");
     *    you may not use this file except in compliance with the License.
     *    You may obtain a copy of the License at
     *
     *       http://www.apache.org/licenses/LICENSE-2.0
     *
     *    Unless required by applicable law or agreed to in writing, software
     *    distributed under the License is distributed on an "AS IS" BASIS,
     *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     *    See the License for the specific language governing permissions and
     *    limitations under the License.
     */
    package org.apache.ibatis.datasource.pooled;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author Clinton Begin
     */
    public class PoolState {
      /**
       * 所属的 PooledDataSource 对象
       */
      protected PooledDataSource dataSource;
      /**
       * 闲置连接集合
       */
      protected final List<PooledConnection> idleConnections = new ArrayList<>();
      /**
       * 活跃连接集合
       */
      protected final List<PooledConnection> activeConnections = new ArrayList<>();
      /**
       * 全局统计 - 获取连接的次数
       */
      protected long requestCount = 0;
      /**
       * 全局统计 - 获取连接的时间
       */
      protected long accumulatedRequestTime = 0;
      /**
       * 全局统计 - 获取到连接非超时 + 超时的占用时长
       *
       * 所以,包括 {@link #accumulatedCheckoutTimeOfOverdueConnections} 部分
       */
      protected long accumulatedCheckoutTime = 0;
      /**
       * 全局统计 - 获取到连接超时的次数
       */
      protected long claimedOverdueConnectionCount = 0;
      /**
       * 全局统计 - 获取到连接超时的占用时长
       */
      protected long accumulatedCheckoutTimeOfOverdueConnections = 0;
      /**
       * 全局统计 - 等待连接的时间
       */
      protected long accumulatedWaitTime = 0;
      /**
       * 全局统计 - 等待连接的次数
       */
      protected long hadToWaitCount = 0;
      /**
       * 全局统计 - 获取到坏的连接的次数
       */
      protected long badConnectionCount = 0;
    
      public PoolState(PooledDataSource dataSource) {
        this.dataSource = dataSource;
      }
    
      public synchronized long getRequestCount() {
        return requestCount;
      }
    
      public synchronized long getAverageRequestTime() {
        return requestCount == 0 ? 0 : accumulatedRequestTime / requestCount;
      }
    
      public synchronized long getAverageWaitTime() {
        return hadToWaitCount == 0 ? 0 : accumulatedWaitTime / hadToWaitCount;
    
      }
    
      public synchronized long getHadToWaitCount() {
        return hadToWaitCount;
      }
    
      public synchronized long getBadConnectionCount() {
        return badConnectionCount;
      }
    
      public synchronized long getClaimedOverdueConnectionCount() {
        return claimedOverdueConnectionCount;
      }
    
      public synchronized long getAverageOverdueCheckoutTime() {
        return claimedOverdueConnectionCount == 0 ? 0 : accumulatedCheckoutTimeOfOverdueConnections / claimedOverdueConnectionCount;
      }
    
      public synchronized long getAverageCheckoutTime() {
        return requestCount == 0 ? 0 : accumulatedCheckoutTime / requestCount;
      }
    
      public synchronized int getIdleConnectionCount() {
        return idleConnections.size();
      }
    
      public synchronized int getActiveConnectionCount() {
        return activeConnections.size();
      }
    
      @Override
      public synchronized String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("\n===CONFINGURATION==============================================");
        builder.append("\n jdbcDriver                     ").append(dataSource.getDriver());
        builder.append("\n jdbcUrl                        ").append(dataSource.getUrl());
        builder.append("\n jdbcUsername                   ").append(dataSource.getUsername());
        builder.append("\n jdbcPassword                   ").append(dataSource.getPassword() == null ? "NULL" : "************");
        builder.append("\n poolMaxActiveConnections       ").append(dataSource.poolMaximumActiveConnections);
        builder.append("\n poolMaxIdleConnections         ").append(dataSource.poolMaximumIdleConnections);
        builder.append("\n poolMaxCheckoutTime            ").append(dataSource.poolMaximumCheckoutTime);
        builder.append("\n poolTimeToWait                 ").append(dataSource.poolTimeToWait);
        builder.append("\n poolPingEnabled                ").append(dataSource.poolPingEnabled);
        builder.append("\n poolPingQuery                  ").append(dataSource.poolPingQuery);
        builder.append("\n poolPingConnectionsNotUsedFor  ").append(dataSource.poolPingConnectionsNotUsedFor);
        builder.append("\n ---STATUS-----------------------------------------------------");
        builder.append("\n activeConnections              ").append(getActiveConnectionCount());
        builder.append("\n idleConnections                ").append(getIdleConnectionCount());
        builder.append("\n requestCount                   ").append(getRequestCount());
        builder.append("\n averageRequestTime             ").append(getAverageRequestTime());
        builder.append("\n averageCheckoutTime            ").append(getAverageCheckoutTime());
        builder.append("\n claimedOverdue                 ").append(getClaimedOverdueConnectionCount());
        builder.append("\n averageOverdueCheckoutTime     ").append(getAverageOverdueCheckoutTime());
        builder.append("\n hadToWait                      ").append(getHadToWaitCount());
        builder.append("\n averageWaitTime                ").append(getAverageWaitTime());
        builder.append("\n badConnectionCount             ").append(getBadConnectionCount());
        builder.append("\n===============================================================");
        return builder.toString();
      }
    
    }
    

    PooledConnection

/**
 *    Copyright 2009-2020 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.datasource.pooled;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;

import org.apache.ibatis.reflection.ExceptionUtil;

/**
 * @author Clinton Begin
 */
class PooledConnection implements InvocationHandler {
  /**
   * 关闭 Connection 方法名
   */
  private static final String CLOSE = "close";
  /**
   * JDK Proxy 的接口
   */
  private static final Class<?>[] IFACES = new Class<?>[] { Connection.class };
  /**
   * 对象的标识,基于 {@link #realConnection} 求 hashCode
   */
  private final int hashCode;
  /**
   * 所属的 PooledDataSource 对象
   */
  private final PooledDataSource dataSource;
  /**
   * 真实的 Connection 连接
   */
  private final Connection realConnection;
  /**
   * 代理的 Connection 连接,即 {@link PooledConnection} 这个动态代理的 Connection 对象
   */
  private final Connection proxyConnection;
  /**
   * 从连接池中,获取走的时间戳
   */
  private long checkoutTimestamp;
  /**
   * 对象创建时间
   */
  private long createdTimestamp;
  /**
   * 最后更新时间
   */
  private long lastUsedTimestamp;
  /**
   * 连接的标识,即 {@link PooledDataSource#expectedConnectionTypeCode}
   */
  private int connectionTypeCode;
  /**
   * 是否有效
   */
  private boolean valid;

  /**
   * Constructor for SimplePooledConnection that uses the Connection and PooledDataSource passed in.
   *
   * @param connection
   *          - the connection that is to be presented as a pooled connection
   * @param dataSource
   *          - the dataSource that the connection is from
   */
  public PooledConnection(Connection connection, PooledDataSource dataSource) {
    this.hashCode = connection.hashCode();
    this.realConnection = connection;
    this.dataSource = dataSource;
    this.createdTimestamp = System.currentTimeMillis();
    this.lastUsedTimestamp = System.currentTimeMillis();
    this.valid = true;
    // 代理的 Connection 连接, 基于 JDK Proxy 创建 Connection 对象,并且 handler 对象就是 this  也就是自己。
    // 意味着后续对 proxyConnection 的所有方法调用,都会委托给 PooledConnection#invoke
    this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);


  }

  /**
   * Invalidates the connection.
   */
  public void invalidate() {
    valid = false;
  }

  /**
   * Method to see if the connection is usable.
   *
   * 检查连接释放可用的方法
   * @return True if the connection is usable
   */
  public boolean isValid() {
    return valid && realConnection != null && dataSource.pingConnection(this);
  }

  /**
   * Getter for the *real* connection that this wraps.
   *
   * @return The connection
   */
  public Connection getRealConnection() {
    return realConnection;
  }

  /**
   * Getter for the proxy for the connection.
   *
   * @return The proxy
   */
  public Connection getProxyConnection() {
    return proxyConnection;
  }

  /**
   * Gets the hashcode of the real connection (or 0 if it is null).
   *
   * @return The hashcode of the real connection (or 0 if it is null)
   */
  public int getRealHashCode() {
    return realConnection == null ? 0 : realConnection.hashCode();
  }

  /**
   * Getter for the connection type (based on url + user + password).
   *
   * @return The connection type
   */
  public int getConnectionTypeCode() {
    return connectionTypeCode;
  }

  /**
   * Setter for the connection type.
   *
   * 连接类型的set方法
   * @param connectionTypeCode
   *          - the connection type
   */
  public void setConnectionTypeCode(int connectionTypeCode) {
    this.connectionTypeCode = connectionTypeCode;
  }

  /**
   * Getter for the time that the connection was created.
   *
   * @return The creation timestamp
   */
  public long getCreatedTimestamp() {
    return createdTimestamp;
  }

  /**
   * Setter for the time that the connection was created.
   *
   * @param createdTimestamp
   *          - the timestamp
   */
  public void setCreatedTimestamp(long createdTimestamp) {
    this.createdTimestamp = createdTimestamp;
  }

  /**
   * Getter for the time that the connection was last used.
   *
   * @return - the timestamp
   */
  public long getLastUsedTimestamp() {
    return lastUsedTimestamp;
  }

  /**
   * Setter for the time that the connection was last used.
   *
   * @param lastUsedTimestamp
   *          - the timestamp
   */
  public void setLastUsedTimestamp(long lastUsedTimestamp) {
    this.lastUsedTimestamp = lastUsedTimestamp;
  }

  /**
   * Getter for the time since this connection was last used.
   *
   * @return - the time since the last use
   */
  public long getTimeElapsedSinceLastUse() {
    return System.currentTimeMillis() - lastUsedTimestamp;
  }

  /**
   * Getter for the age of the connection.
   *
   * @return the age
   */
  public long getAge() {
    return System.currentTimeMillis() - createdTimestamp;
  }

  /**
   * Getter for the timestamp that this connection was checked out.
   *
   * @return the timestamp
   */
  public long getCheckoutTimestamp() {
    return checkoutTimestamp;
  }

  /**
   * Setter for the timestamp that this connection was checked out.
   *
   * @param timestamp
   *          the timestamp
   */
  public void setCheckoutTimestamp(long timestamp) {
    this.checkoutTimestamp = timestamp;
  }

  /**
   * Getter for the time that this connection has been checked out.
   *
   * @return the time
   */
  public long getCheckoutTime() {
    return System.currentTimeMillis() - checkoutTimestamp;
  }

  @Override
  public int hashCode() {
    return hashCode;
  }

  /**
   * Allows comparing this connection to another.
   *
   * @param obj
   *          - the other connection to test for equality
   * @see Object#equals(Object)
   */
  @Override
  public boolean equals(Object obj) {
    if (obj instanceof PooledConnection) {
      return realConnection.hashCode() == ((PooledConnection) obj).realConnection.hashCode();
    } else if (obj instanceof Connection) {
      return hashCode == obj.hashCode();
    } else {
      return false;
    }
  }

  /**
   * Required for InvocationHandler implementation.
   *
   *
   * TODO  没太理解。
   *
   * 重点分析invoke方法,这里代理了Connection对象的close方法,是实现连接池功能的关键
   *
   * @param proxy
   *          - not used 代理对象
   * @param method
   *          - the method to be executed 方法
   * @param args
   *          - the parameters to be passed to the method 参数
   * @see java.lang.reflect.InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])
   */
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // <1> 判断是否为 CLOSE 方法,则将连接放回到连接池中,避免连接被关闭
    String methodName = method.getName();
    if (CLOSE.equals(methodName)) {
      dataSource.pushConnection(this);
      return null;
    }

    try {
      // <2.1> 判断非 Object 的方法,则先检查连接是否可用
      if (!Object.class.equals(method.getDeclaringClass())) {
        // issue #579 toString() should never fail
        // throw an SQLException instead of a Runtime
        checkConnection();
      }
      // <2.2> 反射调用对应的方法
      return method.invoke(realConnection, args);
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }

  }

  private void checkConnection() throws SQLException {
    if (!valid) {
      throw new SQLException("Error accessing PooledConnection. Connection is invalid.");
    }
  }

}

PooledDataSourceFactory

package org.apache.ibatis.datasource.pooled;

import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;

/**
 *
 * 池化的DataSourceFactory 类
 *
 *
 * @author Clinton Begin
 */
public class PooledDataSourceFactory extends UnpooledDataSourceFactory {

  public PooledDataSourceFactory() {
    this.dataSource = new PooledDataSource();
  }

}
 

总结:package org.apache.ibatis.datasource.pooled 这个包的重点是什么。

类总共有4个,

PooledDataSourceFactory 类就只是提供了默认的构造方法,创建了dataSource。

PooledDataSource(这是一个简单的,同步的,线程安全的数据库连接池),比较主要的是popConnection方法,里面把创建的线程往活跃线程对象进行了设置。pushConnection方法,把使用完了线程进行了归还,归还后的线程放到了全局的闲置线程连接对象中。popConnection拿的是一个“PooledConnection”对象,拿连接的时候。

PooledConnection 这个的构造方法中。全局属性中设置了一个代理对象。当前类实现了InvocationHandler 接口,这个接口是做动态代理用的。目录我没有想明白,这个地方为什么要用动态代理。

三: PooledConnection 对象中使用了jkd的动态代理。

实话说。我昨天看的时候,又忘记动态代理是什么。看了下这篇文章。略有所获,分享给大家。https://www.cnblogs.com/liujiarui/p/12408742.html

不能说自己完全看懂了。至少知道动态代理生成是基于接口的,通过字节码生成的代理对象和proxy对象在同一个目录。invoke方法是会在每个代理方法中都进行植入的。如

 
//抽象主题
interface AbstractSubject
{
    void request();
}




//JDK动态代理最终会为我们生成如下结构的代理类
class Proxy0 extends Proxy implements AbstractSubject {
  
    //第一步生成构造器函数
    protected Proxy0(InvocationHandler h) {
        super(h);
    }
  
    //第二步, 生成静态域
    private static Method m1;   //hashCode方法
    private static Method m2;   //equals方法
    private static Method m3;   //toString方法
    private static Method m4;   //抽象接口中的request方法
     
    //第三步, 生成代理方法(四个)
    @Override
    public int hashCode() {
        try {
            return (int) h.invoke(this, m1, null);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
    @Override
    public boolean equals(Object obj) {
        try {
            Object[] args = new Object[] {obj};
            return (boolean) h.invoke(this, m2, args);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
    @Override
    public String toString() {
        try {
            return (String) h.invoke(this, m3, null);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
     @Override
    public void request() {
        try {
            //构造参数数组, 如果request方法有多个参数,直接向{}里面添加参数就行了
            Object[] args = new Object[] {};
            h.invoke(this, m4, args);
        } catch (Throwable e) {
            throw new UndeclaredThrowableException(e);
        }
    }
    //第四步, 生成静态初始化方法
    static {
        try {
            Class c1 = Class.forName(Object.class.getName());
            Class c2 = Class.forName(AbstractSubject.class.getName());   
            m1 = c1.getMethod("hashCode", null);
            m2 = c1.getMethod("equals", new Class[]{Object.class});
            m3 = c1.getMethod("toString", null);   
            //注意参数request方法有多个参数,直接向{}里面添加参数就行了,因为request方法中没有参数,直接传一个null就行
            m4 = c2.getMethod("save", new Class[]{});
            //...
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

1.代理类默认继承Porxy类,因为Java中只支持单继承,所以JDK动态代理只能去实现接口。
2.代理方法都会去调用InvocationHandler的invoke()方法,因此我们需要重写InvocationHandler的invoke()方法。
3.调用invoke()方法时会传入代理实例本身,目标方法和目标方法参数。解释了invoke()方法的参数是怎样来的。

四: unpooled 模块

UnpooledDataSourceFactory

非池化的DataSourceFactory实现类

/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.datasource.unpooled;

import java.util.Properties;

import javax.sql.DataSource;

import org.apache.ibatis.datasource.DataSourceException;
import org.apache.ibatis.datasource.DataSourceFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

/**
 *
 * 非池化的DataSourceFactory实现类
 *
 * 这个数据源的实现知识每次被请求时打开和关闭连接,虽然有点慢,但对于在数据库连接可用性不高的应用程序,
 * 这是个很好的选择,不同的数据库在性能方面的表现也是不一样的,对于某些数据库来说,使用连接池不是很重要。
 * 那么这个数据源就很适合。
 *
 * @author Clinton Begin
 */
public class UnpooledDataSourceFactory implements DataSourceFactory {

  private static final String DRIVER_PROPERTY_PREFIX = "driver.";
  private static final int DRIVER_PROPERTY_PREFIX_LENGTH = DRIVER_PROPERTY_PREFIX.length();

  protected DataSource dataSource;

  public UnpooledDataSourceFactory() {
    // 创建  UnpooledDataSource 对象,啥属性也没有。
    this.dataSource = new UnpooledDataSource();
  }

  /**
   * 设置数据源的属性
   * @param properties
   */
  @Override
  public void setProperties(Properties properties) {
    Properties driverProperties = new Properties();
    MetaObject metaDataSource = SystemMetaObject.forObject(dataSource);
    for (Object key : properties.keySet()) {
      String propertyName = (String) key;
      if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) {
        // 初始化到 driverProperties 中
        String value = properties.getProperty(propertyName);
        driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
      } else if (metaDataSource.hasSetter(propertyName)) {
        // 初始化到 MetaObject 中
        String value = (String) properties.get(propertyName);
        // 转成字段对应的类型后,把值填充到属性中。
        Object convertedValue = convertValue(metaDataSource, propertyName, value);
        metaDataSource.setValue(propertyName, convertedValue);
      } else {
        throw new DataSourceException("Unknown DataSource property: " + propertyName);
      }
    }
    // 设置 driverProperties 到 MetaObject 中
    if (driverProperties.size() > 0) {
      metaDataSource.setValue("driverProperties", driverProperties);
    }
  }

  @Override
  public DataSource getDataSource() {
    return dataSource;
  }

  /**
   * 根据
   * @param metaDataSource mybatis 发射器拿到的 MetaObject对象
   * @param propertyName 属性字段
   * @param value 值
   * @return 把value转成属性类型的值对象。
   */
  private Object convertValue(MetaObject metaDataSource, String propertyName, String value) {
    Object convertedValue = value;
    Class<?> targetType = metaDataSource.getSetterType(propertyName);
    if (targetType == Integer.class || targetType == int.class) {
      convertedValue = Integer.valueOf(value);
    } else if (targetType == Long.class || targetType == long.class) {
      convertedValue = Long.valueOf(value);
    } else if (targetType == Boolean.class || targetType == boolean.class) {
      convertedValue = Boolean.valueOf(value);
    }
    return convertedValue;
  }

}

UnpooledDataSource

/**
 *    Copyright 2009-2020 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.datasource.unpooled;

import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.logging.Logger;

import javax.sql.DataSource;

import org.apache.ibatis.io.Resources;

/**
 *
 *
 *
 * @author Clinton Begin
 * @author Eduardo Macarron
 */
public class UnpooledDataSource implements DataSource {

  private ClassLoader driverClassLoader;
  private Properties driverProperties;
  /**
   * 已注册的 Driver 映射
   *
   * KEY:Driver 类名
   * VALUE:Driver 对象
   */
  private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<>();
  /**
   * Driver 类加载器
   */
  private String driver;
  /**
   * 这是数据库的 JDBC URL 地址。
   */
  private String url;
  /**
   *  登录数据库的用户名。
   */
  private String username;
  /**
   * 登录数据库的密码。
   */
  private String password;

  private Boolean autoCommit;
  /**
   * 默认的连接事务隔离级别
   */
  private Integer defaultTransactionIsolationLevel;
  /**
   */
  private Integer defaultNetworkTimeout;

  /**
   * 静态块,启动的时候加载
   */
  static {
    // 拿到应用程序启动后,所有的driver 对象。
    Enumeration<Driver> drivers = DriverManager.getDrivers();
    while (drivers.hasMoreElements()) {
      Driver driver = drivers.nextElement();
      registeredDrivers.put(driver.getClass().getName(), driver);
    }
  }

  public UnpooledDataSource() {
  }

  public UnpooledDataSource(String driver, String url, String username, String password) {
    this.driver = driver;
    this.url = url;
    this.username = username;
    this.password = password;
  }

  public UnpooledDataSource(String driver, String url, Properties driverProperties) {
    this.driver = driver;
    this.url = url;
    this.driverProperties = driverProperties;
  }

  public UnpooledDataSource(ClassLoader driverClassLoader, String driver, String url, String username, String password) {
    this.driverClassLoader = driverClassLoader;
    this.driver = driver;
    this.url = url;
    this.username = username;
    this.password = password;
  }

  public UnpooledDataSource(ClassLoader driverClassLoader, String driver, String url, Properties driverProperties) {
    this.driverClassLoader = driverClassLoader;
    this.driver = driver;
    this.url = url;
    this.driverProperties = driverProperties;
  }

  @Override
  public Connection getConnection() throws SQLException {
    return doGetConnection(username, password);
  }

  @Override
  public Connection getConnection(String username, String password) throws SQLException {
    return doGetConnection(username, password);
  }

  @Override
  public void setLoginTimeout(int loginTimeout) {
    DriverManager.setLoginTimeout(loginTimeout);
  }

  @Override
  public int getLoginTimeout() {
    return DriverManager.getLoginTimeout();
  }

  @Override
  public void setLogWriter(PrintWriter logWriter) {
    DriverManager.setLogWriter(logWriter);
  }

  @Override
  public PrintWriter getLogWriter() {
    return DriverManager.getLogWriter();
  }

  public ClassLoader getDriverClassLoader() {
    return driverClassLoader;
  }

  public void setDriverClassLoader(ClassLoader driverClassLoader) {
    this.driverClassLoader = driverClassLoader;
  }

  public Properties getDriverProperties() {
    return driverProperties;
  }

  public void setDriverProperties(Properties driverProperties) {
    this.driverProperties = driverProperties;
  }

  public synchronized String getDriver() {
    return driver;
  }

  public synchronized void setDriver(String driver) {
    this.driver = driver;
  }

  public String getUrl() {
    return url;
  }

  public void setUrl(String url) {
    this.url = url;
  }

  public String getUsername() {
    return username;
  }

  public void setUsername(String username) {
    this.username = username;
  }

  public String getPassword() {
    return password;
  }

  public void setPassword(String password) {
    this.password = password;
  }

  public Boolean isAutoCommit() {
    return autoCommit;
  }

  public void setAutoCommit(Boolean autoCommit) {
    this.autoCommit = autoCommit;
  }

  public Integer getDefaultTransactionIsolationLevel() {
    return defaultTransactionIsolationLevel;
  }

  public void setDefaultTransactionIsolationLevel(Integer defaultTransactionIsolationLevel) {
    this.defaultTransactionIsolationLevel = defaultTransactionIsolationLevel;
  }

  /**
   * Gets the default network timeout.
   *
   * @return the default network timeout
   * @since 3.5.2
   */
  public Integer getDefaultNetworkTimeout() {
    return defaultNetworkTimeout;
  }

  /**
   * Sets the default network timeout value to wait for the database operation to complete. See {@link Connection#setNetworkTimeout(java.util.concurrent.Executor, int)}
   *
   * @param defaultNetworkTimeout
   *          The time in milliseconds to wait for the database operation to complete.
   * @since 3.5.2
   */
  public void setDefaultNetworkTimeout(Integer defaultNetworkTimeout) {
    this.defaultNetworkTimeout = defaultNetworkTimeout;
  }

  /**
   * 获取 Connection 连接
   *
   * @param username 用户名
   * @param password 密码
   * @return 数据库连接
   * @throws SQLException 异常
   */
  private Connection doGetConnection(String username, String password) throws SQLException {
    // 创建 Properties 对象
    Properties props = new Properties();
    // 设置 driverProperties 到 props 中
    if (driverProperties != null) {
      props.putAll(driverProperties);
    }
    // 设置 user 和 password 到 props 中
    if (username != null) {
      props.setProperty("user", username);
    }
    if (password != null) {
      props.setProperty("password", password);
    }
    // 执行获得 Connection 连接
    return doGetConnection(props);
  }

  private Connection doGetConnection(Properties properties) throws SQLException {
    // <1> 初始化 Driver
    initializeDriver();
    // <2> 获得 Connection 对象
    Connection connection = DriverManager.getConnection(url, properties);
    // <3> 配置 Connection 对象
    configureConnection(connection);
    return connection;
  }

  /**
   * 初始化 Driver
   * @throws SQLException
   */
  private synchronized void initializeDriver() throws SQLException {
    // 判断 registeredDrivers 是否已经存在该 driver ,若不存在,进行初始化
    if (!registeredDrivers.containsKey(driver)) {
      Class<?> driverType;
      try {
        // <2> 获得 driver 类
        if (driverClassLoader != null) {
          driverType = Class.forName(driver, true, driverClassLoader);
        } else {
          driverType = Resources.classForName(driver);
        }
        // <3> 创建 Driver 对象
        // DriverManager requires the driver to be loaded via the system ClassLoader.
        // http://www.kfu.com/~nsayer/Java/dyn-jdbc.html
        Driver driverInstance = (Driver) driverType.getDeclaredConstructor().newInstance();
        // 创建 DriverProxy 对象,并注册到 DriverManager 中
        DriverManager.registerDriver(new DriverProxy(driverInstance));
        registeredDrivers.put(driver, driverInstance);
      } catch (Exception e) {
        throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + e);
      }
    }
  }

  /**
   * 配置 Connection 对象
   * @param conn
   * @throws SQLException
   */
  private void configureConnection(Connection conn) throws SQLException {
    // Executors.newSingleThreadExecutor() 用来处理setNetworkTimeout的操作。defaultNetworkTimeout 等待数据库操作的时间
    if (defaultNetworkTimeout != null) {
      conn.setNetworkTimeout(Executors.newSingleThreadExecutor(), defaultNetworkTimeout);
    }
    // 设置自动提交
    if (autoCommit != null && autoCommit != conn.getAutoCommit()) {
      conn.setAutoCommit(autoCommit);
    }
    //  设置事务隔离级别
    if (defaultTransactionIsolationLevel != null) {
      conn.setTransactionIsolation(defaultTransactionIsolationLevel);
    }
  }

  /**
   * 为什么此处会有使用 DriverProxy 呢?
   *
   *
   * 因为 <4> 处,使用 MyBatis 自定义的 Logger 对象。
   * 其他方法,实际就是直接调用 driver 对应的方法。
   */
  private static class DriverProxy implements Driver {
    private Driver driver;

    DriverProxy(Driver d) {
      this.driver = d;
    }

    @Override
    public boolean acceptsURL(String u) throws SQLException {
      return this.driver.acceptsURL(u);
    }

    @Override
    public Connection connect(String u, Properties p) throws SQLException {
      return this.driver.connect(u, p);
    }

    @Override
    public int getMajorVersion() {
      return this.driver.getMajorVersion();
    }

    @Override
    public int getMinorVersion() {
      return this.driver.getMinorVersion();
    }

    @Override
    public DriverPropertyInfo[] getPropertyInfo(String u, Properties p) throws SQLException {
      return this.driver.getPropertyInfo(u, p);
    }

    @Override
    public boolean jdbcCompliant() {
      return this.driver.jdbcCompliant();
    }

    @Override
    public Logger getParentLogger() {
      // 因为 <4> 处,使用 MyBatis 自定义的 Logger 对象。只有这一块的是不一样的。我猜这就是他没有直接用driver的原因。
      // 可是为啥非要重写这个不可呢?我还没找到原因。todo
      return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
    }
  }

  @Override
  public <T> T unwrap(Class<T> iface) throws SQLException {
    throw new SQLException(getClass().getName() + " is not a wrapper.");
  }

  @Override
  public boolean isWrapperFor(Class<?> iface) throws SQLException {
    return false;
  }

  @Override
  public Logger getParentLogger() {
    // requires JDK version 1.6
    return Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
  }

}

 总结下:这个模块的功能作用是什么?

这个模块的dataSource 其实在PooledDataSource中是有被引用到的,PooledDataSource 这样做是为了提高代码的重用性。这个模块是非池化的数据库连接。说明这个模块的数据库连接适用那些获取数据库连接较小的应用。

五:jndi 模块

嘻嘻。我一看jndi感觉很熟悉,但却又感觉到陌生。我这里把记录对比下一下。

* JDBC(Java Database Connectivity)是由数据库中间服务商提供的,用于连接数据库的Java API。一组类和接口(对接数据库)。
* JNDI(Java Name Directory Interface)是为应用服务器(Tomcat)管理资源所设置的目录样式的唯一标识。(数据库、网页、文档等)
/**
 *    Copyright 2009-2020 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.datasource.jndi;

import java.util.Map.Entry;
import java.util.Properties;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import org.apache.ibatis.datasource.DataSourceException;
import org.apache.ibatis.datasource.DataSourceFactory;

/**
 * JDBC(Java Database Connectivity)是由数据库中间服务商提供的,用于连接数据库的Java API。一组类和接口(对接数据库)。
 * JNDI(Java Name Directory Interface)是为应用服务器(Tomcat)管理资源所设置的目录样式的唯一标识。(数据库、网页、文档等)
 *
 * 这个数据源的实现是为了能在如 EJB 或应用服务器这类容器中使用,
 * 容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。这种数据源配置只需要两个属性
 *
 * @author Clinton Begin
 */
public class JndiDataSourceFactory implements DataSourceFactory {

  // 这个属性用来在 InitialContext 中寻找上下文
  // (即,initialContext.lookup(initial_context))。这是个可选属性,
  // 如果忽略,那么 data_source 属性将会直接从 InitialContext 中寻找。
  public static final String INITIAL_CONTEXT = "initial_context";
  //  这是引用数据源实例位置的上下文的路径。提供了 initial_context
  //  配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。
  public static final String DATA_SOURCE = "data_source";
  public static final String ENV_PREFIX = "env.";

  private DataSource dataSource;

  /**
   * 在此方法中构造 dataSource
   *
   * 设置 DataSource 对象的属性
   * @param properties 属性
   */
  @Override
  public void setProperties(Properties properties) {
    try {
      InitialContext initCtx;
      Properties env = getEnvProperties(properties);

      // 创建 InitialContext 对象
      if (env == null) {
        initCtx = new InitialContext();
      } else {
        initCtx = new InitialContext(env);
      }
      // 从 InitialContext 上下文中,获取 DataSource 对象
      if (properties.containsKey(INITIAL_CONTEXT) && properties.containsKey(DATA_SOURCE)) {
        Context ctx = (Context) initCtx.lookup(properties.getProperty(INITIAL_CONTEXT));
        dataSource = (DataSource) ctx.lookup(properties.getProperty(DATA_SOURCE));
      } else if (properties.containsKey(DATA_SOURCE)) {
        dataSource = (DataSource) initCtx.lookup(properties.getProperty(DATA_SOURCE));
      }

    } catch (NamingException e) {
      throw new DataSourceException("There was an error configuring JndiDataSourceTransactionPool. Cause: " + e, e);
    }
  }

  /**
   * 获得 DataSource 对象
   *
   * @return DataSource 对象
   */
  @Override
  public DataSource getDataSource() {
    return dataSource;
  }

  /**
   * 获取系统的Properties 对象
   * @param allProps
   * @return
   */
  private static Properties getEnvProperties(Properties allProps) {
    final String PREFIX = ENV_PREFIX;
    Properties contextProperties = null;
    for (Entry<Object, Object> entry : allProps.entrySet()) {
      String key = (String) entry.getKey();
      String value = (String) entry.getValue();
      if (key.startsWith(PREFIX)) {
        if (contextProperties == null) {
          contextProperties = new Properties();
        }
        contextProperties.put(key.substring(PREFIX.length()), value);
      }
    }
    return contextProperties;
  }

}

六: 其他的类。都是比较简单的

DataSourceException (异常类),
DataSourceFactory(数据源接口) 

看完后,我不知道我学到了什么。但是我感觉pooled 包下的稍微有点点难。可能现在看这些是为了后续其他模块作铺垫也不一定。

今天看jdk动态代理的,那个动态代理的一二级缓存。没太懂。但是想想目前的总方向是了解jdk动态代理是怎么回事,然后需要继续看mybatis的代码。我就想先放一放。明天就是大年30,你们回家了吗?我没有。我此刻还在工作岗位。但是值得开心的事是,今天下午3点可以走了的。嘻嘻。更开心的事。昨天把数据源的代码看完了。今天把这一块的博客写了。觉得这块心上石放下了。

“小的时候痛会哭,因为有父母的庇护,有他们在我就是个孩子。往后的岁月不知道自己会怎么样。我想自己努力给家人最好的模样。”

猜你喜欢

转载自blog.csdn.net/lileronglilerong/article/details/113757585