一種完成數(shù)據(jù)庫(kù)連接池的方法(3)
發(fā)表時(shí)間:2023-08-18 來(lái)源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]DataSourceImpl是一個(gè)實(shí)現(xiàn)了接口javax.sql.DataSource的類,該類維護(hù)著一個(gè)連接池的對(duì)象。由于該類是一個(gè)受保護(hù)的類,因此它暴露給使用者的方法只有接口DataSource中...
DataSourceImpl是一個(gè)實(shí)現(xiàn)了接口javax.sql.DataSource的類,該類維護(hù)著一個(gè)連接池的對(duì)象。由于該類是一個(gè)受保護(hù)的類,因此它暴露給使用者的方法只有接口DataSource中定義的方法,其他的所有方法對(duì)使用者來(lái)說(shuō)都是不可視的。我們先來(lái)關(guān)心用戶可訪問(wèn)的一個(gè)方法getConnection
/**
* @see javax.sql.DataSource#getConnection(String,String)
*/
public Connection getConnection(String user, String password) throws SQLException
{
//首先從連接池中找出空閑的對(duì)象
Connection conn = getFreeConnection(0);
if(conn == null){
//判斷是否超過(guò)最大連接數(shù),如果超過(guò)最大連接數(shù)
//則等待一定時(shí)間查看是否有空閑連接,否則拋出異常告訴用戶無(wú)可用連接
if(getConnectionCount() >= connParam.getMaxConnection())
conn = getFreeConnection(connParam.getWaitTime());
else{//沒(méi)有超過(guò)連接數(shù),重新獲取一個(gè)數(shù)據(jù)庫(kù)的連接
connParam.setUser(user);
connParam.setPassword(password);
Connection conn2 = DriverManager.getConnection(connParam.getUrl(),
user, password);
//代理將要返回的連接對(duì)象
_Connection _conn = new _Connection(conn2,true);
synchronized(conns){
conns.add(_conn);
}
conn = _conn.getConnection();
}
}
return conn;
}
/**
* 從連接池中取一個(gè)空閑的連接
* @param nTimeout如果該參數(shù)值為0則沒(méi)有連接時(shí)只是返回一個(gè)null
* 否則的話等待nTimeout毫秒看是否還有空閑連接,如果沒(méi)有拋出異常
* @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("沒(méi)有可用的數(shù)據(jù)庫(kù)連接");
}
return conn;
}
DataSourceImpl類中實(shí)現(xiàn)getConnection方法的跟正常的數(shù)據(jù)庫(kù)連接池的邏輯是一致的,首先判斷是否有空閑的連接,如果沒(méi)有的話判斷連接數(shù)是否已經(jīng)超過(guò)最大連接數(shù)等等的一些邏輯。但是有一點(diǎn)不同的是通過(guò)DriverManager得到的數(shù)據(jù)庫(kù)連接并不是及時(shí)返回的,而是通過(guò)一個(gè)叫_Connection的類中介一下,然后調(diào)用_Connection.getConnection返回的。如果我們沒(méi)有通過(guò)一個(gè)中介也就是JAVA中的Proxy來(lái)接管要返回的接口對(duì)象,那么我們就沒(méi)有辦法截住Connection.close方法。
終于到了核心所在,我們先來(lái)看看_Connection是如何實(shí)現(xiàn)的,然后再介紹是客戶端調(diào)用Connection.close方法時(shí)走的是怎樣一個(gè)流程,為什么并沒(méi)有真正的關(guān)閉連接。
/**
* 數(shù)據(jù)連接的自封裝,屏蔽了close方法
* @author Liudong
*/
class _Connection implements InvocationHandler
{
private final static String CLOSE_METHOD_NAME = "close";
private Connection conn = null;
//數(shù)據(jù)庫(kù)的忙狀態(tài)
private boolean inUse = false;
//用戶最后一次訪問(wèn)該連接方法的時(shí)間
private long lastAccessTime = System.currentTimeMillis();
_Connection(Connection conn, boolean inUse){
this.conn = conn;
this.inUse = inUse;
}
/**
* Returns the conn.
* @return Connection
*/
public Connection getConnection() {
//返回?cái)?shù)據(jù)庫(kù)連接conn的接管類,以便截住close方法
Connection conn2 = (Connection)Proxy.newProxyInstance(
conn.getClass().getClassLoader(),
conn.getClass().getInterfaces(),this);
return conn2;
}
/**
* 該方法真正的關(guān)閉了數(shù)據(jù)庫(kù)的連接
* @throws SQLException
*/
void close() throws SQLException{
//由于類屬性conn是沒(méi)有被接管的連接,因此一旦調(diào)用close方法后就直接關(guān)閉連接
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;
//判斷是否調(diào)用了close的方法,如果調(diào)用close方法則把連接置為無(wú)用狀態(tài)
if(CLOSE_METHOD_NAME.equals(m.getName()))
setInUse(false);
else
obj = m.invoke(conn, args);
//設(shè)置最后一次訪問(wèn)時(shí)間,以便及時(shí)清除超時(shí)的連接
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;
}
}
一旦使用者調(diào)用所得到連接的close方法,由于用戶的連接對(duì)象是經(jīng)過(guò)接管后的對(duì)象,因此JAVA虛擬機(jī)會(huì)首先調(diào)用_Connection.invoke方法,在該方法中首先判斷是否為close方法,如果不是則將代碼轉(zhuǎn)給真正的沒(méi)有被接管的連接對(duì)象conn。否則的話只是簡(jiǎn)單的將該連接的狀態(tài)設(shè)置為可用。到此您可能就明白了整個(gè)接管的過(guò)程,但是同時(shí)也有一個(gè)疑問(wèn):這樣的話是不是這些已建立的連接就始終沒(méi)有辦法真正關(guān)閉?答案是可以的。我們來(lái)看看ConnectionFactory.unbind方法,該方法首先找到名字對(duì)應(yīng)的連接池對(duì)象,然后關(guān)閉該連接池中的所有連接并刪除掉連接池。在DataSourceImpl類中定義了一個(gè)close方法用來(lái)關(guān)閉所有的連接,詳細(xì)代碼如下:
/**
* 關(guān)閉該連接池中的所有數(shù)據(jù)庫(kù)連接
* @return int 返回被關(guān)閉連接的個(gè)數(shù)
* @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;
}
該方法一一調(diào)用連接池中每個(gè)對(duì)象的close方法,這個(gè)close方法對(duì)應(yīng)的是_Connection中對(duì)close的實(shí)現(xiàn),在_Connection定義中關(guān)閉數(shù)據(jù)庫(kù)連接的時(shí)候是直接調(diào)用沒(méi)有經(jīng)過(guò)接管的對(duì)象的關(guān)閉方法,因此該close方法真正的釋放了數(shù)據(jù)庫(kù)資源。
以上文字只是描述了接口方法的接管,具體一個(gè)實(shí)用的連接池模塊還需要對(duì)空閑連接的監(jiān)控并及時(shí)釋放連接,詳細(xì)的代碼請(qǐng)參照附件。