连接池初涉——自定义连接池

关于自定义连接池引入:

      使用JDBC进行数据库连接的时候,我们每次一个insert操作等等之后,就会把Connection关闭掉;但是这样是很低效的,我们的Connection在一次使用之后,就面临被关闭,加载和关闭太浪费资源了,而且这样放任外部自由的修改,不加限制的创建Connection对象,有可以会超过数据库能够承受的最大连接数,数据库容易崩溃!
     
      我们的解决此类问题的办法:
            创建一个连接池,外部要和数据库进行连接,必须通过连接池进行连接,连接池一次初始会初始定量的连接对象,也会限制程序能够 操作的最大的连接数量。如果超过了此最大连接数,就会抛出异常或者让用户进行等待。当用户不使用连接之后,就将连接放回连接池中。
     
     
关于静态代理和动态代理的引入:
      我们发现,在程序使用完了从连接池中获得的连接对象之后,必须要使用我们连接池内部提供的release方法进行连接的释放(即将其放回连接 池中),但是如果外部程序使用完了连接之后,直接close()了怎么办呢?
     
      试想外部程序直接close了之后会发生的结果:
      我们先构建一个场景:我们的初始化的连接数量为5,最大连接数量为10;并且我们当前正处于JDBC操作的峰值上,还有很多等待JDBC操作的程序;
      如果外部程序使用完了我们的连接对象,直接close的话,就会造成我们 当前的连接对象不能放回连接池中,但是连接池记录的当前的连接对象的数量依旧是10,但是我们当前已经close了一个连接,所以实际池中只有9个连接对象了;如果此类情况一直发生,就会造成我们连接池中的连接数量最后为0,但是连接池自身记录的连接数一直处于峰值状况,所有的 连接都是处于不可用的状态!那么这个连接池就算是废了!
     
首先引入的是静态代理:
      关于代理之前,我们要先复习一个概念;
      在JDBC中,JDBC所有的实现类都是数据库提供商编写的,java只提供了 一个规范,一堆接口;数据库提供商面向接口编程的;那么在我们使用JDBC操作的时候,我们开发者也是通过java.sql.*中的接口,作为引用操作的各个数据库提供商写出的实现类的对象;
      代理的概念就是:以前我们是直接使用Connection接口,现在:我们在Connection接口之上,再添加一个我们的Connection操作类,implements  Connection,然后,将其中我们需要修改的方法进行重写,其它的进行原样调用。
     
     
      静态代理就是完全重写一个接口,将其中所有的方法都要修改,不修改的就必须通过手动修改的方式和下层进行相连,当接口中方法很多的时候,这样很低效;
     

      动态代理:首先要运用反射中的Proxy类,来创建动态代理对象;通过反射的操作来降低我们代码中的重复操作(或低效代码)。

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.sql.SQLException;
import java.util.LinkedList;
/**
 * 自定义连接池
 * @author mzy
 */
public class MyPool {
	private static String url="jdbc:mysql://localhost:3306/test";
	private static String user="root";
	private static String password="123456";
	private static String driverClass="com.mysql.jdbc.Driver";
	
	private static LinkedList<Connection> pool = new LinkedList<Connection>();
	// 连接池的初始化连接数
	private int initCount = 5;
	// 连接池的最大连接数
	private int maxCount = 10;
	// 用于记录当前连接的数量
	private int currentCount = 0;
	
	public static LinkedList<Connection> getPool() {
		return pool;
	}
	
	public static void setPool(LinkedList<Connection> pool) {
		MyPool.pool = pool;
	}

