自己动手开发Java DataSource

wangjianme 12年前

而目前很多应用都接收一个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 2012-5-6

 */

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 2012-5-6

     */

    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 2012-5-6

 */

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测试保存通过。