中国IT动力,最新最全的IT技术教程
最新100篇 | 推荐100篇 | 专题100篇 | 排行榜 | 搜索 | 在线API文档 | 网通镜像
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 硬件维护 | 未整理篇 | 站长教程
ASP JS PHP工程 ASP.NET 网站建设 UML J2EESUN .NET VC VB VFP 网络维护 数据库 DB2 SQL2000 Oracle Mysql
服务器 Win2000 Office C DreamWeaver FireWorks Flash PhotoShop 上网宝典 CorelDraw 协议大全 网络安全 微软认证
硬件维护  CPU  主板  硬盘  内存  显卡  显示器  键盘鼠标  声卡音箱  打印机  机箱电源  BIOS  网卡  C#  Java  Delphi  vs.net2005
  当前位置:> 程序开发 > 编程语言 > Java > 数据库
使用JAVA中的动态代理实现数据库连接池(2)
作者:佚名 时间:2006-09-26 11:22 出处:天新网 责编:月夜寒箫
              摘要:使用JAVA中的动态代理实现数据库连接池(2)

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

ConnectionFactory主要提供了用户将将连接池绑定到一个具体的名称上以及取消绑定的操作。使用者只需要关心这两个类即可使用数据库连接池的功能。下面我们给出一段如何使用连接池的代码:



String name = "pool";

String driver = " sun.JDBC.odbc.JdbcOdbcDriver ";

String url = "jdbc:odbc:datasource";

ConnectionParam param = new ConnectionParam(driver,url,null,null);

param.setMinConnection(1);

param.setMaxConnection(5);

param.setTimeoutValue(20000);

ConnectionFactory.bind(name, param);

System.out.println("bind datasource ok.");

//以上代码是用来登记一个连接池对象,该操作可以在程序初始化只做一次即可

//以下开始就是使用者真正需要写的代码

DataSource ds = ConnectionFactory.lookup(name);

try{

for(int i=0;i<10;i++){

Connection conn = ds.getConnection();

try{

testSQL(conn, sql);

}finally{

try{

conn.close();

}catch(Exception e){}

}

}

}catch(Exception e){

e.printStackTrace();

}finally{

ConnectionFactory.unbind(name);

System.out.println("unbind datasource ok.");

System.exit(0);

}


从使用者的示例代码就可以看出,我们已经解决了常规连接池产生的两个问题。但是我们最最关心的是如何解决接管close方法的办法。接管工作主要在ConnectionFactory中的两句代码:


source = new DataSourceImpl(param);

source.initConnection();


DataSourceImpl是一个实现了接口javax.sql.DataSource的类,该类维护着一个连接池的对象。由于该类是一个受保护的类,因此它暴露给使用者的方法只有接口DataSource中定义的方法,其他的所有方法对使用者来说都是不可视的。我们先来关心用户可访问的一个方法getConnection



/**

* @see javax.sql.DataSource#getConnection(String,String)

*/

public Connection getConnection(String user, String password) throws SQLException

{

//首先从连接池中找出空闲的对象

Connection conn = getFreeConnection(0);

if(conn == null){

//判断是否超过最大连接数,如果超过最大连接数

//则等待一定时间查看是否有空闲连接,否则抛出异常告诉用户无可用连接

if(getConnectionCount() >= connParam.getMaxConnection())

conn = getFreeConnection(connParam.getWaitTime());

else{//没有超过连接数,重新获取一个数据库的连接

connParam.setUser(user);

connParam.setPassword(password);

Connection conn2 = DriverManager.getConnection(connParam.getUrl(),

user, password);

//代理将要返回的连接对象

_Connection _conn = new _Connection(conn2,true);

synchronized(conns){

conns.add(_conn);

}

conn = _conn.getConnection();

}

}

return conn;

}

/**

* 从连接池中取一个空闲的连接

* @param nTimeout 如果该参数值为0则没有连接时只是返回一个null

* 否则的话等待nTimeout毫秒看是否还有空闲连接,如果没有抛出异常

* @return Connection

* @throws SQLException

*/

protected synchronized Connection getFreeConnection(long nTimeout)

throws SQLException

{

Connection conn = null;

Iterator iter = conns.iterator();

while(iter.hasNext()){

_Connection _conn = (_Connection)iter.next();

if(!_conn.isInUse()){

conn = _conn.getConnection();

_conn.setInUse(true);

break;

}

}

if(conn == null && nTimeout > 0){

//等待nTimeout毫秒以便看是否有空闲连接

try{

Thread.sleep(nTimeout);

}catch(Exception e){}

conn = getFreeConnection(0);

if(conn == null)

throw new SQLException("没有可用的数据库连接");

}

return conn;

}


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://java.sun.com

JAVA的官方网站


· 关于作者:


刘冬,珠海市创我科技发展有限公司软件工程师,主要从事J2EE方面的开发。电子邮件:winter.lau@163.com


(全文完)

关闭本页
 
首页 | 投资与合作 | 服务条款 | 隐私政策 | 收藏本站 | 设为首页 | 新用户注册 | 免责声明 | 使用帮助
Copyright ©2005-2008 chinaitpower.com All rights reserved. www.chinaitpower.com 版权所有