📄 使用java中的动态代理实现数据库连接池.htm
字号:
conn;<BR> }<BR> /**<BR> *
从连接池中取一个空闲的连接<BR> * @param
nTimeout 如果该参数值为0则没有连接时只是返回一个null<BR> *
否则的话等待nTimeout毫秒看是否还有空闲连接,如果没有抛出异常<BR> * @return
Connection<BR> * @throws SQLException<BR>
*/<BR> protected synchronized Connection
getFreeConnection(long nTimeout) <BR> throws
SQLException<BR> {<BR> Connection conn =
null;<BR> Iterator iter =
conns.iterator();<BR> while(iter.hasNext()){<BR> _Connection
_conn =
(_Connection)iter.next();<BR> if(!_conn.isInUse()){<BR> conn
=
_conn.getConnection();<BR> _conn.setInUse(true); <BR> break;<BR> }<BR> }<BR> if(conn
== null && nTimeout >
0){<BR> //等待nTimeout毫秒以便看是否有空闲连接<BR> try{<BR> Thread.sleep(nTimeout);<BR> }catch(Exception
e){}<BR> conn =
getFreeConnection(0);<BR> if(conn ==
null)<BR> throw new
SQLException("没有可用的数据库连接");<BR> }<BR> return
conn;<BR> }</P>
<P> DataSourceImpl类中实现getConnection方法的跟正常的数据库连接池的逻辑是一致的,首先判断是否有空闲的连接,如果没有的话判断连接数是否已经超过最大连接数等等的一些逻辑。但是有一点不同的是通过DriverManager得到的数据库连接并不是及时返回的,而是通过一个叫_Connection的类中介一下,然后调用_Connection.getConnection返回的。如果我们没有通过一个中介也就是JAVA中的Proxy来接管要返回的接口对象,那么我们就没有办法截住Connection.close方法。</P>
<P> 终于到了核心所在,我们先来看看_Connection是如何实现的,然后再介绍是客户端调用Connection.close方法时走的是怎样一个流程,为什么并没有真正的关闭连接。</P>
<P style="BACKGROUND: #eeeeee">/**<BR> *
数据连接的自封装,屏蔽了close方法<BR> * @author
Liudong<BR> */<BR>class _Connection implements
InvocationHandler<BR>{<BR> private final static
String CLOSE_METHOD_NAME = "close";<BR> private
Connection conn =
null;<BR> //数据库的忙状态<BR> private boolean inUse
= false;<BR> //用户最后一次访问该连接方法的时间<BR> private
long lastAccessTime =
System.currentTimeMillis();<BR> <BR> _Connection(Connection
conn, boolean inUse){<BR> this.conn =
conn;<BR> this.inUse =
inUse;<BR> }<BR> /**<BR> * Returns the
conn.<BR> * @return Connection<BR>
*/<BR> public Connection getConnection()
{<BR> //返回数据库连接conn的接管类,以便截住close方法<BR> Connection
conn2 =
(Connection)Proxy.newProxyInstance(<BR> conn.getClass().getClassLoader(),<BR> conn.getClass().getInterfaces(),this);<BR> return
conn2;<BR> }<BR> /**<BR> *
该方法真正的关闭了数据库的连接<BR> * @throws
SQLException<BR> */<BR> void close() throws
SQLException{<BR> //由于类属性conn是没有被接管的连接,因此一旦调用close方法后就直接关闭连接<BR> conn.close();<BR> }<BR> /**<BR>
* Returns the inUse.<BR> * @return
boolean<BR> */<BR> public boolean isInUse()
{<BR> return
inUse;<BR> }<BR><BR> /**<BR> * @see
java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
java.lang.reflect.Method, java.lang.Object)<BR>
*/<BR> public Object invoke(Object proxy, Method m,
Object[] args) <BR> throws Throwable
<BR> {<BR> Object obj =
null;<BR> //判断是否调用了close的方法,如果调用close方法则把连接置为无用状态<BR> if(CLOSE_METHOD_NAME.equals(m.getName()))<BR> setInUse(false); <BR> else<BR> obj
= m.invoke(conn,
args); <BR> //设置最后一次访问时间,以便及时清除超时的连接<BR> lastAccessTime
= System.currentTimeMillis();<BR> return
obj;<BR> }<BR> <BR> /**<BR> *
Returns the lastAccessTime.<BR> * @return
long<BR> */<BR> public long
getLastAccessTime() {<BR> return
lastAccessTime;<BR> }<BR><BR> /**<BR> *
Sets the inUse.<BR> * @param inUse The inUse to
set<BR> */<BR> public void setInUse(boolean
inUse) {<BR> this.inUse =
inUse;<BR> }<BR>}</P>
<P> 一旦使用者调用所得到连接的close方法,由于用户的连接对象是经过接管后的对象,因此JAVA虚拟机会首先调用_Connection.invoke方法,在该方法中首先判断是否为close方法,如果不是则将代码转给真正的没有被接管的连接对象conn。否则的话只是简单的将该连接的状态设置为可用。到此您可能就明白了整个接管的过程,但是同时也有一个疑问:这样的话是不是这些已建立的连接就始终没有办法真正关闭?答案是可以的。我们来看看ConnectionFactory.unbind方法,该方法首先找到名字对应的连接池对象,然后关闭该连接池中的所有连接并删除掉连接池。在DataSourceImpl类中定义了一个close方法用来关闭所有的连接,详细代码如下:</P>
<P style="BACKGROUND: #eeeeee"> /**<BR> *
关闭该连接池中的所有数据库连接<BR> * @return int
返回被关闭连接的个数<BR> * @throws SQLException<BR>
*/<BR> public int close() throws
SQLException<BR> {<BR> int cc =
0;<BR> SQLException excp =
null;<BR> Iterator iter =
conns.iterator();<BR> while(iter.hasNext()){<BR> try{<BR> ((_Connection)iter.next()).close();<BR> cc
++;<BR> }catch(Exception
e){<BR> if(e instanceof
SQLException)<BR> excp =
(SQLException)e;<BR> }<BR> }<BR> if(excp
!= null)<BR> throw
excp;<BR> return cc;<BR> }</P>
<P> 该方法一一调用连接池中每个对象的close方法,这个close方法对应的是_Connection中对close的实现,在_Connection定义中关闭数据库连接的时候是直接调用没有经过接管的对象的关闭方法,因此该close方法真正的释放了数据库资源。</P>
<P> 以上文字只是描述了接口方法的接管,具体一个实用的连接池模块还需要对空闲连接的监控并及时释放连接。</P></TD></TR></TBODY></TABLE></TD></TR>
<TR>
<TD width=10 height=11><IMG height=11
src="使用JAVA中的动态代理实现数据库连接池.files/u_16.gif" width=10></TD>
<TD style="BORDER-BOTTOM: #e8e8e8 1px solid" width=695
bgColor=#f9f9f9 height=11><IMG height=1 src="" width=1></TD>
<TD width=10 height=11><IMG height=11
src="使用JAVA中的动态代理实现数据库连接池.files/u_17.gif"
width=10></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR>
<DIV align=right>页面功能 【<A
href="javascript:window.external.AddFavorite(location.href,document.title+'--www.JavaFan.NET');">加入收藏</A>】
【<A
onclick="window.open(this.href,'','top=180,left=240,width=342,height=326,scrollbars=yes,resizable=no');return false;"
href="http://www.javafan.net/sendarticle.jsp?title=使用JAVA中的动态代理实现数据库连接池&URL=20050124143602938">推荐给朋友</A>】
【字体:<A class=black href="javascript:fontZoom(15)">大</A> <A
class=black href="javascript:fontZoom(13)">中</A> <A class=black
href="javascript:fontZoom(12)">小</A>】 【<A class=black
href="javascript:window.close()">关闭</A>】 </DIV></TD></TR>
<TR>
<TD vAlign=top><IMG height=12 src="使用JAVA中的动态代理实现数据库连接池.files/u_06.gif"
width=751></TD></TR></TBODY></TABLE><BR>
<TABLE cellSpacing=0 cellPadding=0 width=750 align=center border=0>
<TBODY>
<TR>
<TD bgColor=#cacaca height=1></TD></TR>
<TR>
<TD vAlign=center align=middle height=30>Copyright © 2003 - 2005
JavaFan.NET All Rights Reserved</TD></TR></TBODY></TABLE></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -