Java中可基于JDK自定义轻量连接池,核心是用BlockingQueue缓存Connection、限制最大连接数、代理拦截close()实现归还、isValid()校验有效性,并保证线程安全。

Java 中自定义一个简单的数据库连接池,核心是复用 Connection 对象、控制最大连接数、避免连接泄漏,并提供线程安全的获取/归还机制。不需要依赖 HikariCP 或 Druid,用基础 JDK 就能实现一个轻量、可理解的版本。
1. 设计关键点:连接池要管什么?
一个最小可用的连接池需解决几个问题:
- 连接的创建与缓存:首次需要时创建,后续复用已有连接
- 连接数量限制:防止无节制创建导致数据库拒绝服务(如 maxActive=10)
-
线程安全:多线程并发取连接、还连接不能出错(用
BlockingQueue或synchronized) -
连接有效性检查:归还前或取出前简单验证(比如调用
isValid(1)) - 连接超时与销毁:空闲太久的连接主动关闭(简化版可先忽略,或用定时清理线程)
2. 核心类结构:PoolDataSource
定义一个实现 javax.sql.DataSource 的类,让 Spring 或 JDBC 工具能识别它:
// 简化版,不处理异常重试、JMX、属性配置等
立即学习“Java免费学习笔记(深入)”;
public class SimpleConnectionPool implements DataSource {
private final String url;
private final String username;
private final String password;
private final int maxPoolSize;
private final BlockingQueue<Connection> pool;
public SimpleConnectionPool(String url, String username, String password, int maxPoolSize) {
this.url = url;
this.username = username;
this.password = password;
this.maxPoolSize = maxPoolSize;
this.pool = new LinkedBlockingQueue<>(maxPoolSize);
// 预热:初始化一批连接
for (int i = 0; i < maxPoolSize; i++) {
try {
pool.offer(createConnection());
} catch (SQLException e) {
// 日志警告,不中断启动
System.err.println("预热连接失败:" + e.getMessage());
}
}
}
private Connection createConnection() throws SQLException {
return DriverManager.getConnection(url, username, password);
}
@Override
public Connection getConnection() throws SQLException {
Connection conn = pool.poll(); // 尝试取一个
if (conn == null || !conn.isValid(1)) {
conn = createConnection();
}
return new PooledConnection(conn, this); // 包装为可回收的代理
}
// 归还连接的方法(供 PooledConnection 调用)
void returnConnection(Connection conn) {
if (conn != null && !pool.offer(conn)) {
try {
conn.close(); // 池已满,直接关闭
} catch (SQLException ignored) {}
}
}
// 其他 DataSource 方法(略),可抛 UnsupportedOperationException
public <T> T unwrap(Class<T> iface) { throw new SQLException("Not supported"); }
public boolean isWrapperFor(Class<?> iface) { return false; }
public Connection getConnection(String u, String p) { throw new SQLException("Not supported"); }
// ... 其余方法类似
}
3. 关键:用代理包装 Connection,确保归还行为
直接返回原始 Connection,用户调用 close() 就真关了。必须拦截 close(),让它变成“归还到池”,而不是关闭。
用 java.lang.reflect.Proxy 实现动态代理:
public class PooledConnection implements InvocationHandler {
private final Connection target;
private final SimpleConnectionPool pool;
public PooledConnection(Connection target, SimpleConnectionPool pool) {
this.target = target;
this.pool = pool;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("close".equals(method.getName())) {
pool.returnConnection(target);
return null;
}
return method.invoke(target, args);
}
public static Connection wrap(Connection conn, SimpleConnectionPool pool) {
return (Connection) Proxy.newProxyInstance(
Connection.class.getClassLoader(),
new Class[]{Connection.class},
new PooledConnection(conn, pool)
);
}
}
然后在 getConnection() 中返回:wrap(conn, this) —— 这样用户调 close() 就只是归还,不是销毁。
4. 使用示例与注意事项
这样使用:
DataSource ds = new SimpleConnectionPool(
"jdbc:h2:mem:test", "sa", "", 5
);
try (Connection c = ds.getConnection();
PreparedStatement ps = c.prepareStatement("SELECT 1")) {
ResultSet rs = ps.executeQuery();
// ...
} // 自动归还
// 注意:务必用 try-with-resources 或显式 close,否则连接不会归还
常见坑提醒:
- 忘记
close()→ 连接永远不归还 → 池耗尽 → 后续请求阻塞或超时 - 代理未正确拦截所有 close 变体(如
Connection.close()是唯一标准入口,不用管 Statement/ResultSet) - 没做
isValid()检查 → 数据库重启后,池中连接全失效 → 查询报错 - 没加锁或用非线程安全队列 → 多线程下
poll()/offer()出错
不复杂但容易忽略细节,写出来能帮你真正理解连接池本质。实际项目仍推荐 HikariCP —— 它优化了性能、检测泄漏、支持健康检查,而我们这个版本适合教学和嵌入极简场景。


















