Java使用动态代理编写数据库连接池 - Go语言中文社区

Java使用动态代理编写数据库连接池


在上一篇博客中,将5个连接放到栈里,当做数据库连接池使用,加快了效率。代码如下:

import java.sql.Connection;
import java.sql.DriverManager;
import java.util.ResourceBundle;
import java.util.Stack;

public class DataPool {
	private static Stack<Connection> pool = new Stack<Connection>();
	static {
		try {
		//读取配置文件的信息
			ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
			String Driver = bundle.getString("jdbc.driver");
			String url = bundle.getString("jdbc.url");
			String user = bundle.getString("jdbc.username");
			String password = bundle.getString("jdbc.password");
			Integer size = Integer.valueOf(bundle.getString("jdbc.initialSize"));
			Class.forName(Driver);//// 另一种写法new com.mysql.jdbc.Driver();相当于driver写入内存
			for (int i = 0; i < size; i++) {
	Connection connection = DriverManager.getConnection(url, user, password);//创建连接
				pool.push(connection);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static Connection getConn() {
		return pool.pop();
	}
	public static void close(Connection connection) {
		pool.push(connection);
}

在这里插入图片描述
但是有一个问题,如果当多线程连接数据库时,当超过5个线程连接数据库时,那么在第6个线程包括之后就连接不到数据库了。
这里用了java的动态代理来代理数据库的连接,拦截连接的close方法,来控制数据库连接池的连接数量。以防止多线程访问时出现获取不到连接的情况,并且给代理的连接加上一个时间属性,和实时监控的线程。初始化5个连接放到栈里当做数据库连接池。当连接执行数据库操作时,则去调用真正的连接方法,且当连接用完时,将连接的时间清零,放回连接池里。如果大于5个连接数据库时,连接池没有连接时,则创建新的连接放进连接池里,当连接关闭时,如果连接池里已有5个连接,多余的连接如果超过10秒没被使用,则关闭连接。
实现代码如下:

package pool;

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.util.Stack;

public class PoolServer {
	// 代理连接类
	public class ConnProxy {
		// 连接属性
		private Connection conn;
		// 用于计时的属性
		private int idle;

		public int getIdle() {
			return idle;
		}

		public void setIdle(int idle) {
			this.idle = idle;
		}

		public void setConn(Connection conn) {
			this.conn = conn;
		}

		public Connection getConn() {
			return conn;
		}

		//
		public ConnProxy() {
			// 设置空闲时间,如果空闲时间超过10秒,则回收,关闭连接
			new Thread(() -> {
				try {
					while (true) {
						Thread.sleep(1000);
						idle += 1000;
						if (idle > 10000) {
							synchronized (Object.class) {
								if (pool.size() > 5) {
									conn.close();
									pool.remove(ConnProxy.this);
									break;
								}
							}
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}).start();
		}
	}

	// 使用栈定义数据库连接池
	private Stack<ConnProxy> pool = new Stack<ConnProxy>();
	// 初始化连接
	{
		try {
			Class.forName("com.mysql.jdbc.Driver");
			for (int i = 0; i < 5; i++) {
				Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/dvdstore", "root", "root");
				ConnProxy connProxy = new ConnProxy();
				connProxy.setConn(conn);
				pool.push(connProxy);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	// 获取连接
	// 这里加了锁,防止在多线程情况下,会同时去获取连接导致空栈
	public synchronized Connection getConn() {
		// 如果数据库池里没连接的时候,则创建新的连接放入数据库连接池
		if (pool.isEmpty()) {
			for (int i = 0; i < 3; i++) {
				try {
					Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/dvdstore", "root",
							"root");
					ConnProxy connProxy = new ConnProxy();
					connProxy.setConn(conn);
					pool.push(connProxy);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		// ***
		// 有连接,则从栈里获取连接
		final ConnProxy p = pool.pop();
		// 使用java动态代理来代理连接类,拦截close这个方法
		return (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), new Class[] { Connection.class },
				new InvocationHandler() {
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						synchronized (Object.class) {
							// 当连接close则将连接重新放回数据库连接池
							if (method.getName().equals("close")) {
								p.setIdle(0);// 被使用过的idle从0开始

								pool.push(p);

								System.out.println(pool.size());
								return null;// 不让其调用真正的close方法
							} else {// 当connect不是执行close方法,而是执行其他操作,例如增删改查等操作
								Connection conn = p.getConn();
								return method.invoke(conn, args);// 调用其真正的connetion方法
							}
						}
					}
				});
	}

	// 测试
	public static void main(String[] args) throws Exception {
		final PoolServer poolServer = new PoolServer();

		for (int i = 0; i < 5; i++) {
			new Thread(()-> {
					try {
						Connection conn = poolServer.getConn();
						conn.close();
						// System.out.println(conn);
						// conn.close();
					} catch (Exception e) {
						e.printStackTrace();
					}
			}).start();
		}
		System.in.read();
	}
}

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_41528317/article/details/83831225
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-06-27 22:55:47
  • 阅读 ( 1247 )
  • 分类:数据库

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