自己動手開發Java DataSource

wangjianme 13年前發布 | 67K 次閱讀 Java Java開發 Connection 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 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測試保存通過。

 本文由用戶 wangjianme 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!