2018-07-24期 Java动态代理实现数据库连接池

package cn.sjq.proxy.ds.pool;

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);

}

}

}


猜你喜欢

转载自blog.51cto.com/2951890/2149343
今日推荐