	static {
		// 注册驱动
		try {
			Class.forName(driverClass);
		} catch(ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	public MyPool() {
		for(int i=1; i<=initCount; i++) {
			pool.addLast(createConnection());
			currentCount++;
		}
	}

	private Connection createConnection() {
		final Connection conn;
		try {
			conn = DriverManager.getConnection(url, user, password);
			// 1) 使用静态代理类的方式去创建Connection的代理类
			// MyConnection myConn = new MyConnection(this, conn);
			
			// 2) 使用动态代理类方式去创建Connection的代理类
			/**
			 *  使用到jdk的api:  Proxy类
			 *  		用于创建动态代理类对象:
			 *  		static Object newProxyInstance(
			 *  						ClassLoader loader,
			 *   						Class<?>[] interfaces, 
			 *   						InvocationHandler h
			 *   						)  
			 * 			参数一:类加载器。
			 * 			参数二: 代理类实现的接口列表
			 * 			参数三: 接口 InvocationHandler: 代理类的调用处理程序的接口。(代理完代理对象之后,对其中的方法如何处理???)
			 * 					Object invoke(
			 * 						Object proxy,  代理类对象      
			 * 						Method method,  代理类对象调用的方法。 
			 * 						Object[] args  调用代理类对象方法时传入的参数列表
			 * 					)    
			 */
			Connection myConn = (Connection)Proxy.newProxyInstance(MyPool.class.getClassLoader(), 
					new Class[]{Connection.class}, 
					new InvocationHandler() {

						@Override
						public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
							// 1) 重写需要重写的方法。close方法
							// 获取当前调用的方法的方法名称
							String methodName = method.getName();
							if("close".equals(methodName)) {
								MyPool.getPool().addLast(conn);
								return null;
							} else {
								// 2) 调用回原来的方法,获取返回值
								Object value = method.invoke(conn, args);
								return value;
							}
						}
				
			});
			return myConn;
		} catch (SQLException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}	
	}
	
	/**
	 * 对外提供给java程序一个获取连接的方法
	 */
	public Connection getConnection() {
		// 1) 当并发连接数小于等于初始化连接数量的时候,才从池中取出
		if(pool.size()>0) {
			System.out.println("==== 初始化的连接  ==== ");
			return pool.removeFirst(); // 取出并删除
		}
		
		// 2) 当并发数量超过初始化连接数据的时候,程序自行获取连接对象,但是
		// 一旦超过了最大连接数量的时候,不能获取
		if(currentCount<maxCount) {
			System.out.println("==== 新建的连接  ==== ");
			currentCount++;
			return createConnection();
		}
		
		// 3) 当超过了最大连接数时,不能再获取连接了。
		throw new RuntimeException("已经超过了最大连接数");
	}
	
	/**
	 * 对外提供释放连接对象的方法
	 */
	public void releaseConnection(Connection conn) {
		// 放回连接池容器中
		pool.addLast(conn);
	}
}

 

  测试方法:

 

package pool;

import java.sql.Connection;

public class TestPool {
	public static void main(String[] args) {
		// 1) 构造连接池对象
		MyPool myPool = new MyPool(); // 初始化连接
		// 模拟用户并发获取连接
		for(int i=0; i<11; i++) {
			Connection conn = myPool.getConnection();
			// 如果获取的连接数小于初始化连接数,就不用真的连接数据库
			System.out.println("第"+(i+1)+"个"+conn);
			
			if(i == 3) {
				// 模拟用户释放连接,把连接放回连接池中
				myPool.releaseConnection(conn);
			}
		}
		
		/*
		 * 我们当前的自定义pool已经完成了;
		 * 但是仍然有问题;
		 * 我们当前的连接池是没有加锁的,如果多个程序同时来拿的话,
		 * 是线程不安全的;
		 * 其次,当连接数量达到max的时候,当再有程序想获得Connection
		 * 我们直接是抛出一个runtimeException;
		 * 不合理,让用户进行等待sleep才是一个合理的操作!
		 */
	}
	/**
	 * 所以我们通常使用别人写好的连接池工具:
	 * 	通常使用DBCP(DataBase Connection Pool)
	 * 		是Apache组织编写的产品
	 * 
	 * C3P0
	 * 		是开源框架使用的(hibernate内置默认的连接池工具C3P0)
	 */
}

 

posted @ 2018-05-11 08:19  五彩世界  阅读(121)  评论(0编辑  收藏  举报