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

浙公网安备 33010602011771号