自己动手开发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测试保存通过。