代理模式实现连接池

1,106 阅读4分钟

一、连接池的原理。

  每一次web请求都要建立一次数据库连接。建立连接需要花费大量时间,系统还要分配内存资源。对于现在的web应用,尤其是大型电子商务网站,同时有几百人甚至几千人在线是很正常的事。在这种情况下,频繁的进行数据库连接操作势必占用很多的系统资源,网站的响应速度必定下降。

  其次,对于每一次数据库连接,使用完后都得断开。否则,如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将不得不重启数据库。还有,这种开发不能控制被创建的连接对象数,系统资源会被毫无顾及的分配出去,如连接过多,也可能导致内存泄漏.

  为了避免频繁的创建和断开连接,可以事先在系统中创建一定量的连接放在一个容器中,当用户需要使用时,就在池子中取出事先准备好的连接,用户使用完后,在将连接放回池子中。工作原理如下图:

代理模式实现简单连接池

  实现连接池,具体需要分为两步,第一步创建自己的代理类(AgencyConnection),这个类需要继承一个Connection接口的实现类(这里采用ConnectionImpl)。同时要实现连接的回收,即需要重写Conncetion接口中colse()方法。然后这个类需要一个有参构造,用来接收原始的Connection对象,将原始的Connection对象包装成自己的代理类对象。代码如下:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import com.mysql.jdbc.ConnectionImpl;

/**
 * 代理类时connection的一个实现类 ,为了方便,继承一个connection的一个实现类
 * 
 * @author Administrator
 *
 */
public class AgencyConncetion extends ConnectionImpl {

	// 定义一个Connection变量
	private static Connection conn;

	// 创建一个有参构造接收Connection的对象,并赋值给conn
	public AgencyConncetion(Connection conn) {
		this.conn = conn;
	}

	/**
	 * 重写close方法,将连接资源放到连接池中
	 */
	@Override
	public synchronized void close() throws SQLException {
		// 如果连接池中的连接数小于三,将连接回收到连接池中,否则直接调用父类的close方法,将连接断开
		if (ConnPool.list.size() < 3) {
			ConnPool.list.add(this);
		} else {
			conn.close();
		}
		ConnPool.maxCount++;
	}

	/**
	 * 重写prepareStatement方法,获取连接
	 */
	@Override
	public PreparedStatement prepareStatement(String sql) throws SQLException {
        //调用Connection中的方法
		return conn.prepareStatement(sql);
	}

}

  第二步、创建连接池(ConnPool): 在这个类中,需要创建一个可以容纳连接的容器,这里使用list集合的方式。同时创建的连接对象必须是AgencyConnection类的对象,只有这样,才可以使用AgencyConncetio类中close()方法回收连接。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

@SuppressWarnings("resource")
public class ConnPool {
	// 定义池子的容量
	public static int initCount = 3;
	// 定义池子最大连接数
	public static int maxCount = 5;
	// 定义池子的容器
	public static List<Connection> list = new ArrayList<Connection>();
	static {
		try {
			// 注册驱动
			Class.forName("com.mysql.jdbc.Driver");
			// 创建三个连接
			for (int i = 0; i < initCount; i++) {
				Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
				AgencyConncetion agency = new AgencyConncetion(conn);
				list.add(agency);
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	/**
	 * 获取连接方法
	 * 
	 * @return
	 */
	public Connection getConnection() {
		// 如果池子还有连接,直接返回集合中第一个连接
		if (list.size() > 0) {
			// 最大连接数减一,计数
			maxCount--;
			return list.remove(0);
		} else {
			// 如果池子已经空了,而且最大连接数大于0,说明还可以继续连接
			if (maxCount > 0) {
				try {
					// 创建临时连接并返回
					Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
					return new AgencyConncetion(conn);
				} catch (SQLException e) {
					e.printStackTrace();
				}
				// 否则返回空
			} else {
				return null;
			}
		}
		return null;
	}
}

  定义测试类,测试连接池效果

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class Test {
	
	public static void main(String[] args) throws SQLException {
		//创建连接池对象
		ConnPool connPool = new ConnPool();
		//调用getConnection()方法,获取连接
		Connection conn1 = connPool.getConnection();
		System.out.println(conn1);
		Connection conn2 = connPool.getConnection();
		System.out.println(conn2);
		Connection conn3 = connPool.getConnection();
		System.out.println(conn3);
		conn1.close();
		conn2.close();
		conn3.close();
		//关闭前三个连接
		Connection conn4 = connPool.getConnection();
		System.out.println(conn4);
		Connection conn5 = connPool.getConnection();
		System.out.println(conn5);
		Connection conn6 = connPool.getConnection();
		System.out.println(conn6);
		Connection conn7 = connPool.getConnection();
		System.out.println(conn7);
		Connection conn8 = connPool.getConnection();
		System.out.println(conn8);
/*		
		//创建操作对象
		PreparedStatement ps1 = conn1.prepareStatement("insert into t_user values(null,?,?)");
		//操作数据库
		ps1.setString(1, "1111");
		ps1.setString(2, "111222");
		ps1.executeUpdate();
		ps1.close();
		conn1.close();
//		Connection conn2 = connPool.getConnection();
//		System.out.println(conn2);
//		conn2.close();
*/
	}
}

  结果分析:连接池中定义最大的连接数是5,初始化连接数是3,前三个连接调用的是close()方法是将连接回收,放回list集合中。所以后面五个连接对应的五个地址中一定有三个与前三个连接相同。 结果如下:

  结果符合设想结果,连接池效果实现。

  代理模式中,代理类只提供了一个回收方法,Conncetion的其他功能全部都是Connection的实现类来完成。用户创建的对象却是经过包装代理类的对象。