release() 而非標準 Connection.close(),改變使用習慣。close(),連線池無法感知,導致連線洩漏與狀態不一致。利用 java.lang.reflect.Proxy 為真實 Connection 建立代理,攔截 close() 轉為「標記閒置歸還池中」,而非真正關閉底層連線。
bind/lookup/unbind)。javax.sql.DataSource,內部維護兩個集合:FreeConnectionPool、BusyConnectionPool。InvocationHandler,包裝真實連線,攔截 close()、createStatement()、prepareStatement()、事務方法。class _Connection implements InvocationHandler {
private static final String CLOSE = "close";
private final Connection realConn;
private Statement stmtRef;
private PreparedStatement pstmtRef;
private boolean inUse = false;
_Connection(ConnectionParam param) { /* 建立真實連線 */ }
public Connection getProxy() {
return (Connection) Proxy.newProxyInstance(
realConn.getClass().getClassLoader(),
realConn.getClass().getInterfaces(),
this
);
}
@Override
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
String name = m.getName();
if (CLOSE.equals(name)) {
inUse = false; // 歸還池中
closeStatements(); // 關閉關聯 Statement
return null;
}
if ("createStatement".equals(name)) {
Object stmt = m.invoke(realConn, args);
stmtRef = (Statement) stmt;
return stmt;
}
if ("prepareStatement".equals(name)) {
Object ps = m.invoke(realConn, args);
pstmtRef = (PreparedStatement) ps;
return ps;
}
// 事務方法若不支援則忽略
if (("commit".equals(name) || "rollback".equals(name)) && !supportsTransactions) return null;
return m.invoke(realConn, args);
}
void reallyClose() throws SQLException { realConn.close(); } // 池關閉時真正釋放
}
public Connection getConnection() throws SQLException {
_Connection wrapper = getFreeWrapper(); // 從閒置池取出或新建
wrapper.inUse = true;
moveToBusyPool(wrapper);
return wrapper.getProxy(); // 回傳代理
}
DataSource ds = ConnectionFactory.lookup("pool");
Connection conn = ds.getConnection(); // 標準 API
try {
PreparedStatement ps = conn.prepareStatement(sql);
ps.executeUpdate();
} finally {
conn.close(); // 實際歸還池中,非關閉
}
完美解決兩大痛點:保留標準 JDBC 習慣、連線池完全掌控連線生命週期。