在web項目中,Java訪問資料庫採用的是多使用者操作,需要頻繁串連資料庫,一種方法是來一個請求給一個操作對象,這種方法想法簡單,但存在巨大隱患,如果訪問量特別的大,資料庫連接對象過多,可能導致奔潰。好的方法是,採用資料庫連接時統一管理,包括資料庫連接對象的個數限制以及使用後回收。
說到管理,根據程式員習慣,拿到連線物件進行完操作後,一般會關閉連線物件,這就會產生一個問題,下一次別的使用者再拿到回收後的關閉對象後,由於對象已關閉,再進行其他動作的話,會產生異常。所以我們要修改close()這個函數的功能。
思路:要修改函數功能,基本上是採用覆蓋的方法,一種方式是採用裝飾模式,即繼承加組合,但這種方式有個弊端,如果要實現的介面有很多函數,代碼量非常大,所以一般不採用。另一種方式是採用代理模式。下面想詳細敘述
代理要用到一個關鍵的API 類,即Proxy,此外要用到一個介面和介面的一個執行個體對象。
Proxy.newProxyInstance(loader, interfaces, h);
第一個參數:與被代理對象處於同一空間的類載入器
第二個參數:要實現介面數組
第三個參數:實現功能的控制代碼對象
下面是一個租房者通過中介租房的例子來示範:
介面
public interface IRenter { public abstract void rent(int n);}
介面實作類別
public class Renter implements IRenter{ @Override public void rent(int n) { System.out.println("給你"+n+"間房,請交500元錢"); }}
關鍵類
public class Client { private static IRenter rent = new Renter(); public static void main(String[] args) { Object obj =Proxy.newProxyInstance( Client.class.getClassLoader(), // new Class[]{IRenter.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("我代理的......"); return method.invoke(rent, args); } }); IRenter o = (IRenter)obj; o.rent(3); }}
根據原理。做一個代理工具類:
public class ProxyUtil implements InvocationHandler{ private Object srcObj; public ProxyUtil(Object srcObj) { super(); this.srcObj = srcObj; } public static Object getProxy(Object srcObj){ Object obj = Proxy.newProxyInstance( ProxyUtil.class.getClassLoader(), srcObj.getClass().getInterfaces(), new ProxyUtil(srcObj) ); return obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(srcObj, args); }}
資料庫連接池的代碼實現:
public class ConnsUtil { private static List<Connection>pool = new ArrayList<Connection>(); private static int num = 3; static{ Properties p = new Properties(); try { p.load(ConnsUtil.class.getClassLoader().getResourceAsStream("jdbc.properties")); String driver = p.getProperty("driver"); String url = p.getProperty("url"); String user = p.getProperty("username"); String password = p.getProperty("password"); Class.forName(driver); for(int i=0;i<num;i++){ final Connection conn = DriverManager.getConnection(url, user, password); Connection con = (Connection)Proxy.newProxyInstance( ConnsUtil.class.getClassLoader(), new Class[]{Connection.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equalsIgnoreCase("close")){ pool.add((Connection)proxy); return null; } return method.invoke(conn, args); } } ); pool.add(con); } } catch (Exception e) { e.printStackTrace(); } } public static synchronized Connection getConnection(){ if(pool.size()<=0){ try { Thread.sleep(100); return getConnection(); } catch (InterruptedException e) { e.printStackTrace(); } } return pool.remove(0); }}