【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