编写数据库连接池的步骤:
1、要实现java.sql.DataSource接口,
2、在DataSource的构造函数里面,批量创建与数据库的连接.并把创建的数据库连接加入LinkedList. (因为用LinkedList便于增加和删除)
3、实现getConnection()方法,每次调用getConnection()方法时,在LinkedList中,去获取一个连接,并返回给用户
4、当用户用完连接Connection时,调用conn.close()方法时,应该将连接Connection返回给LinkedList,而不是返回给数据库.
5、编程的难点在于,当用户用完连接Connection时,调用conn.close()方法时,应该将连接Connection返回给LinkedList,而不是返回给数据库.利用动态代理来实现
6、自己手动实现的数据库连接池JDBCPool代码,如下
package com.jdbc.enhancegenericitypool.utils; 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.Properties; import java.util.logging.Logger; import javax.sql.DataSource; import com.jdbc.utils.MyJDBCUtils; /** * 只需要重写getConnection()方法即可 * */ public class JDBCPool implements DataSource { /** * 为了程序的更好的解耦合,利用Properties文件保存连接Mysql的配置文件 */ private static Properties config = new Properties(); /** * 用于保存连接池中的Connection */ private LinkedList<Connection> connPool = new LinkedList<Connection>(); /** * 默认Connection初始化的数量为3个 */ private int connCount = 3; // 在静态代码块里面,加载并且初始化Mysql的配置 static { try { config.load(MyJDBCUtils.class.getClassLoader().getResourceAsStream( "db.properties")); Class.forName(config.getProperty("driver")); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } /** * 1、在JDBCPool的构造方法中,初始化连接池的数量 */ public JDBCPool() { for (int i = 0; i < connCount; i++) { connPool.add(getConnectionToConnPool()); } } /** * 获取一个数据库的连接,并加入到数据库连接池connPool中 * * @return */ private Connection getConnectionToConnPool() { Connection conn = null; try { conn = DriverManager.getConnection(config.getProperty("url"), config.getProperty("username"), config.getProperty("password")); } catch (SQLException e) { throw new RuntimeException("获取连接Mysql数据库连接失败"); } return conn; } @Override public PrintWriter getLogWriter() throws SQLException { return null; } @Override public void setLogWriter(PrintWriter out) throws SQLException { } @Override public void setLoginTimeout(int seconds) throws SQLException { } @Override public int getLoginTimeout() throws SQLException { return 0; } @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { return null; } @Override public <T> T unwrap(Class<T> iface) throws SQLException { return null; } @Override public boolean isWrapperFor(Class<?> iface) throws SQLException { return false; } @Override public Connection getConnection() throws SQLException { if (connPool.size() <= 0) { throw new RuntimeException("服务器繁忙"); } // 获取数据库的连接池里面的Connection Connection conn = connPool.removeFirst(); // 使用动态代理的方式,来加强Connection的close()方法, // 是的连接的释放,会回到conns中,而不是数据库连接池中 Connection proxyConn = (Connection) Proxy.newProxyInstance(this .getClass().getClassLoader(), conn.getClass().getInterfaces(), new MyInvocationHabdle(conn) { }); return proxyConn; } @Override public Connection getConnection(String username, String password) throws SQLException { return null; } // 创建内部类MyInvocationHabdle,用于动态代理 public class MyInvocationHabdle implements InvocationHandler { /** * 保存被代理的对象 */ private Connection conn; public MyInvocationHabdle(Connection conn) { this.conn = conn; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 当调用close方法时,把数据库的连接返回给conns, if ("close".equals(method.getName())) { connPool.add(conn); return null; } // 调用其他方法时,直接返回 return method.invoke(conn, args); } } }
7、对于连接池的测试代码,证明获取的数据库的连接就是连接池里面的
package com.jdbc.enhancegenericitypool.utils; import java.sql.Connection; import java.sql.SQLException; public class PoolTest { public static void main(String[] args) throws SQLException { JDBCPool jdbcPool = new JDBCPool(); for (int i = 0; i < 9; i++) { Connection connection = jdbcPool.getConnection(); System.out.println(connection); connection.close(); } } }