2018-07-24期 Java動態代理實現數據庫連接池
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.logging.Logger;
import javax.sql.DataSource;
/**
* 使用Java 動態代理實現數據庫連接池Pool
*
* @author songjq
*
*/
public class DataSourcePool implements DataSource {
// 驅動程序名
static String driver = "com.mysql.jdbc.Driver";
// URL指向要訪問的數據庫名mydata
static String url = "jdbc:mysql://hadoop-server03:3306/mysql";
// MySQL配置時的用戶名
static String user = "root";
// MySQL配置時的密碼
static String password = "user#0989";
// 定義一個鏈表保存20個數據庫連接
static LinkedList<Connection> pool = new LinkedList<>();
// 靜態代碼塊
static {
// 加載驅動程序
try {
Class.forName(driver);
for (int i = 0; i < 20; i++) {
pool.add(DriverManager.getConnection(url, user, password));
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public PrintWriter getLogWriter() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public int getLoginTimeout() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
// TODO Auto-generated method stub
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
// TODO Auto-generated method stub
return false;
}
/**
* 重寫getConnection()方法
*/
@Override
public Connection getConnection() throws SQLException {
/*
* 如果pool長度大於0,說明pool存在連接,將第一個對象返回,否則拋出異常
*/
// 未使用動態代理前
/*
* if(pool.size()>0) { return pool.removeFirst(); }else { throw new
* SQLException("連接池已耗盡,無法分配新的連接..."); }
*/
/**
* 未使用動態代理前,客戶端進行第21次連接的時候就拋出連接池已耗盡的異常,雖然使用conn.close()方法歸還連接,但是該方法是將連接歸還給數據庫,而不是我們定義的pool對象
* 如果要實現調用conn.close()方法後將conn連接對象歸還給pool連接池,就需要使用Java
* 動態代理重寫Connection接口的close()方法。
* 這是客戶端第19次獲得數據庫連接...,連接信息:com.mysql.jdbc.Connection@389922
* 這是客戶端第20次獲得數據庫連接...,連接信息:com.mysql.jdbc.Connection@1cda81e Exception in
* thread "main" java.sql.SQLException: 連接池已耗盡,無法分配新的連接... at
* cn.sjq.proxy.ds.pool.DataSourcePool.getConnection(DataSourcePool.java:101) at
* cn.sjq.proxy.ds.pool.ClientTest.main(ClientTest.java:21)
*/
/**
* 下面使用動態代理重寫Connection接口的close()方法,實現調用close()方法後歸還DataSourcePool連接池的連接。 loader
* - 定義代理類的類加載器 interfaces - 代理類要實現的接口列表 h - 指派方法調用的調用處理程序
* 通過使用動態代理後客戶端可以源源不斷獲得連接,而不是到達20個上限後拋出異常,如下:
* 這是客戶端第25次獲得數據庫連接...,連接信息:com.mysql.jdbc.Connection@1cc5af0
* 這是客戶端第26次獲得數據庫連接...,連接信息:com.mysql.jdbc.Connection@1665a0d
* 這是客戶端第27次獲得數據庫連接...,連接信息:com.mysql.jdbc.Connection@a22e0c
* 這是客戶端第28次獲得數據庫連接...,連接信息:com.mysql.jdbc.Connection@17d51a6
*/
if (pool.size() > 0) {
// 從pool裏面獲取一個連接對象Connection
final Connection conn = pool.removeFirst();
// 使用Java 動態代理重寫Connection中close()方法
Connection newProxyConn = (Connection) Proxy.newProxyInstance(DataSourcePool.class.getClassLoader(),
conn.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 重寫close方法
if (method.getName().equals("close")) {
// 回收釋放連接
pool.add(conn);
return null;
} else {
// 其它方法不重寫
return method.invoke(conn, args);
}
}
});
return newProxyConn;
} else {
throw new SQLException("連接池已耗盡,無法分配新的連接...");
}
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
// TODO Auto-generated method stub
return null;
}
}
package cn.sjq.proxy.ds.pool;
import java.sql.Connection;
import java.sql.SQLException;
/**
* 模擬客戶端連接數據
* @author songjq
*
*/
public class ClientTest {
public static void main(String[] args) throws SQLException, InterruptedException {
int count = 0;
//實例化連接池對象DataSourcePool
DataSourcePool pool = new DataSourcePool();
//循環模擬連接數據庫
while(true) {
//獲得一個連接
Connection conn = pool.getConnection();
count++;
//打印連接信息
System.out.println("這是客戶端第"+count+"次獲得數據庫連接...,連接信息:"+conn);
//關閉客戶端連接,將連接歸還連接池
conn.close();
//線程睡眠3秒
Thread.sleep(3000);
}
}
}
2018-07-24期 Java動態代理實現數據庫連接池