首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java EE 數據庫連接池與動態代理實戰:從手寫池到 JDK Proxy 改造 Connection

     編輯:Java 綜合教學

深入 Java EE 數據庫連接池核心:手寫 DataSource 實現 + 動態代理裝飾 Connection

數據庫連接池是 Java EE 應用性能的關鍵基礎設施。本文從零實現一個符合 javax.sql.DataSource 標準的連接池,並演示如何利用動態代理無侵入式增強 Connection.close() 行為(歸還而非真正關閉),同時對比繼承、裝飾器模式的優劣,幫你建立「池化思想」與「代理增強」的雙重認知。

第一部分:手寫連接池 MyPool 實現 DataSource


public class MyPool implements DataSource {
    // 連接池容器:LinkedList 支援高效首尾操作
    public static List pool = new LinkedList();
    
    // 靜態初始化:預創建 5 條連接
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            for (int i = 0; i < 5; i++) {
                Connection conn = DriverManager.getConnection(
                    "jdbc:mysql://localhost:3306/java", "root", "root");
                pool.add(conn);
            }
        } catch (Exception e) { e.printStackTrace(); }
    }
    
    // 核心:獲取連接(池空時自動擴容)
    @Override
    public Connection getConnection() throws SQLException {
        if (pool.size() == 0) {  // 池耗盡自動補充
            for (int i = 0; i < 5; i++) {
                Connection conn = DriverManager.getConnection(...);
                pool.add(conn);
            }
        }
        Connection conn = pool.remove(0); // 取出首部(FIFO)
        System.out.println("獲取連接,剩余:" + pool.size());
        return conn;
    }
    
    // 關鍵:歸還連接(而非關閉)
    public void resetConn(Connection conn) {
        try {
            if (conn != null && !conn.isClosed()) {
                pool.add(conn);  // 歸還池尾
                System.out.println("歸還連接,剩余:" + pool.size());
            }
        } catch (Exception e) { e.printStackTrace(); }
    }
    // ... 其他 DataSource 方法拋出 UnsupportedOperationException 或返回 null
}

第二部分:動態代理改造 Connection.close() → 歸還池

業務代碼習慣調用 conn.close(),但連接池需要 resetConn(conn)。動態代理完美解決:


// 測試類
public class JDBCTest {
    public static void main(String[] args) throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        MyPool pool = new MyPool();
        
        // 1. 獲取原始連接
        Connection rawConn = pool.getConnection();
        
        // 2. 創建代理:攔截 close() 轉調 resetConn()
        Connection proxyConn = (Connection) Proxy.newProxyInstance(
            Connection.class.getClassLoader(),
            new Class[]{Connection.class},
            new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    if ("close".equals(method.getName())) {
                        pool.resetConn(rawConn);  // 歸還池
                        return null;
                    }
                    return method.invoke(rawConn, args); // 其他方法透傳
                }
            }
        );
        
        // 3. 業務代碼照常使用 proxyConn
        Statement stmt = proxyConn.createStatement();
        ResultSet rs = stmt.executeQuery("select * from user");
        while (rs.next()) System.out.println(rs.getString("username"));
        
        // 4. 調用 close() 實際歸還池
        proxyConn.close();  // 不拋異常,不真正關閉底層連接
        rs.close(); stmt.close();
    }
}

三種增強模式對比

模式核心代碼優點缺點適用場景
繼承class MyConn extends Connection簡單直觀無法代理已有對象;最終類不可繼承設計階段預知類結構
裝飾器class DecoratedConn implements Connection { Connection target; }編譯期類型安全;可組合多層裝飾需手動實現所有接口方法(代碼量大)需靜態類型檢查、組合增強
動態代理Proxy.newProxyInstance(...)運行時生成;僅攔截關心方法;零侵入僅支援接口;性能略低於直接調用框架級增強(事務、日志、連接池)

裝飾器模式代碼示例(對比參考)


interface Animal { void eat(); void sound(); }
class Dog implements Animal { 
    public void eat() { System.out.println("吃"); }
    public void sound() { System.out.println("汪"); }
}
class BigDog implements Animal { // 裝飾器
    private Dog dog;
    public BigDog(Dog dog) { this.dog = dog; }
    public void eat() { dog.eat(); }           // 透傳
    public void sound() { System.out.println("大叫"); } // 增強
}

生產級連接池選型建議

  • HikariCP:當前性能王者,Spring Boot 2.x 默認,無鎖設計、字節碼優化
  • Druid:阿里出品,監控強大,SQL 防火牆、慢查詢分析
  • DBCP2 / C3P0:老牌穩定,但性能不如 HikariCP
  • Tomcat JDBC Pool:Tomcat 內置,配置簡單

核心心法總結

  1. 池化本質:用空間換時間,用「歸還」替換「關閉」,減少昂貴的 TCP 握手與驅動初始化
  2. 代理增強:動態代理是框架無侵入擴展的利器,核心是 InvocationHandler.invoke() 中的「分派邏輯」
  3. 接口導向:所有代碼面向 DataSource/Connection 接口編程,才能無縫切換實現與注入代理

動手跑通本文代碼,你將同時掌握「連接池內核機制」與「JDK 動態代理實戰」,為後續學習 Spring 事務管理、MyBatis 執行器、RPC 框架打下堅實基礎。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved