【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();
}
}</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> Connection getConn() { </span><span style="color: rgba(0, 0, 255, 1)">synchronized</span><span style="color: rgba(0, 0, 0, 1)"> (pool) { Connection conn</span>=pool.remove(0<span style="color: rgba(0, 0, 0, 1)">); System.out.println(</span>"还有 "+pool.size()+"个连接"<span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> conn; } } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> back(Connection conn) { System.out.println(</span>"还连接:"+<span style="color: rgba(0, 0, 0, 1)">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(); } }</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> Connection getConn(){ </span><span style="color: rgba(0, 0, 255, 1)">synchronized</span><span style="color: rgba(0, 0, 0, 1)"> (pool) { </span><span style="color: rgba(0, 0, 255, 1)">if</span>(pool.size()==0<span style="color: rgba(0, 0, 0, 1)">){ //如果连接池中没有连接则进入等待池中等待 </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> { pool.wait(); } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (InterruptedException e) { e.printStackTrace(); } </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> getConn(); }</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">{ //如果连接池中有连接则将连接分配出去。 Connection con </span>=<span style="color: rgba(0, 0, 0, 1)"> pool.removeFirst(); System.err.println(</span>"还有几个:"+<span style="color: rgba(0, 0, 0, 1)">pool.size()); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> con; } } }
}
5. 动态代理的核心类。
(1)Proxy 类:提供用于创建动态代理类和实例的动态方法,它还是由这些方法创建的所有动态代理类的超类。
(2)InvocationHandler 接口:是代理实例的调用处理程序实现的接口。
6. 代理的任务
(1)在内存中创建某个接口的子类。
(2)拦截所有在代理上执行的方法。
三、联系人管理小练习。
源代码:https://github.com/kdyzm/day15
__EOF__