DataSource 是 Java 操作資料庫的關鍵介面,主流持久化框架(Hibernate、MyBatis、Spring JDBC)皆建立在其之上。雖然生產環境建議使用成熟的連線池(DBCP、HikariCP、C3P0),但親手實作一個極簡版本,能深入理解連線池的核心機制:連線複用、同步控制、生命週期管理。
javax.sql.DataSource 介面,滿足框架整合需求。Collections.synchronizedList(new LinkedList<>()) 作為線程安全的連線容器。getConnection() 優先從池取出,池空則新建;freeConnection() 歸還連線。package com.lavasoft.simpledatesource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.sql.DataSource;
import java.util.Collections;
import java.util.LinkedList;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.DriverManager;
import java.io.PrintWriter;
/**
* 簡易 DataSource 實作,示範連線池基本原理
* @author leizhimin
*/
public class SimpleDateSource implements DataSource {
private static final Log log = LogFactory.getLog(SimpleDateSource.class);
// 資料庫連線參數(實際專案應外部化至設定檔)
private static final String DRIVER_CLASS = "com.mysql.jdbc.Driver";
private static final String URL = "jdbc:mysql://127.0.0.1:3306/testdb";
private static final String USER = "root";
private static final String PASSWORD = "leizhimin";
// 連線池:線程安全的 LinkedList
private static final LinkedList<Connection> pool =
(LinkedList<Connection>) Collections.synchronizedList(new LinkedList<Connection>());
// 單例實例
private static final SimpleDateSource INSTANCE = new SimpleDateSource();
static {
try {
Class.forName(DRIVER_CLASS);
} catch (ClassNotFoundException e) {
log.error("找不到 JDBC 驅動類別!", e);
}
}
private SimpleDateSource() {}
public static SimpleDateSource getInstance() {
return INSTANCE;
}
@Override
public Connection getConnection() throws SQLException {
synchronized (pool) {
if (pool.size() > 0) {
return pool.removeFirst(); // 復用現有連線
}
return createConnection(); // 池空則新建
}
}
/** 歸還連線至池中 */
public static void freeConnection(Connection conn) {
if (conn != null) {
pool.addLast(conn);
}
}
private Connection createConnection() throws SQLException {
return DriverManager.getConnection(URL, USER, PASSWORD);
}
// --- 以下為 DataSource 介面要求的方法,簡易實作多拋出 UnsupportedOperationException 或返回預設值 ---
@Override public Connection getConnection(String u, String p) throws SQLException { return DriverManager.getConnection(URL, u, p); }
@Override public PrintWriter getLogWriter() throws SQLException { return null; }
@Override public void setLogWriter(PrintWriter out) throws SQLException {}
@Override public void setLoginTimeout(int seconds) throws SQLException {}
@Override public int getLoginTimeout() throws SQLException { return 0; }
@Override public <T> T unwrap(Class<T> iface) throws SQLException { return null; }
@Override public boolean isWrapperFor(Class<?> iface) throws SQLException { return false; }
}
synchronized (pool) 確保多執行緒環境下「取出/歸還」操作的原子性,避免同一連線被多執行緒同時使用。
本範例為教學簡化,省略了連線有效性檢查。實務上應在 getConnection() 時執行 conn.isValid(1) 或執行 SELECT 1,失效則丟棄並重建。
getLogWriter、setLoginTimeout 等標準介面方法。不到百行程式碼即完成核心功能,展示了連線池「借用-歸還」的基本模型。雖不適合直接用於生產,卻是理解 HikariCP 等高階池原理的最佳切入點。建議讀者嘗試加上「最大池大小限制」與「取得連線超時等待」機制,進一步完善。