Mybatis-连接池源码分析(不包含jndi)

一.数据源DataSource的创建过程

MyBatis在初始化时,解析此文件,根据<dataSource>的type属性来创建相应类型的的数据源DataSource,即:

type=”POOLED”  :MyBatis会创建PooledDataSource实例-(连接池)继承自DataSource -避免同一个链接同时被两个请求使用(并发问题  需要加锁

type=”UNPOOLED” :MyBatis会创建UnpooledDataSource实例-非连接池)继承自DataSource -每调用一次就会产生一个新的Connection实例对象。

type=”JNDI”     :MyBatis会从JNDI服务上查找DataSource实例,然后返回使用-(使用JNDI实现的数据源)

二.DataSource什么时候创建Connection对象

当我们需要创建SqlSession对象并需要执行SQL语句时,这时候MyBatis才会去调用dataSource对象来创建java.sql.Connection对象。也就是说,java.sql.Connection对象的创建一直延迟到执行SQL语句的时候
三.pooled连接池

准备了两个池子:idleConnections空闲池 idleConnections活动池(list集合)

请求过程:

1.  先看是否有空闲(idle)状态下的PooledConnection对象,如果有,就直接返回一个可用的PooledConnection对象;否则进行第2步。

2.  查看活动状态的PooledConnection池activeConnections是否已满;如果没有满,则创建一个新的PooledConnection对象,然后放到activeConnections池中,然后返回此PooledConnection对象;否则进行第三步;

3.  看最先进入activeConnections池中的PooledConnection对象是否已经过期:如果已经过期,从activeConnections池中移除此对象,然后创建一个新的PooledConnection对象,添加到activeConnections中,然后将此对象返回;否则进行第4步。

4.  线程等待,循环2步

代码分析如下

  while (conn == null) {
      synchronized (state) {//用Staye做同步,空闲的链接集合和活跃的链接集合都在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
          if (state.activeConnections.size() < poolMaximumActiveConnections) {//现在没有空闲链接了,那看一下活跃队列,如果当前的链接结合还没满。
            // Can create new connection
            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) {//既然现在满了,那就检查一下看看能不能有可以抢占的链接,先看一下这个连接被用了多久了,有米有超过我的限制。
              //如果超限了,那么我此时应该把它抢占
              state.claimedOverdueConnectionCount++;
              state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
              state.accumulatedCheckoutTime += longestCheckoutTime;
              state.activeConnections.remove(oldestActiveConnection);//移除这个连接
              if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
                try {
                  oldestActiveConnection.getRealConnection().rollback();
                } catch (SQLException e) {
                  log.debug("Bad connection. Could not roll back");
                }  
              }
 
              state.claimedOverdueConnectionCount++;
              state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
              state.accumulatedCheckoutTime += longestCheckoutTime;
              state.activeConnections.remove(oldestActiveConnection);//移除这个连接
     conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);//用原来老的Connection创建一个新的PooledConnection
              conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
              conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
              oldestActiveConnection.invalidate();
              if (log.isDebugEnabled()) {
                log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
              }
            } else { //如果此时我没有可以抢占的连接池。wait一会儿
              // 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); //wait唤醒之后继续while 重复上述步骤。
                state.accumulatedWaitTime += System.currentTimeMillis() - wt;
              } catch (InterruptedException e) {
                break;
              }
            }
          }
        }
        if (conn != null) { //如果我现在拿到了链接,不管是从哪拿的吧
          if (conn.isValid()) {//先判断一下我的链接是否还有效,有效的话我就初始化一下。那么此时默认下一次循环会退出while
            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;

四:connection对象的回收

unpool:调用 connection.close()方法,关闭connection连接,释放资源

pool:Connection对象调用了close()方法,而实际是将其添加到连接池中(

这是要使用代理模式,为真正的Connection对象创建一个代理对象,代理对象所有的方法都是调用相应的真正Connection对象的方法实现。当代理对象执行close()方法时,要特殊处理,不调用真正Connection对象的close()方法,而是将Connection对象添加到连接池中

 

使用代理模式,为真正的Connection对象创建一个代理对象,代理对象所有的方法都是调用相应的真正Connection对象的方法实现。当代理对象执行close()方法时,要特殊处理,不调用真正Connection对象的close()方法,而是将Connection对象添加到连接池中。

调用PooledDataSource的getConnection()方法返回的就是这个proxyConnection对象。

当我们调用此proxyConnection对象上的任何方法时,都会调用PooledConnection对象内invoke()方法。

让我们看一下PooledConnection类中的invoke()方法定义:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
//当调用关闭的时候,回收此Connection到PooledDataSource中
if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {
dataSource.pushConnection(this);
return null;
} else {
try {
if (!Object.class.equals(method.getDeclaringClass())) {
checkConnection();
}
return method.invoke(realConnection, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}

从上述代码可以看到,当我们使用了pooledDataSource.getConnection()返回的Connection对象的close()方法时,不会调用真正Connection的close()方法,而是将此Connection对象放到连接池中。

参考资料:https://blog.csdn.net/luanlouis/article/details/37671851

posted @ 2020-05-22 10:32  我是张某某  阅读(412)  评论(0)    收藏  举报