本人小白,在学习数据库连接池实现时,为了确保调用者在获取了Connection后,不要直接close。使用动态代理方式创建代理对象,重新处理close函数。在执行中发现,用户在释放了代理对象以后,连接在add进list以后,变回了非代理的ConnectionImpl对象,导致再次取出时,代理作用丢失,请各位帮忙分析一下是怎么回事?
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.util.LinkedList;
public class DBPool {
private int maxSize;//最大连接数
private int initSize;//初始化连接数
private int cur = 0;//已经使用的连接数
private LinkedList<Connection> pool = new LinkedList<Connection>();
//初始化连接池
public DBPool(int initSize, int maxSize) {
this.maxSize = maxSize;
this.initSize = initSize;
initPool(initSize);
}
@Override
public String toString() {
return "DBPool [pool=" + pool + "]";
}
//得到正在使用的连接个数
public int getCur() {
return cur;
}
//连接池中连接个数
public int getPoolSize() {
return pool.size();
}
//初始化连接池方法
public void initPool(int initSize) {
for (int i = 0; i < initSize; i++) {
pool.add(createConnection());
}
}
//提供给调用者一个连接
public Connection getPoolConnection() {
if (pool.size() > 0) {
cur++;
return pool.removeFirst();
} else if (cur >= maxSize) {
System.out.println("连接已达到最大值");
return null;
} else {
cur++;
return createConnection();
}
}
public Connection createConnection() {
//使用工具类获取数据库连接
Connection con = MysqlUtils.getConnection();
//创建代理对象,代理con的close方法,如果连接池不满,就放入连接池,如果连接池满,则调用原close方法。
Connection proxy = (Connection) Proxy.newProxyInstance(con.getClass().getClassLoader(),
new Class[]{Connection.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
//判断是否close方法
if ("close".equals(method.getName())) {
// 如果连接池里面连接数<initSize,则直接加入pool
if (pool.size() < initSize) {
cur--;
pool.addLast(con);
} else {// 如果连接池>initSize,则直接关闭。
cur--;
result = method.invoke(con, args);
}
//如果不是close方法,则直接调用原函数
} else {
result = method.invoke(con, args);
}
return result;
}
});
return proxy;
}
}
测试方法如下:
@Test
public void testPool() throws SQLException {
//初始化连接池,池大小1,最大连接数2
DBPool pool=new DBPool(1, 2);
//获取2个连接
Connection[] conn=new Connection[2];
for(int i=0;i<conn.length;i++) {
conn[i]=pool.getPoolConnection();
}
//释放两个连接,按照代理处理结果,应该是一个放回连接池,一个关闭。
for(int i=0;i<conn.length;i++) {
if(conn[i]!=null) {
conn[i].close();
conn[i]=null;
}
}
//再次获取2个连接
for(int i=0;i<conn.length;i++) {
conn[i]=pool.getPoolConnection();
}
//再次释放2个连接
for(int i=0;i<conn.length;i++) {
if(conn[i]!=null) {
conn[i].close();
conn[i]=null;
}
}
//预期结果应该是当前使用连接cur=0,池中连接数为1.
System.out.println("当前使用的连接数:"+pool.getCur()+",当前连接池中连接数:"+pool.getPoolSize());
}
执行后结果如下:
当前使用的连接数:1,当前连接池中连接数:1
当前结果与预期结果不一致。
经过调试发现:
1、初始化完毕时,链表内容为:
pool LinkedList<E> (id=299)
[0] $Proxy4 (id=313)
2、第一次获取getPoolConnection(),conn数组内容为:
conn Connection[2] (id=322)
[0] $Proxy4 (id=307)
[1] $Proxy4 (id=331)
3、第一次放回池中,pool链表内容为和conn数组分别为:
pool LinkedList<E> (id=293)
[0] ConnectionImpl (id=316)
conn Connection[2] (id=322)
[0] null
[1] null
4、 第二次获取getPoolConnection(),conn数组内容为:
conn Connection[2] (id=322)
[0] ConnectionImpl (id=316)
[1] $Proxy4 (id=491)
同时,在调试时也确实在第二次close两个连接时,conn[0].close没有执行代理程序。
不好意思,没有C币,请大家多包涵。