MyBatis之配置文件解析_数据库联接池

数据源工厂接口及实现类

Mybatis定义了接口DataSourceFactory,可用来创建DataSource对象,这个接口很简单,只定义了两个方法。

public interface DataSourceFactory {
  // 提供属性对象供数据源对象使用
  void setProperties(Properties props);
  // 获得DataSource对象
  DataSource getDataSource();

}

Mybatis提供了DataSourceFactory的实现类:UnpooledDataSourceFactory、PooledDataSourceFactory及JndiDataSourceFactory。其中PooledDataSourceFactory是UnpooledDataSourceFactory的子类,UnpooledDataSourceFactory创建的DataSource是UnpooledDataSource对象,PooledDataSourceFactory创建的DataSource是PooledDataSource。PooledDataSource是我们重点分析的对象

配置文件中数据源的配置

数据源的配置标签是dataSource,其属性type是一个DataSourceFactory实现类的别名,例子中的别名是POOLED,即PooledDataSourceFactory,这个别名在Configuration构造方法中注册。

<!-- mybatis环境相关配置  -->
<environments default="development">
    <environment id="development">
        <!-- 事务工厂的类型, JDBC是mybatis提供的 -->
        <transactionManager type="JDBC"/>
        <!-- 数据源类型, POOLED是mybatis提供的,配置的类型是DataSourceFactory -->
        <dataSource type="POOLED">
            <!-- 这种形式的配置,解析时转换为Properties对象, 创建DataSource对象,可以用这里的name-value,设置属性值 -->
            <property name="driver" value="${driver}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${uname}"/>
            <property name="password" value="${password}"/>
        </dataSource>
    </environment>
    <!-- 可以配置多个环境 -->
</environments>

DataSourceFactory默认实现类别名的注册:

public Configuration() {
  // DataSourceFactory默认实现类别名的注册
  typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
  typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
  typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
}

dataSource标签的解析

XMLConfigBuilder的方法dataSourceElement用来解析dataSource标签,该方法比较简单,即获得dataSource的属性type,同时获得dataSource标签下的所有property子标的集合,创建好DataSourceFactory对象,并设置属性对象。若没有配置dataSource标签,则抛出BuilderException异常。environmentsElement方法获得DataSourceFactory后,即可创建好DataSource对象,保存在Environment中,Environment持有一个DataSource对象

private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      Properties props = context.getChildrenAsProperties();
      DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance();
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}

UnpooledDataSource代码分析

UnPooledDataSource实现了DataSource接口,通过DriverManager获得Connection对象。

public class UnpooledDataSource implements DataSource {

  private ClassLoader driverClassLoader;
  private Properties driverProperties;
  private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<>();
  // JDBC联接数据库的四大参数,
  private String driver;
  private String url;
  private String username;
  private String password;

  private Boolean autoCommit;
  private Integer defaultTransactionIsolationLevel;
  private Integer defaultNetworkTimeout;
  // 提供多个构造方法获得UnPooledDataSource对象,PooledDataSource通过这个方法获得真正的联接对象
  // 真正获得联接对象的方法
  private Connection doGetConnection(Properties properties) throws SQLException {
    // 初始化参数  
    initializeDriver();
    // 获得Connection对象  
    Connection connection = DriverManager.getConnection(url, properties);
    // 设置connection对象的属性,包插网络过期时间、是否自动提交、事务隔离级别
    configureConnection(connection);
    return connection;
  }    
    
}

PooledDataSource代码分析

PooledDataSource也实现了DataSource是Mybatis数据库联接池的实现,内部使用UnPooldDataSource获得联接对象

PooledDataSource有两个帮助类PoolState它与PooledDataSource是一对一关联,互为所有,主要描述池的状态,如有空闲联接的集合与活动联接的集合

PooledConnection是mybatis内部使用的一个类,这是联接池中存放的对象,实际返回的联接对象是Connection一个动态代理对象


public class PooledDataSource implements DataSource {

  private static final Log log = LogFactory.getLog(PooledDataSource.class);
  // 池的状态,
  private final PoolState state = new PoolState(this);
  // 所使用的UnpooledDataSource
  private final UnpooledDataSource dataSource;

  // OPTIONAL CONFIGURATION FIELDS,池的特性
  protected int poolMaximumActiveConnections = 10;	// 最大活动联接
  protected int poolMaximumIdleConnections = 5;		// 最大空闲联接
  protected int poolMaximumCheckoutTime = 20000;	// 最大	
  protected int poolTimeToWait = 20000;				// 最大等待时间
  protected int poolMaximumLocalBadConnectionTolerance = 3;		// 最大本地坏联接
  protected String poolPingQuery = "NO PING QUERY SET";
  protected boolean poolPingEnabled;
  protected int poolPingConnectionsNotUsedFor;
    
  // 多个构造方法创建PooledDataSource对象,略
  // 多个重载的getConnection方法,如,是从联接池中获得联接对象
   @Override
  public Connection getConnection() throws SQLException {
    // 返回的是动态代理联接对象(Connection动态代理对象),在PooledConnection中定义  
    return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
  }
    
