【设计模式】代理模式实现连接池

来源:互联网 发布:杭州淘宝商学院地址 编辑:程序博客网 时间:2024/06/05 23:49
1. 连接池
创建Connection的过程是非常耗时的,为了保证Conection可以重用。应该将Connection进行池管理。
使用静态工厂方法管理一个唯一的连接:
/** * 用静态工厂方法管理一个唯一的可重用的连接 */public class ConnUtils {    private ConnUtils(){}    private static Connection con;    //在静态代码块中创建与数据库的连接    static{       try{           Class.forName("com.mysql.jdbc.Driver");           String url = "jdbc:mysql:///db_test?characterEncoding=UTf8";           con = DriverManager.getConnection(url,"root","root");       }catch(Exception e){           throw new RuntimeException(e.getMessage(),e);       }    }    //使用静态工厂方法,返回connection实例    public static Connection getCon(){[W1]        return con;    }}
2. 连接池的实现
为什么要有连接池?
1. 维护多个连接。必须要保证每一个线程获取到的是不同的connection对象。
2. 提供一个方法可以回收连接。

以下是最基本的实现:
public class ConnUtils2 {    //声明一个容器,放所有声明的连接Connection    private static List<Connection> pool = new ArrayList<Connection>();    static{       try{           Class.forName("com.mysql.jdbc.Driver");           String url = "jdbc:mysql:///db_test?characterEncoding=UTf8";           for(int i=0;i<3;i++){              //创建三个连接              Connection con = DriverManager.getConnection(url,"root","root");              //将这个三个连接放到pool中去              pool.add(con);           }           System.err.println("连接:"+pool);       }catch(Exception e){           throw new RuntimeException(e.getMessage(),e);       }    }    public static Connection getCon(){       synchronized (pool) {           Connection con = pool.remove(0);           try {              Thread.sleep(100);           } catch (InterruptedException e) {              e.printStackTrace();           }           System.err.println("还有:"+pool.size());           return con;       }    }    //手工还连接 – close    public static void back(Connection con){       System.err.println("还连接:"+con);       pool.add(con);    }}
实际中总是调用close方法,所以为了回收连接。我们应该重写close方法。对close方法增强。可以使用以下三种方式进行方法增强:子类,包装,代理。

3. 在代码中调用close时也可以还连接,对close方法进行修改。
动态代理作用:
1. 对某个方法增强。
2. 在不污染源类的情况下,修改原类的行为。
代理类,与被代理类,两个不同的实体。

要求:
所有被代理的类,都必须要实现同一个接口。
本质上是对方法进行修改,但其实它是通过反射执行的某个方法。

4. 动态代理的核心类
Proxy – 用于创建给定接口的子类,在内存中动态的创建。$Proxy0.此类只使用一次。
InovocationHandler – 执行句柄。在执行时可以获取被代理类的所有反射。用户的每一次调用都会被这个句柄拦截到。

5. 代理的任务
1. 在内存中创建某个接口的子类。
2. 拦截所有在代理上执行的方法。(除了getClass方法)
 
6. 用动态代理书写连接池
代理的目标:原生的connection。
代理的目的:修改close方法,让close方法不可以关闭连接,且主动收回连接。

通过动态代理,和线程通讯:
1. 对Connection进行代理。
2. 在获取Connection时,通过同步,如果没有连接时,就让线程进入等待池。
3. 修改close方法,且在还了连接以后唤醒正在等待的线程。

public class ConnUtils3 {    //声明连接池维护所有的连接    private static List<Connection> pool = new ArrayList<Connection>();    //静态代码块中创建多个连接    static{       try{           Class.forName("com.mysql.jdbc.Driver");           String url = "jdbc:mysql:///db_test?characterEncoding=UTF8";           for(int i=0;i<3;i++){              final Connection con = DriverManager.getConnection(url,"root","root");              //对con对象进行动态代理              Object proxyedCon =                      Proxy.newProxyInstance(                            ConnUtils3.class.getClassLoader(),                            new Class[]{Connection.class},                            //声明执行句柄,只对close方法设置拦截                            new InvocationHandler() {                                public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {                                   if(method.getName().equals("close")){                                       System.err.println("想关闭连接,不能关,还连接");                                       //将proxy再加到pool中,这个proxy就是proxyedCon                                       synchronized (pool) {                                          pool.add((Connection) proxy);                                          pool.notify();                                       }                                       return null;                                   }else{                                       System.err.println("放行"+method.getName());                                       return method.invoke(con, args);  // 通过反射执行被代理对象的方法                                    }                                }                            });              //将代理对象添加到池中              pool.add((Connection) proxyedCon);           }       }catch(Exception e){           throw new RuntimeException(e.getMessage(),e);       }    }    /**     * 提供一个静态工厂方法返回一个连接     */    public static Connection getCon(){       synchronized (pool) {           if(pool.size()==0){              try {                  pool.wait();              } catch (InterruptedException e) {                  e.printStackTrace();              }              return getCon();           }           Connection con = pool.remove(0);//返回一个代理的connection对象           System.err.println("还有几个:"+pool.size());           return con;       }    }}

7. 再说连接池的作用
1. 维护多个连接。
在初始化时创建List,将多个连接放到List中去.
2. 可以在close时回收连接
对Connection进行动态代理,
对close方法进行拦截。




Author:顾故

Sign:别输给曾经的自己





2 0