數據庫連接池、動態代理

在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);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章