亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

使用JAVA中的動態代理實現數據庫連接池

2019-11-17 04:33:54
字體:
來源:轉載
供稿:網友

數據庫連接池在編寫應用服務是經常需要用到的模塊,太過頻繁的連接數據庫對服務性能來講是一個瓶頸,使用緩沖池技術可以來消除這個瓶頸。我們可以在互聯網上找到很多關于數據庫連接池的源程序,但是都發現這樣一個共同的問題:這些連接池的實現方法都不同程度地增加了與使用者之間的耦合度。很多的連接池都要求用戶通過其規定的方法獲取數據庫的連接,這一點我們可以理解,畢竟目前所有的應用服務器取數據庫連接的方式都是這種方式實現的。但是另外一個共同的問題是,它們同時不允許使用者顯式的調用Connection.close()方法,而需要用其規定的一個方法來關閉連接。這種做法有兩個缺點:

第一:改變了用戶使用習慣,增加了用戶的使用難度。

首先我們來看看一個正常的數據庫操作過程:

int executeSQL(String sql) throws SQLException

{

Connection conn = getConnection(); //通過某種方式獲取數據庫連接

PReparedStatement ps = null;

int res = 0;

try{

ps = conn.prepareStatement(sql);

res = ps.executeUpdate();

}finally{

try{

ps.close();

}catch(Exception e){}

try{

conn.close();//

}catch(Exception e){}

}

return res;

}

使用者在用完數據庫連接后通常是直接調用連接的方法close來釋放數據庫資源,如果用我們前面提到的連接池的實現方法,那語句conn.close()將被某些特定的語句所替代。

第二:使連接池無法對之中的所有連接進行獨占控制。由于連接池不允許用戶直接調用連接的close方法,一旦使用者在使用的過程中由于習慣問題直接關閉了數據庫連接,那么連接池將無法正常維護所有連接的狀態,考慮連接池和應用由不同開發人員實現時這種問題更容易出現。

綜合上面提到的兩個問題,我們來討論一下如何解決這兩個要命的問題。

首先我們先設身處地的考慮一下用戶是想怎么樣來使用這個數據庫連接池的。用戶可以通過特定的方法來獲取數據庫的連接,同時這個連接的類型應該是標準的java.sql.Connection。用戶在獲取到這個數據庫連接后可以對這個連接進行任意的操作,包括關閉連接等。

通過對用戶使用的描述,怎樣可以接管Connection.close方法就成了我們這篇文章的主題。

為了接管數據庫連接的close方法,我們應該有一種類似于鉤子的機制。例如在Windows編程中我們可以利用Hook API來實現對某個Windows API的接管。在JAVA中同樣也有這樣一個機制。JAVA提供了一個Proxy類和一個InvocationHandler,這兩個類都在java.lang.reflect包中。我們先來看看SUN公司提供的文檔是怎么描述這兩個類的。

public interface InvocationHandler

InvocationHandler is the interface implemented by the invocation handler of a proxy instance.

Each proxy instance has an associated invocation handler.

When a method is invoked on a proxy instance,

the method invocation is encoded and dispatched to the invoke method of its invocation handler.

SUN的API文檔中關于Proxy的描述很多,這里就不羅列出來。通過文檔對接口InvocationHandler的描述我們可以看到當調用一個Proxy實例的方法時會觸發Invocationhanlder的invoke方法。從JAVA的文檔中我們也同時了解到這種動態代理機制只能接管接口的方法,而對一般的類無效,考慮到java.sql.Connection本身也是一個接口由此就找到了解決如何接管close方法的出路。

首先,我們先定義一個數據庫連接池參數的類,定義了數據庫的JDBC驅動程序類名,連接的URL以及用戶名口令等等一些信息,該類是用于初始化連接池的參數,具體定義如下:

public class ConnectionParam implements Serializable

