关于自定义连接池引入:
使用JDBC进行数据库连接的时候,我们每次一个insert操作等等之后,就会把Connection关闭掉;但是这样是很低效的,我们的Connection在一次使用之后,就面临被关闭,加载和关闭太浪费资源了,而且这样放任外部自由的修改,不加限制的创建Connection对象,有可以会超过数据库能够承受的最大连接数,数据库容易崩溃!
我们的解决此类问题的办法:
创建一个连接池,外部要和数据库进行连接,必须通过连接池进行连接,连接池一次初始会初始定量的连接对象,也会限制程序能够 操作的最大的连接数量。如果超过了此最大连接数,就会抛出异常或者让用户进行等待。当用户不使用连接之后,就将连接放回连接池中。
关于静态代理和动态代理的引入:
我们发现,在程序使用完了从连接池中获得的连接对象之后,必须要使用我们连接池内部提供的release方法进行连接的释放(即将其放回连接 池中),但是如果外部程序使用完了连接之后,直接close()了怎么办呢?
试想外部程序直接close了之后会发生的结果:
我们先构建一个场景:我们的初始化的连接数量为5,最大连接数量为10;并且我们当前正处于JDBC操作的峰值上,还有很多等待JDBC操作的程序;
如果外部程序使用完了我们的连接对象,直接close的话,就会造成我们 当前的连接对象不能放回连接池中,但是连接池记录的当前的连接对象的数量依旧是10,但是我们当前已经close了一个连接,所以实际池中只有9个连接对象了;如果此类情况一直发生,就会造成我们连接池中的连接数量最后为0,但是连接池自身记录的连接数一直处于峰值状况,所有的 连接都是处于不可用的状态!那么这个连接池就算是废了!
首先引入的是静态代理:
关于代理之前,我们要先复习一个概念;
在JDBC中,JDBC所有的实现类都是数据库提供商编写的,java只提供了 一个规范,一堆接口;数据库提供商面向接口编程的;那么在我们使用JDBC操作的时候,我们开发者也是通过java.sql.*中的接口,作为引用操作的各个数据库提供商写出的实现类的对象;
代理的概念就是:以前我们是直接使用Connection接口,现在:我们在Connection接口之上,再添加一个我们的Connection操作类,implements Connection,然后,将其中我们需要修改的方法进行重写,其它的进行原样调用。
静态代理就是完全重写一个接口,将其中所有的方法都要修改,不修改的就必须通过手动修改的方式和下层进行相连,当接口中方法很多的时候,这样很低效;
动态代理:首先要运用反射中的Proxy类,来创建动态代理对象;通过反射的操作来降低我们代码中的重复操作(或低效代码)。
package pool; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.LinkedList; /** * 自定义连接池 * @author mzy */ public class MyPool { private static String url="jdbc:mysql://localhost:3306/test"; private static String user="root"; private static String password="123456"; private static String driverClass="com.mysql.jdbc.Driver"; private static LinkedList<Connection> pool = new LinkedList<Connection>(); // 连接池的初始化连接数 private int initCount = 5; // 连接池的最大连接数 private int maxCount = 10; // 用于记录当前连接的数量 private int currentCount = 0; public static LinkedList<Connection> getPool() { return pool; } public static void setPool(LinkedList<Connection> pool) { MyPool.pool = pool; } static { // 注册驱动 try { Class.forName(driverClass); } catch(ClassNotFoundException e) { e.printStackTrace(); } } public MyPool() { for(int i=1; i<=initCount; i++) { pool.addLast(createConnection()); currentCount++; } } private Connection createConnection() { final Connection conn; try { conn = DriverManager.getConnection(url, user, password); // 1) 使用静态代理类的方式去创建Connection的代理类 // MyConnection myConn = new MyConnection(this, conn); // 2) 使用动态代理类方式去创建Connection的代理类 /** * 使用到jdk的api: Proxy类 * 用于创建动态代理类对象: * static Object newProxyInstance( * ClassLoader loader, * Class<?>[] interfaces, * InvocationHandler h * ) * 参数一:类加载器。 * 参数二: 代理类实现的接口列表 * 参数三: 接口 InvocationHandler: 代理类的调用处理程序的接口。(代理完代理对象之后,对其中的方法如何处理???) * Object invoke( * Object proxy, 代理类对象 * Method method, 代理类对象调用的方法。 * Object[] args 调用代理类对象方法时传入的参数列表 * ) */ Connection myConn = (Connection)Proxy.newProxyInstance(MyPool.class.getClassLoader(), new Class[]{Connection.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 1) 重写需要重写的方法。close方法 // 获取当前调用的方法的方法名称 String methodName = method.getName(); if("close".equals(methodName)) { MyPool.getPool().addLast(conn); return null; } else { // 2) 调用回原来的方法,获取返回值 Object value = method.invoke(conn, args); return value; } } }); return myConn; } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException(e); } } /** * 对外提供给java程序一个获取连接的方法 */ public Connection getConnection() { // 1) 当并发连接数小于等于初始化连接数量的时候,才从池中取出 if(pool.size()>0) { System.out.println("==== 初始化的连接 ==== "); return pool.removeFirst(); // 取出并删除 } // 2) 当并发数量超过初始化连接数据的时候,程序自行获取连接对象,但是 // 一旦超过了最大连接数量的时候,不能获取 if(currentCount<maxCount) { System.out.println("==== 新建的连接 ==== "); currentCount++; return createConnection(); } // 3) 当超过了最大连接数时,不能再获取连接了。 throw new RuntimeException("已经超过了最大连接数"); } /** * 对外提供释放连接对象的方法 */ public void releaseConnection(Connection conn) { // 放回连接池容器中 pool.addLast(conn); } }
测试方法:
package pool; import java.sql.Connection; public class TestPool { public static void main(String[] args) { // 1) 构造连接池对象 MyPool myPool = new MyPool(); // 初始化连接 // 模拟用户并发获取连接 for(int i=0; i<11; i++) { Connection conn = myPool.getConnection(); // 如果获取的连接数小于初始化连接数,就不用真的连接数据库 System.out.println("第"+(i+1)+"个"+conn); if(i == 3) { // 模拟用户释放连接,把连接放回连接池中 myPool.releaseConnection(conn); } } /* * 我们当前的自定义pool已经完成了; * 但是仍然有问题; * 我们当前的连接池是没有加锁的,如果多个程序同时来拿的话, * 是线程不安全的; * 其次,当连接数量达到max的时候,当再有程序想获得Connection * 我们直接是抛出一个runtimeException; * 不合理,让用户进行等待sleep才是一个合理的操作! */ } /** * 所以我们通常使用别人写好的连接池工具: * 通常使用DBCP(DataBase Connection Pool) * 是Apache组织编写的产品 * * C3P0 * 是开源框架使用的(hibernate内置默认的连接池工具C3P0) */ }