Mybatis连接池实现及池化模式

MyBatis 使用 javax.sql.DataSource 标准接口,通过工厂模式创建不同类型的数据源实现。使用对象池模式实现PooledDataSource,用代理模式改变Connection#close方法逻辑,实现资源归还。

理解MyBatis中连接池的实现,可为学习其他组件如Druid 、HikariCP打好基础。

注:本文中源码来自mybatis 3.4.x版本,地址github.com/mybatis/myb...

一 DataSourceFactory接口

工厂模式,声明创建DataSource实例方法,有3个实现:

  • UnpooledDataSourceFactory,用于创建UnpooledDataSource
  • PooledDataSourceFactory,继承自UnpooledDataSourceFactory,用于创建PooledDataSource
  • JndiDataSourceFactory,第三方 DataSource (通过 JNDI 获取)

二 3种连接池

2.1 UnpooledDataSource

非池化数据源,有以下特点:

  • 每次 getConnection() 都创建新的数据库连接
  • 通过 DriverManager 获取连接
  • 适用于简单应用或测试环境

核心属性如下。每次获取连接,都直接调用DriverManager.getConnection创建一个实例。

2.2 PooledDataSource

池化数据源,维护一个池子(其实就是List),复用数据库连接,基于 UnpooledDataSource 实现。

PooledDataSource内部持有 UnpooledDataSource 创建真实连接,维护 PoolState 管理连接状态,使用动态代理拦截Connection.close() 方法,将连接归还到池子而非真的关闭。 连接池配置如下 连接池状态主要参数如下,使用中连接、空闲连接分别放在不同的List。 从PooledDataSource中获取到的连接,其实是一个代理对象。

PooledConnection

PooledDataSource池中其实是PooledConnection对象(并没有实现Connection接口),它持有Connection实例、Connection代理对象,并且实现了InvocationHandler接口,声明了动态代理逻辑:

  • 代理连接调用close方法时,会将连接放回连接池;
  • 代理连接调用其他非Object的方法时,会先检查连接是否有效;
  • 记录连接创建时间、最后一次使用时间、借出时间,用于连接有效性管理;

获取/归还连接

2.3 JndiDataSourceFactory

从 JNDI 上下文获取已配置的 DataSource,适用于应用服务器环境(如 Tomcat、WebLogic),不直接创建连接,而是使用容器管理的数据源。

三 池化模式实现

3.1 Mybatis的启发

MyBatis 的 PooledDataSource 虽然简单,但包含了池化技术的核心思想,比如:

  • 对象复用而非对象共享

池化不是简单地复用对象本身,而是复用昂贵的底层资源(真实连接),通过新包装对象隔离使用状态。

java 复制代码
// 归还时创建新的包装对象
PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
state.idleConnections.add(newConn);
conn.invalidate(); // 旧对象失效
  • 动态代理拦截资源释放

通过代理模式透明地改变资源释放行为,用户无感知地实现池化。

java 复制代码
// PooledConnection 实现 InvocationHandler
public Object invoke(Object proxy, Method method, Object[] args) {
    if (CLOSE.equals(methodName)) {
        // 归还而非关闭
        dataSource.pushConnection(this);
        return null;
    }
    return method.invoke(realConnection, args);
}
  • 超时强制回收机制

防止资源泄漏的最后防线,避免因使用方忘记释放导致池耗尽。

java 复制代码
if (longestCheckoutTime > poolMaximumCheckoutTime) {
    // 强制回收超时连接
    state.activeConnections.remove(oldestActiveConnection);
    conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
}
  • 状态与健康管理

除了检查资源状态,还需检查池中的connection是否断连。

java 复制代码
protected boolean pingConnection(PooledConnection conn) {
    // 1. 检查连接是否关闭
    // 2. 可选:执行 ping 查询测试
    // 3. 长时间未使用才 ping,避免频繁测试
}
  • 监控与可观测性

设计详细的统计信息,便于分析性能、调优和问题诊断。

java 复制代码
protected long requestCount;                    // 请求总数
protected long accumulatedRequestTime;          // 累计请求时间
protected long accumulatedCheckoutTime;         // 累计借出时间
protected long badConnectionCount;              // 坏连接数
protected long hadToWaitCount;                  // 等待次数

3.2 通用池化实现

基于以上分析,可以抽象出通用的池化实现模式:

java 复制代码
public class GenericObjectPool<T> {
    // 1. 双列表管理
    private List<PooledObject<T>> idle = new ArrayList<>();
    private List<PooledObject<T>> active = new ArrayList<>();
    
    // 2. 配置参数
    private int maxTotal;
    private int maxIdle;
    private long maxWaitMillis;
    
    // 3. 获取资源
    public T borrow() {
        synchronized (lock) {
            // 优先从空闲列表获取
            // 其次创建新对象
            // 或回收超时对象
            // 最后等待
            lock.wait(maxWaitMillis);
        }
    }
    
    // 4. 归还资源
    public void return(T obj) {
        synchronized (lock) {
            // 验证有效性
            // 重置状态
            // 放回空闲列表或销毁
            lock.notifyAll();
        }
    }
    
    // 5. 健康检查
    private boolean validate(T obj) {
    }
    
    // 6. 统计监控
    private PoolStats stats;
}
相关推荐
蛇皮划水怪5 小时前
深入浅出LangChain4J
java·langchain·llm
老毛肚7 小时前
MyBatis体系结构与工作原理 上篇
java·mybatis
风流倜傥唐伯虎7 小时前
Spring Boot Jar包生产级启停脚本
java·运维·spring boot
Yvonne爱编码7 小时前
JAVA数据结构 DAY6-栈和队列
java·开发语言·数据结构·python
Re.不晚7 小时前
JAVA进阶之路——无奖问答挑战1
java·开发语言
你这个代码我看不懂7 小时前
@ConditionalOnProperty不直接使用松绑定规则
java·开发语言
fuquxiaoguang8 小时前
深入浅出:使用MDC构建SpringBoot全链路请求追踪系统
java·spring boot·后端·调用链分析
琹箐8 小时前
最大堆和最小堆 实现思路
java·开发语言·算法
__WanG8 小时前
JavaTuples 库分析
java