{

private String driver; //數據庫驅動程序

private String url; //數據連接的URL

private String user; //數據庫用戶名

private String passWord; //數據庫密碼

private int minConnection = 0; //初始化連接數

private int maxConnection = 50; //最大連接數

private long timeoutValue = 600000;//連接的最大空閑時間

private long waitTime = 30000; //取連接的時候如果沒有可用連接最大的等待時間

其次是連接池的工廠類ConnectionFactory,通過該類來將一個連接池對象與一個名稱對應起來,使用者通過該名稱就可以獲取指定的連接池對象,具體代碼如下:

/**

* 連接池類廠,該類常用來保存多個數據源名稱合數據庫連接池對應的哈希

* @author liusoft

*/

public class ConnectionFactory

{

//該哈希表用來保存數據源名和連接池對象的關系表

static Hashtable connectionPools = null;

static{

connectionPools = new Hashtable(2,0.75F);

}

/**

* 從連接池工廠中獲取指定名稱對應的連接池對象

* @param dataSource 連接池對象對應的名稱

* @return DataSource 返回名稱對應的連接池對象

* @throws NameNotFoundException 無法找到指定的連接池

*/

public static DataSource lookup(String dataSource)

throws NameNotFoundException

{

Object ds = null;

ds = connectionPools.get(dataSource);

if(ds == null || !(ds instanceof DataSource))

throw new NameNotFoundException(dataSource);

return (DataSource)ds;

}

/**

* 將指定的名字和數據庫連接配置綁定在一起并初始化數據庫連接池

* @param name 對應連接池的名稱

* @param param 連接池的配置參數,具體請見類ConnectionParam

* @return DataSource 如果綁定成功后返回連接池對象

* @throws NameAlreadyBoundException 一定名字name已經綁定則拋出該異常

* @throws ClassNotFoundException 無法找到連接池的配置中的驅動程序類

* @throws IllegalaccessException 連接池配置中的驅動程序類有誤

* @throws InstantiationException 無法實例化驅動程序類

* @throws SQLException 無法正常連接指定的數據庫

*/

public static DataSource bind(String name, ConnectionParam param)

throws NameAlreadyBoundException,ClassNotFoundException,

IllegalAccessException,InstantiationException,SQLException

{

DataSourceImpl source = null;

try{

lookup(name);

throw new NameAlreadyBoundException(name);

}catch(NameNotFoundException e){

source = new DataSourceImpl(param);

source.initConnection();

connectionPools.put(name, source);

}

return source;

}

/**

* 重新綁定數據庫連接池

* @param name 對應連接池的名稱

* @param param 連接池的配置參數,具體請見類ConnectionParam

* @return DataSource 如果綁定成功后返回連接池對象

* @throws NameAlreadyBoundException 一定名字name已經綁定則拋出該異常

* @throws ClassNotFoundException 無法找到連接池的配置中的驅動程序類

* @throws IllegalAccessException 連接池配置中的驅動程序類有誤

* @throws InstantiationException 無法實例化驅動程序類

* @throws SQLException 無法正常連接指定的數據庫

*/

public static DataSource rebind(String name, ConnectionParam param)

throws NameAlreadyBoundException,ClassNotFoundException,

IllegalAccessException,InstantiationException,SQLException

{

try{

unbind(name);

}catch(Exception e){}

return bind(name, param);

}

/**

* 刪除一個數據庫連接池對象

* @param name

* @throws NameNotFoundException

*/

public static void unbind(String name) throws NameNotFoundException

{

DataSource dataSource = lookup(name);

if(dataSource instanceof DataSourceImpl){

DataSourceImpl dsi = (DataSourceImpl)dataSource;

try{

dsi.stop();

dsi.close();

}catch(Exception e){

}finally{

dsi = null;

}

}

connectionPools.remove(name);

}

}

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方法真正的釋放了數據庫資源。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
精品成人国产在线观看男人呻吟| 日韩中文在线观看| 日韩中文字幕在线播放| 色婷婷成人综合| 最新国产成人av网站网址麻豆| 欧美激情国产精品| 97视频色精品| 欧美性开放视频| 亚洲欧洲高清在线| 欧美性猛交xxxx黑人猛交| 欧美另类精品xxxx孕妇| 成人午夜在线影院| 国产在线不卡精品| 欧美成人激情视频免费观看| 国产精品日韩在线播放| 久久久精品国产亚洲| 亚洲精品综合精品自拍| 午夜精品久久久久久久白皮肤| 国产精自产拍久久久久久蜜| 国产黑人绿帽在线第一区| 97在线视频免费看| 欧美极品xxxx| 中文字幕av一区中文字幕天堂| 亚洲欧美日韩中文在线制服| 国产精品视频免费观看www| 69久久夜色精品国产7777| 91情侣偷在线精品国产| 国产精品va在线播放我和闺蜜| 久久久久久久久久久亚洲| 日本欧美在线视频| 大胆欧美人体视频| 国模精品一区二区三区色天香| 精品久久久久久久大神国产| 91国自产精品中文字幕亚洲| 久热精品视频在线观看一区| 久久影视电视剧免费网站| 日本中文字幕久久看| 狠狠色狠狠色综合日日五| 91久久在线观看| 国产成人精品a视频一区www| 啊v视频在线一区二区三区| 日韩在线免费视频| 国产91在线播放精品91| 国产精品久久久久高潮| 成人中文字幕+乱码+中文字幕| 日本高清不卡的在线| 91色视频在线观看| 欧美在线视频a| zzjj国产精品一区二区| 久久6免费高清热精品| 91国内精品久久| 久久久久久久久久久久av| 久久成人人人人精品欧| 精品美女国产在线| 国产精品成人aaaaa网站| 国产精品欧美久久久| 2019亚洲日韩新视频| 欧美性开放视频| 亚洲国产私拍精品国模在线观看| 中文字幕日韩av综合精品| 欧美一级bbbbb性bbbb喷潮片| 欧美激情综合色综合啪啪五月| 亚洲成人动漫在线播放| 亚洲国产日韩精品在线| 日韩在线视频网站| 色伦专区97中文字幕| 亚洲国产高清福利视频| 色琪琪综合男人的天堂aⅴ视频| 国产视频欧美视频| 欧美精品手机在线| 亚洲欧洲国产伦综合| 91视频国产高清| 欧美成人在线免费| 日韩电影在线观看永久视频免费网站| 欧美一区二区三区精品电影| 日韩av在线一区二区| 亚洲欧美国产精品va在线观看| 欧美裸体xxxx极品少妇| 久久亚洲精品毛片| 精品视频久久久| 综合网中文字幕| 91精品免费看| 亚洲无av在线中文字幕| 欧美在线视频一区| 国产91色在线|免| 国产精品久久中文| 亚洲精品自拍第一页| 色悠悠久久88| 91成人精品网站| 不卡伊人av在线播放| 日韩一区视频在线| 欧美极品欧美精品欧美视频| 欧美午夜激情小视频| 丝袜一区二区三区| 这里只有精品视频在线| 亚洲国产91色在线| 久久视频精品在线| 免费av一区二区| 亚洲黄色片网站| 亚洲xxx自由成熟| 亚洲四色影视在线观看| 亚洲大尺度美女在线| 91色在线观看| 欧美一区二区色| 成人免费视频网址| 国产精彩精品视频| 国产成人精品最新| 色哟哟亚洲精品一区二区| 亚洲男女自偷自拍图片另类| 亚洲精品国产精品自产a区红杏吧| www.国产精品一二区| 成人h片在线播放免费网站| 亚洲欧美成人一区二区在线电影| 热99精品里视频精品| 奇门遁甲1982国语版免费观看高清| 中文字幕亚洲一区二区三区| 综合网日日天干夜夜久久| 日韩av不卡电影| 色婷婷av一区二区三区久久| 成人黄色影片在线| 欧美一性一乱一交一视频| 欧美日韩中文字幕| 国产日韩欧美在线视频观看| 日韩在线观看免费高清完整版| 国内精品久久久久影院优| 97欧美精品一区二区三区| 国产免费一区视频观看免费| 亚洲欧洲自拍偷拍| 日韩网站免费观看高清| 亚洲视频国产视频| 亚洲第一精品夜夜躁人人躁| 日韩久久免费电影| 久久成人精品视频| 国产一区二区丝袜高跟鞋图片| 亚洲系列中文字幕| 欧美成人剧情片在线观看| 国产欧美日韩免费看aⅴ视频| 亚洲小视频在线观看| 欧美日韩午夜视频在线观看| 亚洲欧美一区二区三区四区| 亚洲跨种族黑人xxx| 欧美性色视频在线| 国产欧美 在线欧美| 日韩一区二区欧美| 日韩在线免费视频| 91久久国产精品| 中文字幕亚洲第一| 欧美性做爰毛片| 中文字幕亚洲一区二区三区| 伊人伊成久久人综合网站| 欧美专区中文字幕| 欧美激情视频网站| 国产精品久久久久免费a∨| 欧美久久久精品| 成人福利视频网| 狠狠爱在线视频一区| 国产免费一区二区三区在线观看| 亚洲久久久久久久久久| 亚洲午夜未满十八勿入免费观看全集| 亚洲视频在线免费看| 国产精品欧美风情| 日韩av在线影院| 97在线视频国产| 亚洲精品不卡在线|