使用JAVA中的动态代理实现数据库连接池(3)

发表于:2007-07-14来源:作者:点击数: 标签:
使用JAVA中的动态代理实现数据库连接池(3) DataSourceImpl类中实现getConnection方法的跟正常的数据库连接池的逻辑是一致的,首先判断是否有空闲的连接,如果没有的话判断连接数是否已经超过最大连接数等等的一些逻辑。但是有一点不同的是通过DriverManage
使用JAVA中的动态代理实现数据库连接池(3)

DataSourceImpl类中实现getConnection方法的跟正常的数据库连接池的逻辑是一致的,首先判断是否有空闲的连接,如果没有的话判断连接数是否已经超过最大连接数等等的一些逻辑。但是有一点不同的是通过DriverManager得到的数据库连接并不是及时返回的,而是通过一个叫_Connection的类中介一下,然后调用_Connection.getConnection返回的。如果我们没有通过一个中介也就是JAVA中的Proxy来接管要返回的接口对象,那么我们就没有办法截住Connection.close方法。

终于到了核心所在,我们先来看看_Connection是如何实现的,然后再介绍是客户端调用Connection.close方法时走的是怎样一个流程,为什么并没有真正的关闭连接。


/**
* 数据连接的自封装,屏蔽了close方法
* @author Liudong
*/
class _Connection implements InvocationHandler
{
private final static String CLOSE_METHOD_NAME = "close";
private Connection conn = null;
//数据库的忙状态
private boolean inUse = false;
//用户最后一次访问该连接方法的时间
private long lastAccessTime = System.currentTimeMillis();

_Connection(Connection conn, boolean inUse){
this.conn = conn;
this.inUse = inUse;
}
/**
* Returns the conn.
* @return Connection
*/
public Connection getConnection() {
//返回数据库连接conn的接管类,以便截住close方法
Connection conn2 = (Connection)Proxy.newProxyInstance(
conn.getClass().getClassLoader(),
conn.getClass().getInterfaces(),this);
return conn2;
}
/**
* 该方法真正的关闭了数据库的连接
* @throws SQLException
*/
void close() throws SQLException{
//由于类属性conn是没有被接管的连接,因此一旦调用close方法后就直接关闭连接
conn.close();
}
/**
* Returns the inUse.
* @return boolean
*/
public boolean isInUse() {
return inUse;
}

/**
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object)
*/
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable
{
Object obj = null;
//判断是否调用了close的方法,如果调用close方法则把连接置为无用状态
if(CLOSE_METHOD_NAME.equals(m.getName()))
setInUse(false);
else
obj = m.invoke(conn, args);
//设置最后一次访问时间,以便及时清除超时的连接
lastAccessTime = System.currentTimeMillis();
return obj;
}

/**
* Returns the lastAccessTime.
* @return long
*/
public long getLastAccessTime() {
return lastAccessTime;
}

/**
* Sets the inUse.
* @param inUse The inUse to set
*/
public void setInUse(boolean inUse) {
this.inUse = inUse;
}
}

一旦使用者调用所得到连接的close方法,由于用户的连接对象是经过接管后的对象,因此JAVA虚拟机会首先调用_Connection.invoke方法,在该方法中首先判断是否为close方法,如果不是则将代码转给真正的没有被接管的连接对象conn。否则的话只是简单的将该连接的状态设置为可用。到此您可能就明白了整个接管的过程,但是同时也有一个疑问:这样的话是不是这些已建立的连接就始终没有办法真正关闭?答案是可以的。我们来看看ConnectionFactory.unbind方法,该方法首先找到名字对应的连接池对象,然后关闭该连接池中的所有连接并删除掉连接池。在DataSourceImpl类中定义了一个close方法用来关闭所有的连接,详细代码如下:


/**
* 关闭该连接池中的所有数据库连接
* @return int 返回被关闭连接的个数
* @throws SQLException
*/
public int close() throws SQLException
{
int cc = 0;
SQLException excp = null;
Iterator iter = conns.iterator();
while(iter.hasNext()){
try{
((_Connection)iter.next()).close();
cc ++;
}catch(Exception e){
if(e instanceof SQLException)
excp = (SQLException)e;
}
}
if(excp != null)
throw excp;
return cc;
}

该方法一一调用连接池中每个对象的close方法,这个close方法对应的是_Connection中对close的实现,在_Connection定义中关闭数据库连接的时候是直接调用没有经过接管的对象的关闭方法,因此该close方法真正的释放了数据库资源。

以上文字只是描述了接口方法的接管,具体一个实用的连接池模块还需要对空闲连接的监控并及时释放连接,详细的代码请参照附件。

(全文完)

原文转自:http://www.ltesting.net