论坛登陆 用户名:  密 码:
设为首页  加入收藏
08年北京名校秋季招生
名牌院校免试入学宽进严出,突破考分限制,名校与你零距离,以下院校按报名先后顺序录取,24小时网上报名覆盖全国
  您现在的位置: 中国教育招生在线 >> IT >> JAVA认证 >> IT正文
使用JAVA中的动态代理实现数据库连接池(2)
 作者:佚名     2007-3-14 17:20:20        来源:不详  浏览次数:

 

 

 

 

 

 

 

 

使用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


(全文完)


责任编辑:lss
  相关新闻
WebWork拦截器(Interceptor)使用方法
Java模板引擎Velocity基本语法
【JAVA基础】JDBC连接DB2数据库详解
为什么要学习Java,成为Java程序员
Java基础-漫谈EJB在Java中的应用
使用AppFuse框架进行开发的总结
快速上手Spring--4.安装和使用IDE
漫谈EJB在Java中的应用(二)
Oracle诊断工具-RDA使用概述
J2EE技术-漫谈EJB在Java中的应用
为什么要学习Java,成为Java程序员
Oracle诊断工具-RDA使用概述
Java基础-漫谈EJB在Java中的应用
在Struts框架下使用时间类型
漫谈EJB在Java中的应用(二)
快速上手Spring--4.安装和使用IDE
运用Jakarta Struts的七大实战心法
使用Spring更好地处理Struts动作
使用Buffalo集成Spring写的一个登录实例
通过Java Swing 看透MVC设计模式
  评论
现在有100人对本文发表评论
查看所有评论
 
推  荐
 
100本成功必读热销书
热门招生
  北京文理研修学院   前进大学
  北京明园大学   北京建设大学
  北京邮电大学世纪学院   北方工商管理学院
  联想软件定向委培班   香港数码动画学院
  青年企业管理研修学院   北京华夏管理学院
热门培训
网络化办公专家培训认证 电子科技大学软件学院
软件测试工程师培训认证 北大青鸟十大授权培训
IT硬件工程师培训认证班 北京环球雅思荷兰预科
JAVA开发工程师培训 潜能时代IT服务管理培训
网络信息化工程师培训 清华大学继续教育学院
论坛精选
 
有些细节是男人也该注意的风度!最容易读错的字
某强人手机里保存的30条短信 中国十大高薪职业
最感人的十大韩剧经典台词 嫁给工程师的N个理由
爆强!只有一句话的鬼故事 转贴教你如何做妖精
 女人一定要記住的話 女人最好别嫁给最爱的男人
城市联盟
 大连 上海 天津 广州 西安 深圳  天津  青岛  大连  福州  沈阳  青海  连云港  南京  吉林  厦门  威海  辽宁  呼和浩特
Copyright © 2006   www.edu999.com   All rights reserved. 中国教育招生在线  版权所有
北京市通信管理局[2004]字第552号函    京ICP证040442号