数据库连接池、动态代理

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