Mybatis源码学习-数据源模块分析

所用到的设计模块:
工厂模式
代理模式

数据源模块

数据源的创建

数据源对象是比较复杂的对象,其创建过程相对比较复杂,对于 MyBatis 创建一个数据源,
具体来讲有如下难点:

  1. 常见的数据源组件都实现了 javax.sql.DataSource 接口;
  2. MyBatis 不但要能集成第三方的数据源组件,自身也提供了数据源的实现;
  3. 一般情况下,数据源的初始化过程参数较多,比较复杂;

这3种情况,所以mybatis使用了工厂模式,来实现数据源的创建

用工厂模式创建数据源的原因

  1. 数据源模块创建比较复杂
  2. 对象创建与使用耦合在一起 —— 违反单一原则
  3. 当业务扩展时,需要修改代码 —— 违反开闭原则

在这里插入图片描述

DataSourceFactory:

工厂接口,定义了创建DataSource方法

public interface DataSourceFactory {
  //设置DataSource的相关属性
  void setProperties(Properties props);
  //获取数据源
  DataSource getDataSource();
}

UnPooledDataSourceFactory

工厂实现类之一,用于创建UnpooledDataSource(不带连接池的数据源)

protected DataSource dataSource;

public UnpooledDataSourceFactory() {
   this.dataSource = new UnpooledDataSource();
}
@Override
public DataSource getDataSource() {
  return dataSource;
}

PooledDataSourceFactory

工厂实现类之一,用于创建PooledDataSource(带连接池的数据源)

public class PooledDataSourceFactory extends UnpooledDataSourceFactory {

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

}

DataSource

数据源接口,JDBC标准规范之一,定义了获取Connection方法

public interface DataSource  extends CommonDataSource, Wrapper {
  Connection getConnection() throws SQLException;
  Connection getConnection(String username, String password)
    throws SQLException;
}

UnpooledDataSource

不带连接池的数据源,获取连接的方式和手动通过JDBC获取连接是一样的

private String driver;
private String url;
private String username;
private String password;

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

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

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

PooledDataSource

带连接池的数据源,提高连接资源的复用性,避免频繁创建、关闭连接带来的开销


@Override
public Connection getConnection() throws SQLException {
  return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
}

@Override
public Connection getConnection(String username, String password) throws SQLException {
// getProxyConnection对象其实就是获取连接的代理对象,如果用户直接.close(),该连接并不会真正的释放,而是重新方会线程池中。
  return popConnection(username, password).getProxyConnection();  
}

//从连接池获取资源
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) {
     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 {// 没有空闲连接
         if (state.activeConnections.size() < poolMaximumActiveConnections) {//判断活跃连接池中的数量是否大于最大连接数
           // 没有则可创建新的连接
           conn = new PooledConnection(dataSource.getConnection(), this);
           if (log.isDebugEnabled()) {
             log.debug("Created connection " + conn.getRealHashCode() + ".");
           }
         } else {// 如果已经等于最大连接数,则不能创建新连接
           //获取最早创建的连接
           PooledConnection oldestActiveConnection = state.activeConnections.get(0);
           long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
           if (longestCheckoutTime > poolMaximumCheckoutTime) {//检测是否已经以及超过最长使用时间
             // 如果超时,对超时连接的信息进行统计
             state.claimedOverdueConnectionCount++;//超时连接次数+1
             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 happend.
                    Wrap the bad connection with a new PooledConnection, this will help
                    to not intterupt current executing thread and give current thread a
                    chance to join the next competion 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 {
             // 无空闲连接,最早创建的连接没有失效,无法创建新连接,只能阻塞
             try {
               if (!countedWait) {
                 state.hadToWaitCount++;//连接池累计等待次数加1
                 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
         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);
           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++;//累计的获取无效连接次数+1
           localBadConnectionCount++;//当前获取无效连接次数+1
           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.");
           }
         }
       }
     }

   }

   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;
 }

PooledConnection

使用线程池的数据源连接对象,通过代理模式,增强了真正的连接对象(关闭连接方法跟使用前连接是否有效)

public Connection getProxyConnection() {
    return proxyConnection;
}


@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  String methodName = method.getName();
  if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {//如果是调用连接的close方法,不是真正的关闭,而是回收到连接池
    dataSource.pushConnection(this);//通过pooled数据源来进行回收
    return null;
  } else {
    try {
  	  //使用前要检查当前连接是否有效
      if (!Object.class.equals(method.getDeclaringClass())) {
        // issue #579 toString() should never fail
        // throw an SQLException instead of a Runtime
        checkConnection();//
      }
      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.");
  }
}

PoolState

用于管理PooledConnection对象状态的组件,通过两个list分别管理空闲状态的连接资源和活跃状态的连接资源

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;
//累计的使用连接的时间。从连接取出到归还,算一次使用的时间;
protected long accumulatedCheckoutTime = 0;
//使用连接超时的次数
protected long claimedOverdueConnectionCount = 0;
//累计超时时间
protected long accumulatedCheckoutTimeOfOverdueConnections = 0;
//累计等待时间
protected long accumulatedWaitTime = 0;
//等待次数 
protected long hadToWaitCount = 0;
//无效的连接次数 
protected long badConnectionCount = 0;

使用连接池获取数据库连接的原理

获取数据库连接
PooledDataSource.popConnection(String username, String password)方法
获取数据库连接流程
释放数据库连接
PooledDataSource.pushConnection(PooledConnection conn)
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值