连接池的原理是基于动态代理重写了close方法。
可以使用动态代理实现,也可以使用装饰者模式来实现。
cn.pro.utils.DBUtils
package cn.pro.utils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ResourceBundle;
public class DBUtils {
private static Connection conn ;
static ResourceBundle rb = ResourceBundle.getBundle("DBconfig");
static{
try {
Class.forName(rb.getString("driver"));
final String jdbcurl = rb.getString("jdbcurl");
final String user = rb.getString("user");
final String passwd =rb.getString("password");
conn = DriverManager.getConnection(jdbcurl, user, passwd);
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public static Connection getConnection(){
return conn;
}
public static void freeConn(ResultSet rs,Statement st,Connection conn){
try {
if(rs!=null){
rs.close();}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally
{
try {
if (st != null) {
st.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}finally{
try {
if (conn != null) {
conn.close();
}
} catch (Exception e3) {
e3.printStackTrace();
}
}
}
}
}
/datasource/src/DBconfig.properties
driver=com.mysql.jdbc.Driver
jdbcurl=jdbc:mysql://localhost:3306/springtest
user=root
password=123456
cn.pro.datasource.MyDatasource
package cn.pro.datasource;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import cn.pro.utils.DBUtils;
//自定义连接池
public class MyDatasource {
//定义一个连接池,用于存放连接
//Collections.synchronizedList(new ArrayList<Connection>()); 把ArrayList转化成线程安全的
private static List<Connection> pool = Collections.synchronizedList(new ArrayList<Connection>());
static {
for(int i=0;i<10;i++){
Connection conn = DBUtils.getConnection();
pool.add(conn);
}
}
public static int getPoolsize(){
return pool.size();
}
public static List<Connection> getPool(){
return pool;
}
public static Connection getConnection(){
final Connection conn = pool.remove(0);
//用完后需要把连接放入连接池 ,需要对此方法增强 重写connction的close方法
Connection proxyconn = (Connection) Proxy.newProxyInstance(conn.getClass().getClassLoader(), conn.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
Object RtValue = null;
//重写close方法,如果是关闭连接 ,则重新键入连接池
if("close".equals(method)){
pool.add(conn);
}else {
RtValue = method.invoke(conn, args);
return RtValue;
}
return RtValue;
}
});
return proxyconn;
}
}
测试类:
cn.pro.UI.TestConn
package cn.pro.UI;
import java.sql.Connection;
import java.sql.SQLException;
import org.junit.Test;
import cn.pro.datasource.MyDatasource;
import cn.pro.utils.DBUtils;
public class TestConn {
@Test
public void test1(){
Connection conn = DBUtils.getConnection();
System.out.println(conn);
}
@Test
public void test2() throws Exception{
int size;
size = MyDatasource.getPoolsize();
System.out.println(size);
for (int i=0;i<size;i++){
System.out.println(MyDatasource.getConnection());
MyDatasource.getConnection().close();
}
int size2;
size2 = MyDatasource.getPoolsize();
System.out.println(size2);
}
}
测试test2报错:
Proxy0 cannot be cast to java.sql.Connection
Connection.getInterfaces() 与数据库驱动有关,5.1.7及后面的版本会报此错误,5.0版本的数据库驱动就不会报错。数据库驱动不同 Connection.getInterfaces() 的结果也就不同,Connection.getInterfaces() 返回的是 Class[] 数组,此数组的第一个元素必须是Connection才能把创建的代理类转为Connection对象,否则就会报:java.lang.ClassCastException。
因为Connection本身就是一个接口,它的字节码符合第二个参数要求,于是把conn.getClass().getInterfaces();改成new Class[]{Connection.class},问题就解决了