【Java EE 学习 15】【自定义数据库连接池之动态代理的使用】

一、动态代理的作用

  使用动态代理可以拦截一个对象某个方法的执行,并执行自定义的方法,其本质是反射

  优点:灵活

  缺点:由于其本质是反射,所以执行速度相对要慢一些

二、数据库连接池设计思想

  1.为什么要使用数据库连接池:创建Connection对象的过程是非常耗时的,为了保证Connection可以重用,应该对Connection进行管理。

  2.设计要求:

    (1)连接池能够实现维护多个连接,必须要保证每一个线程获取到的是不同的Connection对象。

    (2)提供一个方法能够回收连接。

  3.最基本的实现

package day15_2;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;

/**
 * 使用最基本的方式创建数据库连接池
 * @author kdyzm
 *
 */
public class JDBCPool1 {
    private static ArrayList<Connection>pool=new ArrayList<Connection>();
    static
    {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            String url="jdbc:mysq://localhost:3306?useUnicode=true&characterEncoding=utf-8";
            for(int i=0;i<5;i++)
            {
                Connection conn=DriverManager.getConnection(url, "root", "5a6f38");
                pool.add(conn);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    
    public Connection getConn()
    {
        synchronized (pool) {
            Connection conn=pool.remove(0);
            System.out.println("还有 "+pool.size()+"个连接");
            return conn;
        }
    }
    
    public static void back(Connection conn)
    {
        System.out.println("还连接:"+conn);
        pool.add(conn);
    }
}

  4.程序员写代码总是习惯性的调用close方法,如果实际调用了close方法,则该连接将会被释放,再也回收不回来了,所以应当使用一种方法拦截close方法的执行,并且替换成自定义的动作。使用代理可以完成这个任务。

  代码示例:

import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.net.URLDecoder;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.LinkedList;
import java.util.Properties;
public class ConnUtils {
    private static LinkedList<Connection> pool = new LinkedList<Connection>();
    static{
        try {
            //声明资源器类 - 
            Properties prop = new Properties();
            //获取这个文件的路径
            URL url = ConnUtils.class.getClassLoader().getResource("jdbc.properties");
            String path = url.getPath();
            //为了防止有中文或是空格
            path = URLDecoder.decode(path,"UTf-8");
            File file = new File(path);
            //加载jdbc.properties这个文件
            prop.load(new FileInputStream(file));
            //获取信息
            String driver = prop.getProperty("driver");
            Class.forName(driver);
            String jdbcurl = prop.getProperty("url");
            String nm = prop.getProperty("name");
            String pwd = prop.getProperty("pwd");
            //创建三个原生的连接,都将它们代理
            String poolSize = prop.getProperty("poolSize");
            int size = Integer.parseInt(poolSize);
            for(int i=0;i<size;i++){
                final Connection con = DriverManager.getConnection(jdbcurl,nm,pwd);
                //对con进行动态代理
                Object proxyedObj = 
                        Proxy.newProxyInstance(ConnUtils.class.getClassLoader(),
                                    new Class[]{Connection.class},
                                    new InvocationHandler() {
                                        public Object invoke(Object proxy, Method method, Object[] args)
                                                throws Throwable {
                                            //是否是close方法
                                            if(method.getName().equals("close")){
                                                synchronized (pool) {
                                                    pool.addLast((Connection) proxy);
                                                    pool.notify();
                                                }
                                                return null;//如果调用的是close则不会调用被代理类的方法。
                                            }
                                            return method.invoke(con, args);
                                        }
                                    });
                //将代理对象放到pool中
                pool.add((Connection) proxyedObj);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static Connection getConn(){
        synchronized (pool) {
            if(pool.size()==0){  //如果连接池中没有连接则进入等待池中等待
                try {
                    pool.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return getConn();
            }else{          //如果连接池中有连接则将连接分配出去。
                Connection con = pool.removeFirst();
                System.err.println("还有几个:"+pool.size());
                return con;
            }
        }
    }
}

  5.动态代理的核心类。

  (1)Proxy类:提供用于创建动态代理类和实例的动态方法,它还是由这些方法创建的所有动态代理类的超类。

  (2)InvocationHandler接口:是代理实例的调用处理程序实现的接口。

  6.代理的任务

  (1)在内存中创建某个接口的子类。

  (2)拦截所有在代理上执行的方法。

三、联系人管理小练习。

  源代码:https://github.com/kdyzm/day15

  

原文地址:https://www.cnblogs.com/kuangdaoyizhimei/p/4678145.html