  // 从联接池中获得PooledConnection对象的核心方法,返回PooledConnection对象    
  private PooledConnection popConnection(String username, String password) throws SQLException {
    boolean countedWait = false;
    PooledConnection conn = null;
    long t = System.currentTimeMillis();
    int localBadConnectionCount = 0;
	// 循环等待一直到有可用的PooledConnection对象
    while (conn == null) {
      synchronized (state) {
        // 如果池中有空闲的联接,  
        if (!state.idleConnections.isEmpty()) {
          // Pool has available connection,获得这个联接,并且从空闲池中去掉这个联接
          conn = state.idleConnections.remove(0);
          if (log.isDebugEnabled()) {
            log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
          }
        } else {
          // Pool does not have available connection,没有空闲的联接池对象
          // 如果还没有达到最大活动数,则创建一个新的PooledConnection对象,  
          if (state.activeConnections.size() < poolMaximumActiveConnections) {
            // Can create new connection,则创建一个新的PooledConnection对象,
            conn = new PooledConnection(dataSource.getConnection(), this);
            if (log.isDebugEnabled()) {
              log.debug("Created connection " + conn.getRealHashCode() + ".");
            }
          } else {
            // Cannot create new connection,如果不允许创建新的联接对象
            // 取出活动联接中的第一个联接  
            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");
                }
              }
              // 基于原来的联接(真实的联接),创建一个新的PooledConnection对象  
              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对象上等待释放出的联接,等待最长时间为poolTimeWait  
                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,测试这个联接是否有效
          // 联接有效  
          if (conn.isValid()) {
            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);
            // 联接请求数加1  
            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;	// 重新获得联接
            // 若坏的联接个数太多,则抛出异常  
            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.");
            }
          }
        }
      }

    }
	// 现测试一下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.");
    }
      
    // 返回PooledConnection对象
    return conn;
  } 
}
    
// 将一个联接对象返回给联接池,当调用close方法时,通过动态代理对象,调用这个方法    
protected void pushConnection(PooledConnection conn) throws SQLException {

    synchronized (state) {
      // 从活动联接池中去掉这个联接  
      state.activeConnections.remove(conn);
      // 若联接有效  
      if (conn.isValid()) {
        //   
        if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
          state.accumulatedCheckoutTime += conn.getCheckoutTime();
          if (!conn.getRealConnection().getAutoCommit()) {
            conn.getRealConnection().rollback();
          }
          // 基于原来的Connection对象,创建新的PooledConnection对象  
          PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
          // 加入到空闲池中
          state.idleConnections.add(newConn);
          newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
          newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
          // 让原来的conn失效  
          conn.invalidate();
          if (log.isDebugEnabled()) {
            log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");
          }
          // 唤醒在state上等待的线程,有空闲的联接对象可用  
          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++;
      }
    }
  }    
    
    
    
    
    
public class PoolState {

  protected PooledDataSource dataSource;
  // 这里存放的是PooedConnection对象,联接池的数据结构
  // 空闲联接对象集合  
  protected final List<PooledConnection> idleConnections = new ArrayList<>();
  // 空闲活动联接对象集合  
  protected final List<PooledConnection> activeConnections = new ArrayList<>();
  protected long requestCount = 0;
  protected long accumulatedRequestTime = 0;
  protected long accumulatedCheckoutTime = 0;
  protected long claimedOverdueConnectionCount = 0;
  protected long accumulatedCheckoutTimeOfOverdueConnections = 0;
  protected long accumulatedWaitTime = 0;
  protected long hadToWaitCount = 0;
  protected long badConnectionCount = 0;    
}

// 内部使用的类,实现InvocationHandler,实现了invoke方法    
class PooledConnection implements InvocationHandler {

  private static final String CLOSE = "close";
  // 动态代理实现的接口  
  private static final Class<?>[] IFACES = new Class<?>[] { Connection.class };

  private final int hashCode;
  private final PooledDataSource dataSource;
  // 真正的联接对象  
  private final Connection realConnection;
  // 代理联接对象,InvocationHandler即PooledConnection  
  private final Connection proxyConnection;
  private long checkoutTimestamp;
  private long createdTimestamp;
  private long lastUsedTimestamp;
  private int connectionTypeCode;
  private boolean valid;
  
  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创建了动态代理对象  
    this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
  }  
    
    
    
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    String methodName = method.getName();
    // 当调用Connection的close方法时,调用PooledDataSource的pushConnection方法  
    if (CLOSE.equals(methodName)) {
      dataSource.pushConnection(this);
      return null;
    }
    try {
      if (!Object.class.equals(method.getDeclaringClass())) {
        // issue #579 toString() should never fail
        // throw an SQLException instead of a Runtime
        checkConnection();
      }
      // 调用原始Connection方法  
      return method.invoke(realConnection, args);
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }

  }
  
}    


posted @   beckwu  阅读(129)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示