[JDBC]数据库连接池拦截close的方法/代理模式关闭连接

时间:2022-09-22 11:49:44

关闭连接池的方法就是将连接放回连接池,会另外创建一个free方法把连接放回集合中,但是,如果操作直接关闭原来的connection而不是使用定义的方法,就容易造成连接池失效。

所以,必须要拦截关闭原来连接的close方法。这种方法是代理模式的一种(不是很了解)

》》通过实现jdbc.connection接口

实际上真正的连接的其他工作交给真正的sql的connection去做,我们要做的是实现接口(jdbc)里的close方法。

如下

package com.yiki.pool;

import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.Map;
import java.util.Properties;
import java.util.TimeZone;
import java.util.concurrent.Executor;
import com.mysql.jdbc.ExceptionInterceptor;
import com.mysql.jdbc.Extension;
import com.mysql.jdbc.MySQLConnection;
import com.mysql.jdbc.log.Log;

public class myConnection implements com.mysql.jdbc.Connection {// Jdbc的connection

	private Connection realConnection;// sql的connection
	private myDataSource dataSource;

	public myConnection(Connection connection, myDataSource dataSource) {
		this.realConnection = connection;
		this.dataSource = dataSource;
	}

	@Override
	public void close() throws SQLException {// 最重要的!!!!!!
		this.dataSource.conPool.addLast(this);
	}

	@Override
	public Statement createStatement() throws SQLException {
		return this.realConnection.createStatement();
	}

	@Override
	public PreparedStatement prepareStatement(String sql) throws SQLException {
		return this.realConnection.prepareStatement(sql);
	}
//...以下省略n个要实现的方法……方式同上
但是:你会发现如果要把除了close的方法其他方法都要return this.realConnection.方法,就会发现非常繁琐

package com.yiki.pool;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;

public class myDataSource {

	private static String url;
	private static String user;
	private static String password;

	private static int initSize = 3;// 初始化连接池的连接数
	private static int MaxSize = 5;// 假设数据库可以创建的最大连接数
	private static int CurrentSize = 0;// 当前连接数

	static {
		url = "jdbc:mysql://localhost:3306/yiki?useUnicode=true&characterEncoding=gb2312";
		user = "root";
		password = "******";
	}

	LinkedList<Connection> conPool = new LinkedList<Connection>();// 创建链表来存储连接

	public myDataSource() {

		for (int i = 0; i < initSize; i++) {// 连接池可以容纳5个连接
			try {
				this.conPool.addLast(this.createCon());
				myDataSource.CurrentSize++;
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}

	}

	public Connection getCon() throws Exception {

		synchronized (conPool) {// 加锁,保证多个线程不会难道同一个链接
			if (this.conPool.size() > 0) {// 看看还有没有
				return this.conPool.removeFirst();// 就是从连接池里取出来(连接池是链表,移走表头
			} // else表已经没有连接了,就再创建连接
			if (myDataSource.CurrentSize < MaxSize) {// 如果请求的连接超载了,可在数据库允许的连接上再创建连接
				myDataSource.CurrentSize++;
				return this.createCon();
			}

			throw new SQLException("连接池已没有链接");
		}

	}

	public void free(Connection con) {// 释放连接就是把连接放回连接池
		if (con instanceof myConnection) {
			this.conPool.addLast((myConnection)con);
		}
	}

	private Connection createCon() throws SQLException {// 创建连接
		Connection realConn = DriverManager.getConnection(url, user, password);
		myConnection myConnection = new myConnection(realConn, this);//拦截close,防止其他人把真正的connection关掉,而是把它添加回连接池
		return myConnection;
	}

}

》》动态代理实现InvocationHandler接口

package com.yiki.ConnHandler;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;

public class myConnectionHandler implements InvocationHandler {

	private Connection realConnection;// sql
	private myDataSource dataSource;
	private Connection warpedConnection;

	private int maxUseCount = 10;// 最大连接次数
	private int currentUserCount = 0;// 现在连接次数

	myConnectionHandler(myDataSource myDataSource) {
		this.dataSource = myDataSource;

	}

	Connection bind(Connection realconn) {

		this.realConnection = realconn;
		this.warpedConnection = (Connection) Proxy.newProxyInstance(this.getClass().getClassLoader(),
				new Class[] { Connection.class }, this);

		return warpedConnection;

	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

		if ("close".equals(method.getName())) {
			this.currentUserCount++;
			if (currentUserCount < maxUseCount) {
				this.dataSource.conPool.addLast(this.warpedConnection);
			} else {
				this.realConnection.close();
				myDataSource.CurrentSize--;
				System.out.println("myDataSource.CurrentSize:"+myDataSource.CurrentSize);
			}

		}

		return method.invoke(this.realConnection, args);
	}

}
package com.yiki.ConnHandler;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;

public class myDataSource {

	private static String url;
	private static String user;
	private static String password;

	private static int initSize = 3;// 初始化连接池的连接数
	private static int MaxSize =10;// 假设数据库可以创建10个连接
	public static int CurrentSize = 0;// 当前连接数

	static {
		url = "jdbc:mysql://localhost:3306/yiki?useUnicode=true&characterEncoding=gb2312";
		user = "root";
		password = "******";
	}

	LinkedList<Connection> conPool = new LinkedList<Connection>();// 创建链表来存储连接

	public myDataSource() {

		for (int i = 0; i < initSize; i++) {// 连接池可以容纳5个连接
			try {
				this.conPool.addLast(this.createCon());
				myDataSource.CurrentSize++;
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}

	}

	public Connection getCon() throws Exception {

		synchronized (conPool) {// 加锁,保证多个线程不会难道同一个链接
			if (this.conPool.size() > 0) {// 看看还有没有
				return this.conPool.removeFirst();// 就是从连接池里取出来(连接池是链表,移走表头
			} // else表已经没有连接了,就再创建连接
			if (myDataSource.CurrentSize < MaxSize) {// 如果请求的连接超载了,可在数据库允许的连接上再创建连接
				myDataSource.CurrentSize++;
				return this.createCon();
			}

			throw new SQLException("连接池已没有链接");
		}

	}

	public void free(Connection con) {// 释放连接就是把连接放回连接池
		this.conPool.addLast(con);
	}

	private Connection createCon() throws SQLException {// 创建连接
		Connection realconn = DriverManager.getConnection(url, user, password);
		myConnectionHandler proxy = new myConnectionHandler(this);
		return proxy.bind(realconn);
				

	}

}

package com.yiki.ConnHandler;

import java.sql.Connection;
public class DButil {

	private static String driver;
	private static  myDataSource source ;
	
	static {
		driver = "com.mysql.jdbc.Driver";
		source = new myDataSource();
	}
	public static Connection open() {
		try {
			Class.forName(driver);
			return source.getCon();
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("连接错误");
		}
		return null;
	}
	public static void close(Connection con) {
		if (con != null) {
			try {
				con.close();
				//source.free(con);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

}

测试
[JDBC]数据库连接池拦截close的方法/代理模式关闭连接