连接池初涉——自定义连接池
关于自定义连接池引入:
使用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)
*/
}