一、使用LinkedList保存鏈接 java
即便是最簡單的JDBC操做,也須要包含如下幾步:創建鏈接、建立SQL語句、執行語句、處理執行結果、釋放資源,其中創建鏈接步驟是很耗費計算機性能的,若是咱們每次進行JDBC操做都建立新的JDBC鏈接,使用完後再當即釋放鏈接,這樣作會耗費大量性能。更合理的作法應該是:建立JDBC鏈接,使用JDBC鏈接,使用完後不是馬上釋放JDBC鏈接,而是把鏈接緩存起來,當下一次操做JDBC時,咱們能夠直接使用緩存中已經鏈接上的JDBC鏈接。mysql
如下的代碼經過一個LinkedList保存建立的JDBC鏈接,在剛建立的時候,咱們會先創建10個鏈接並保存在list中,當客戶代碼須要使用Connection的時候,咱們直接從list中取出第一個Connection返回,當客戶代碼使用完Connection,須要調用free()方法,把Connection再放回list中:sql
public class DataSource1 { LinkedList<Connection> connectionPool = new LinkedList<Connection>(); public DataSource1() { try { for (int i = 0; i < 10; i++) { this.connectionPool.addLast(this.createConnection()); } } catch (SQLException e) { throw new ExceptionInInitializerError(e); } } public Connection getConnection() { return connectionPool.removeFirst(); } public void free(Connection conn) { connectionPool.addLast(conn); } private Connection createConnection() throws SQLException { return DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", ""); } }
二、控制鏈接數量 數據庫
在上例的getConnection()方法中,咱們沒有檢測connectionPool中是否包含Connection,直接返回connectionPool中的第一個Connection,這樣作是不安全的,在從connectionPool中返回Connection以前,咱們首先應該檢測connectionPool中是否有Connection,如有,返回connectionPool中的第一個Connection,若是沒有,咱們能夠新建立一個並返回。可是這樣有一個問題,若是請求的線程不少,咱們這樣無限制地建立不少Connection可能致使數據庫阻塞,由於數據庫能夠支持的鏈接數是有限的,因此咱們應該控制新建Connection的上限,若沒有達到上限,咱們能夠建立並返回,若是達到鏈接上限,那麼咱們就拋出異常。同時咱們應該在getConnection()上加鎖,保證多線程安全性,修改後代碼以下,咱們用initCount表示list初始化大小,maxCount表示list最大大小,currentCount表示如今存活的Connection數量:緩存
public class DataSource2 { private static int initCount = 10; private static int maxCount = 30; private int currentCount = 0; LinkedList<Connection> connectionPool = new LinkedList<Connection>(); public DataSource2() { try { for (int i = 0; i < initCount; i++) { this.connectionPool.addLast(this.createConnection()); this.currentCount++; } } catch (SQLException e) { throw new ExceptionInInitializerError(e); } } public Connection getConnection() throws SQLException { synchronized (connectionPool) { if (connectionPool.size() > 0) return connectionPool.removeFirst(); if (this.currentCount < maxCount) { this.currentCount++; return createConnection(); } throw new SQLException("已沒有連接"); } } public void free(Connection conn) { connectionPool.addLast(conn); } private Connection createConnection() throws SQLException { return DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc", "root", ""); } }
三、動態代理攔截close方法 安全
上面的代碼有個問題,那就是要關閉Connection必須調用咱們的free()方法,不能直接調用Connection上的close()方法,這對於一些習慣使用完Connection就close()的用戶並非很友好,很容易致使它們忘記把用完的Connection還回connectionPool,爲了讓用戶保持原有習慣,咱們但願可以改寫Connection的close()方法,讓它不是直接關閉鏈接,而是把鏈接還回connectionPool中,其餘方法保持原來不變,對於這種需求,咱們可使用動態代理實現:多線程
public class DataSource3 { private static int initCount = 1; private static int maxCount = 1; int currentCount = 0; LinkedList<Connection> connectionsPool = new LinkedList<Connection>(); public DataSource3() { try { for (int i = 0; i < initCount; i++) { this.connectionsPool.addLast(this.createConnection()); this.currentCount++; } } catch (SQLException e) { throw new ExceptionInInitializerError(e); } } public Connection getConnection() throws SQLException { synchronized (connectionsPool) { if (this.connectionsPool.size() > 0) return this.connectionsPool.removeFirst(); if (this.currentCount < maxCount) { this.currentCount++; return this.createConnection(); } throw new SQLException("已沒有連接"); } } public void free(Connection conn) { this.connectionsPool.addLast(conn); } private Connection createConnection() throws SQLException { Connection realConn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/jdbc", "root", ""); MyConnectionHandler proxy = new MyConnectionHandler(this); return proxy.bind(realConn); } } class MyConnectionHandler implements InvocationHandler { private Connection realConnection; private Connection warpedConnection; private DataSource3 dataSource; MyConnectionHandler(DataSource3 dataSource) { this.dataSource = dataSource; } Connection bind(Connection realConn) { this.realConnection = realConn; this.warpedConnection = (Connection) Proxy.newProxyInstance(this .getClass().getClassLoader(), new Class[] { Connection.class }, this); return warpedConnection; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("close".equals(method.getName())) { this.dataSource.connectionsPool.addLast(this.warpedConnection); } return method.invoke(this.realConnection, args); } }
上述代碼的關鍵點是MyConnectionHandler類,在該類的bind()方法中返回了一個wrapedConnection,wrapedConnection的建立方法以下:ide
this.warpedConnection = (Connection) Proxy.newProxyInstance(this.getClass().getClassLoader(),
new Class[] { Connection.class },this);
Proxy.newProxyInstance()方法是Java動態代理的關鍵方法,該方法會在運行時在內存中動態建立一個類,該方法的第一個參數是指定一個ClassLoader,第二個參數指定動態建立類實現的接口,第三個參數指定在該類上調用的方法應該轉給哪一個類,在這裏指定了this,因此全部方法都會轉給MyConnectionHandler,準確的說是MyConnectionHandler的invoke方法:性能
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("close".equals(method.getName())) { this.dataSource.connectionsPool.addLast(this.warpedConnection); } return method.invoke(this.realConnection, args); }
在invoke()方法中,咱們能夠看到第二個參數傳遞了調用的Method,咱們根據傳遞的Method對象判斷,用戶調用的是不是close方法,若是是close方法,那麼咱們就把這個Connection從新加入list,若是不是,那麼咱們就執行真正Connection上的相應方法。this
還須要注意的是,如今調用createConnection()產生的Connection對象已經不是原始的Connection對象,而是調用MyConnectionHandler類上bind()方法動態產生的代理類:
private Connection createConnection() throws SQLException { Connection realConn = DriverManager.getConnection( "jdbc:mysql://localhost:3306/jdbc", "root", ""); MyConnectionHandler proxy = new MyConnectionHandler(this); return proxy.bind(realConn); }