自己動手開發Java DataSource
而目前很多應用都接收一個java.sql.DataSource。為此我們應該實現一個DataSource。實現DataSoure的關鍵即是實現此接口中的方法getConnection。通過查看java.sql.DataSource接口的API我們知道它的實現有以下幾種:
<!--[endif]-->
在此,我們實現第二種,即連接池的實現:
上代碼:
package cn.itcast.utils;
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.util.LinkedList;
import javax.sql.DataSource;
/**
* 默認情況下,池中保存著三個連接對象
* 并通過對Connection的代理實現在close時還回池
* @author <a href="mailto:wj@itcast.cn">王健</a>
* @version 1.0
*/
public class StdDataSource implements DataSource {
private int poolSize=3;//默認為3個
private LinkedList<Connection> pool = new LinkedList<Connection>();
public StdDataSource(String driver,String url,String name,String pwd){
this(driver,url,name,pwd,3);
}
public StdDataSource(String driver,String url,String name,String pwd,int poolSize){
try{
Class.forName(driver);
this.poolSize=poolSize;
if(poolSize<=0){
throw new RuntimeException("不支持的池大小"+poolSize);
}
for(int i=0;i<poolSize;i++){
Connection con = DriverManager.getConnection(url,name,pwd);
con = ConnProxy.getProxyedConnection(con,pool);//獲取被代理的對象
pool.add(con);//添加被代理的對象
}
}catch(Exception e){
throw new RuntimeException(e.getMessage(),e);
}
}
/**
* 獲取池大小
*/
public int getPoolSize() {
return poolSize;
}
/**
* 不支持日志操作
*/
public PrintWriter getLogWriter() throws SQLException {
throw new RuntimeException("Unsupport Operation.");
}
public void setLogWriter(PrintWriter out) throws SQLException {
throw new RuntimeException("Unsupport operation.");
}
/**
* 不支持超時操作
*/
public void setLoginTimeout(int seconds) throws SQLException {
throw new RuntimeException("Unsupport operation.");
}
public int getLoginTimeout() throws SQLException {
return 0;
}
@SuppressWarnings("unchecked")
public <T> T unwrap(Class<T> iface) throws SQLException {
return (T)this;
}
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return DataSource.class.equals(iface);
}
/**
* 從池中取一個連接對象<br/>
* 使用了同步和線程調度技術
*/
public Connection getConnection() throws SQLException {
synchronized (pool) {
if(pool.size()==0){
try {
pool.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e.getMessage(),e);
}
return getConnection();
}else{
Connection con = pool.removeFirst();
return con;
}
}
}
public Connection getConnection(String username, String password)
throws SQLException {
throw new RuntimeException("不支持接收用戶名和密碼的操作");
}
/**
* 靜態內部類,實現對Connection的動態代理
* @author <a href="mailto:wj@itcast.cn">王健</a>
* @version 1.0
*/
static class ConnProxy implements InvocationHandler{
private Object o;
private LinkedList<Connection> pool;
private ConnProxy(Object o,LinkedList<Connection> pool){
this.o=o;
this.pool=pool;
}
public static Connection getProxyedConnection(Object o,LinkedList<Connection> pool){
Object proxed = Proxy.newProxyInstance(o.getClass().getClassLoader(),
new Class[]{Connection.class},new ConnProxy(o,pool));
return (Connection) proxed;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(method.getName().equals("close")){
synchronized (pool) {
pool.add((Connection) proxy);//將被代理的對象放回池中
pool.notify();//通知等待線程去獲取一個連接吧
}
return null;
}else{
return method.invoke(o, args);
}
}
}
}
為了可以獲取DataSource我們一般還需要提供一個工廠類,以前在工廠類中只可以獲取Connection,現在應該在工廠類中獲取DataSource或是Connection。如果需要處理事務,則應該同時提供一個ThreadLocal對象來維護線程局部的Connection,以下是源代碼:
package cn.itcast.utils;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
/**
* 通過實例化自己的DataSource獲取連接
* @author <a href="mailto:wj@itcast.cn">王健</a>
* @version 1.0
*/
public class MyDsUtils {
private static DataSource dataSource;
private static ThreadLocal<Connection> thread = new ThreadLocal<Connection>();
static{
dataSource =
new StdDataSource("com.mysql.jdbc.Driver",
"jdbc:mysql:///itcast?characterEncoding=UTF-8",
"root","1234",3);
}
/**
* 直接獲取一個Connection
*/
public static Connection getConn(){
Connection con = null;
try {
con= dataSource.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(),e);
}
return con;
}
/**
* 獲取線程局部的Connection
*/
public static Connection getThreadConn(){
Connection con = thread.get();//先從線程中取數據
if(con==null){
con = getConn();
thread.set(con);
}
return con;
}
/**
* 可選的調用刪除局部線程中的對象
*/
public static void remove(){
thread.remove();
}
/**
* 獲取一個DataSource
*/
public static DataSource getDataSource(){
return dataSource;
}
}
以上代碼,通過dbutls測試保存通過。