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


    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:别输给以前的自己





  • 相关阅读:
    TreeSet和TreeMap中“相等”元素可能并不相等
    求众数——摩尔投票
    5802. 统计好数字的数目
    快速幂
    LCP 07.传递消息
    332. 重新安排行程(欧拉回路问题)
    126. 单词接龙 II
    879. 盈利计划
    287. 寻找重复数
    239. 滑动窗口最大值
  • 原文地址:https://www.cnblogs.com/cynchanpin/p/6932993.html
Copyright © 2020-2023  润新知