在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);
}
}