一、数据库连接池原理
例:
************************************************************************************
public class SimpleConnectionPool {
private static String driverClassName;
private static String url;
private static String user;
private static String password;
// 创建一个集合作为存储连接的池,用来存储数据库的连接
private static LinkedList<Connection> pool = new LinkedList<Connection>();
static{
try {
InputStream in = JdbcUtil.class.getClassLoader().getResourceAsStream("db.properties");
Properties props = new Properties();
props.load(in);
driverClassName = props.getProperty("driverClassName");
url = props.getProperty("url");
user = props.getProperty("user");
password = props.getProperty("password");
Class.forName(driverClassName);
// 初始化10个连接到池中
for (int i = 0; i < 10; i++) {
Connection conn = DriverManager.getConnection(url, user, password);
pool.add(conn);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 从数据库连接池中获取连接 注:为了避免多线程均从第一个拿,所以要加同步
public synchronized static Connection getConnection(){
if(pool.size()>0){
// 从池中拿到连接,并从池中删掉该连接
Connection conn = pool.remove();
return conn;
}else{
System.out.println("对不起,服务器繁忙");
return null;
}
}
// 将连接放回数据库连接池中
public static void release(Connection conn){
pool.add(conn);
}
}
************************************************************************************
// 模拟使用数据库连接池(数据源)
public class SimpleConnectionPoolClient {
public static void main(String[] args) {
// 创建连接
Connection c1 = SimpleConnectionPool.getConnection();
// 省略一些使用c1操作DAO的代码
Connection c2 = SimpleConnectionPool.getConnection();
// 省略一些使用c2操作DAO的代码
Connection c3 = SimpleConnectionPool.getConnection();
// 省略一些使用c3操作DAO的代码
// 释放连接
SimpleConnectionPool.release(c2);
SimpleConnectionPool.release(c3);
SimpleConnectionPool.release(c1);
}
}
************************************************************************************
二、编写标准的数据库连接池
6.1 实现了javax.sql.DataSource接口的才是标准的数据库连接池,按照字面意思,一般称之为数据源。
6.2 创建一个标准的数据源
**********************************************************************************************
public class MyDataSource implements DataSource {
private static String driverClassName;
private static String url;
private static String user;
private static String password;
private static LinkedList<Connection> pool = new LinkedList<Connection>();
static{
try {
InputStream in = JdbcUtil.class.getClassLoader().getResourceAsStream("db.properties");
Properties props = new Properties();
props.load(in);
driverClassName = props.getProperty("driverClassName");
url = props.getProperty("url");
user = props.getProperty("user");
password = props.getProperty("password");
Class.forName(driverClassName);
// 初始化10个连接到连接池中
for(int i=0; i<10; i++){
Connection conn = DriverManager.getConnection(url, user, password);
pool.add(conn);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 从池中获取一个连接
@Override
public synchronized Connection getConnection() throws SQLException {
if(pool.size()>0){
Connection conn = pool.remove();
// 方式2-Ⅰ: 包装设计模式复写close方法
// MyConnection mconn = new MyConnection(conn, pool);
// 方式2-Ⅱ: 通过继承 默认适配器的包装设计模式复写close方法
MyConnection1 mconn = new MyConnection1(conn, pool);
return mconn;
}else{
throw new RuntimeException("对不起,服务器繁忙");
}
}
@Override
此处省略一些接口中需要覆盖的方法
}
**********************************************************************************************
public class MyDataSourceClient {
// private static DataSource ds = new MyDataSource();
// 方式3: 使用动态代理方式创建的数据源来拦截close方法
private static DataSource1 ds = new MyDataSource1();
public static void main(String[] args) {
Connection conn = null;
PreparedStatement stmt = null;
try{
conn = ds.getConnection();
// 利用conn做一些dao操作
conn.createStatement();
}catch(Exception e){
e.printStackTrace();
}finally{
if(stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn!=null){
try {
conn.close(); // 该方法应该做到:不要关闭连接,而应该还回池中,应该复写该方法
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
}
}
**********************************************************************************************
6.3 引发的问题: conn.close() // 该方法应该做到:不要关闭连接,而应该还回池中,应该复写该方法
对于一个已知类的某个方法进行功能上的改变有以下三种方式:
1、定义子类,扩展父类的某个功能。(此处行不通)
2、利用包装设计模式改写原有的类的功能
方式2-Ⅰ:包装设计模式
a、编写一个类实现与被改写类(com.mysql.jdbc.Connection)相同的接口
b、定义一个引用,记住被改写类的实例
c、定义构造方法,传入被改写类的实例
d、对于要改写的方法,改写即可
e、对于不需要改写的方法,调用原有的对象的对应方法
方式2-Ⅱ:默认适配器设计模式
a.编写一个类,继承默认适配器
b.定义一个引用,记住被改写类的实例
c.定义构造方法,传入被改写类的实例
d.对于要改写的方法,改写即可
3、动态代理: 基于接口的动态代理(最佳解决方案)
java.lang.reflect.Proxy
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
作用:返回代理类的实例
参数:
loader:类加载器,一般与被代理对象使用同一个
interfaces:被代理对象所实现的接口
h:怎么代理
Object invoke(Object proxy, Method method, Object[] args) :调用原有类的任何方法,都会经过此方法。
**********************************************************************************************
方式2-Ⅰ:包装设计模式改写com.mysql.jdbc.Connect中的close访法,在调用时,不是关闭连接,而是返回池中
public class MyConnection implements Connection{
private Connection conn; // 引用被改写对象
private LinkedList<Connection> pool;
public MyConnection(Connection conn,LinkedList<Connection> pool){
// 通过构造方法注入需要的对象: 依赖注入: spring框架的核心
this.conn = conn;
this.pool = pool;
}
@Override
public void close() throws SQLException {
pool.add(conn);
}
@Override
public void clearWarnings() throws SQLException {
conn.clearWarnings();
}
@Override
此处省略一系列的接口中被覆盖的方法
}
在上面的获取连接,将被包装的类换为包装类:MyConnection mconn = new MyConnection(conn, pool);
**********************************************************************************************
方式2-Ⅱ: 通过继承默认适配器的包装设计模式改写com.mysql.jdbc.Connect中的close访法
// 全部调用原有对象的原有方法,没有做任何功能上的改动
// 此类便是一个默认适配器 再设置一个MyConnection1继承该类,并覆盖close方法
public class ConnectionWrapper implements Connection { // 包装类
private Connection conn;
public ConnectionWrapper(Connection conn){
this.conn = conn;
}
@Override
public void clearWarnings() throws SQLException {
conn.clearWarnings();
}
此处省略一系列的覆盖的方法;
}
// 继承默认适配器类,复写close方法
public class MyConnection1 extends ConnectionWrapper {
private Connection conn;
private LinkedList<Connection> pool;
public MyConnection1(Connection conn, LinkedList<Connection> pool) {
super(conn);
this.pool = pool;
}
@Override
public void close() throws SQLException {
pool.add(conn);
}
}
在上面的获取连接,将被包装的类换为包装类:MyConnection1 mconn = new MyConnection1(conn, pool);
**********************************************************************************************
方式3: 基于接口的动态代理(最佳方案)
public class MyDataSource1 implements DataSource {
private static String driverClassName;
private static String url;
private static String user;
private static String password;
private static LinkedList<Connection> pool = new LinkedList<Connection>();
static{
try {
InputStream in = JdbcUtil.class.getClassLoader().getResourceAsStream("db.properties");
Properties props = new Properties();
props.load(in);
driverClassName = props.getProperty("driverClassName");
url = props.getProperty("url");
user = props.getProperty("user");
password = props.getProperty("password");
Class.forName(driverClassName);
// 初始化10个连接到连接池中
for(int i=0; i<10; i++){
Connection conn = DriverManager.getConnection(url, user, password);
pool.add(conn);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 从池中获取一个连接
@Override
public Connection getConnection() throws SQLException {
if(pool.size()>0){
final Connection conn = pool.remove(); // 原有对象
// 返回动态代理对象
return (Connection)Proxy.newProxyInstance(conn.getClass().getClassLoader(),
conn.getClass().getInterfaces(), new InvocationHandler(){
@Override //注: 调用Conncection的任何方法都会经过该方法
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if("close".equals(method.getName())){
return pool.add(conn);
}else{
return method.invoke(conn, args);
}
}
});
}else{
throw new RuntimeException("对不起,服务器繁忙");
}
}
@Override
此处省略一系列的覆盖方法
}
注: 然后调用数据源的时候调用该数据源即可;
**********************************************************************************************