JDBC/InvocationHandler动态代理实现数据库连接池数据源
Posted smile4lee
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDBC/InvocationHandler动态代理实现数据库连接池数据源相关的知识,希望对你有一定的参考价值。
Java的JDBC编程中,数据库的连接和关闭是十分耗费资源的。当对数据库的访问不是很频繁时,可以在每次访问数据库时建立一个连接,用完之后关闭。但是,对于一个复杂的数据库应用,频繁的建立、关闭连接,会极大的减低系统性能,造成瓶颈。所以可以使用数据库连接池来达到连接资源的共享,使得对于数据库的连接可以使高效、安全的复用。
MyDataSource 实现数据库连接池
通过自定义数据库了连接MyConnection(包裹了真正的Connection),使用一个LinkedList存放MyConnection,当一个数据库连接使用完成后,重新添加到LinkedList中,实现连接的复用。数据库连接池初始化时,构造多个数据库连接,虽然此时比较耗时,但是能够实现连接池的复用,提高效率。
package com.jdbc.datasource;
import java.sql.*;
import java.util.LinkedList;
/**
* 数据库连接池
* 自定义实现数据源
*
*/
public class MyDataSource
private static final String URL = "jdbc:postgresql://localhost:5432/db";
private static final String USER = "postgres";
private static final String PASSWORD = "root";
private static final int INI_COUNT = 5; // 初始连接数
private static final int MAX_COUNT = 10; // 最大连接数
public int curCount= 0; // 当前连接数
// add remove频繁,LinkedList 效率由于 ArrayList
LinkedList<Connection> connsPool = new LinkedList<Connection>();
/**
* 初始构造多个数据库连接
*/
public MyDataSource()
try
for (int i = 0; i < INI_COUNT; i++)
this.connsPool.add(this.createConnection());
curCount++;
catch (SQLException e)
e.printStackTrace();
/**
* 创建连接
* @return
* @throws SQLException
*/
public Connection createConnection() throws SQLException
Connection realConn = DriverManager.getConnection(URL, USER, PASSWORD);
// MyConnection conn = new MyConnection(this, realConn);
/*** Connection 的代理类,绑定真正的Connection,拦截 close()方法 ***/
MyConnectionHandler pHandler = new MyConnectionHandler(this);
return pHandler.bind(realConn);
/**
* 释放
* @param conn 数据库连接
*/
public void free(Connection conn)
this.connsPool.addLast(conn);
/**
* 获取连接
* @return
* @throws SQLException
*/
public Connection getConnection() throws SQLException
Connection conn = null;
/*** 同步加锁 ***/
synchronized (connsPool)
if (this.connsPool.size() > 0)
conn = this.connsPool.removeFirst();
return conn;
else if (curCount < MAX_COUNT) // 连接池里面没有连接,且当前连接数没有达到最大连接
this.curCount++; // 创建新连接
conn = this.createConnection();
return conn;
throw new SQLException(" 连接池里已无可用连接 ... ");
MyConnection 实现 Connection接口方式
将自定义类MyConnection实现Connection接口,重写close()方法,关闭时重新放入连接池,其他的非close()方法则直接转交给真正的Connection实现即可。该方式的缺点是,需要实现Connection接口的所有方法。
package com.jdbc.datasource;
import java.sql.*;
import java.util.*;
import java.util.concurrent.Executor;
/**
* MyConnection 代理 Connection,实现 Connection接口
* 相当于Connection的子类,可以和Connection一样操作
* 代理了所有对真正的Connection操作
* 重要:close()方法重写,关闭时重新放入连接池
* 不足:需要重写所有方法
* 改进:改用Proxy代理模式实现,只对close()方法进行拦截,不修改其他方法
*
*/
public class MyConnection implements Connection
private MyDataSource myDataSource = null; // 数据源
private Connection realConn = null; // 真正的connection
private static final int MAX_USE_COUNT = 5; // 最大使用次数
private int curUseCount = 0; // 当前使用次数
MyConnection(MyDataSource myDataSource, Connection realConn)
this.myDataSource = myDataSource;
this.realConn = realConn;
/**
* close()方法重写
* 未超过最大使用次数,关闭时重新放入连接池
* @throws SQLException
*/
@Override
public void close() throws SQLException
curUseCount++;
if (curUseCount < MAX_USE_COUNT)
this.myDataSource.connsPool.addLast(this);
else
this.realConn.close();
this.myDataSource.curCount--;
/**
* 其他方法直接交给realConn实现
* @throws SQLException
*/
@Override
public boolean isClosed() throws SQLException
return this.realConn.isClosed();
//其他方法略...
InvocationHandler动态代理实现方式
MyConnectionHandler类实现InvocationHandler接口,最主要包括两个步骤:
- Proxy.newProxyInstance()方法
this.warpedConn = (Connection) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[] Connection.class , this);
使用包裹后的Connection代理真正的Connection - 重写invoke()方法
public Object invoke(Object proxy, Method method, Object[] args)
拦截代理对应的方法。
package com.jdbc.datasource;
import java.lang.reflect.*;
import java.sql.Connection;
/**
* Connection 代理类
* 拦截 close()方法
*
*/
public class MyConnectionHandler implements InvocationHandler
private Connection realConn = null; // 真正的连接
private Connection warpedConn = null; // 包裹的连接(代理连接)
private MyDataSource myDataSource = null; // 连接池
private static final int MAX_USE_COUNT = 5; // 最大使用次数
private int curUseCount = 0; // 当前使用次数
/**
* 构造方法
* @param myDataSource 连接池
*/
public MyConnectionHandler(MyDataSource myDataSource)
this.myDataSource = myDataSource;
/**
* 绑定真正的连接到包裹连接
* @param realConn 真正的连接
* @return
*/
public Connection bind(Connection realConn)
/*** 真正的连接,用于数据库的其他操作 ***/
this.realConn = realConn;
/*** 代理模式动态生成类 ,实现了Connection.Class 接口, 其方法作用在当前handler上 ***/
/*** 用于拦截 realConn.close() 方法 ***/
this.warpedConn = (Connection) Proxy.newProxyInstance(this.getClass()
.getClassLoader(), new Class[] Connection.class , this);
return this.warpedConn; // 返回包裹后的连接
/**
* 重写 invoke() 方法
* 拦截close()方法,关闭连接是重新放回到数据库连接池
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
/*** 拦截 realConn.close() 方法 ***/
if (method.getName().equals("close"))
curUseCount++;
if (curUseCount < MAX_USE_COUNT)
this.myDataSource.connsPool.addLast(this.warpedConn);
else
this.realConn.close();
this.myDataSource.curCount--;
/*** 其他方法,直接执行到realConn上 ***/
return method.invoke(this.realConn, args);
Apache Commons DBCP 数据源
数据源一般不需要自己实现,apache DBCP数据源就是一种很好的开源实现。
将数据源的相关配置信息以配置文件的方式进行设置,读取配置文件,生成数据源即可。DBCP数据源会自动管理数据库连接池,JDBC操作直接从数据源中获取Connection即可。
DBCP数据源配置信息:
#连接设置
driverClassName=org.postgresql.Driver
url=jdbc:postgresql://localhost:5432/db
username=postgres
password=root
#<!-- 初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最大空闲连接 -->
maxIdle=20
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000
#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=gbk
#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true
#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=
#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED
JdbcUtil类使用DBCP数据源:
package com.utils;
import java.io.*;
import java.sql.*;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSourceFactory;
//import com.jdbc.datasource.MyDataSource;
public class JdbcUtil
/* private static final String URL = "jdbc:postgresql://localhost:5432/db";
private static final String USER = "postgres";
private static final String PASSWORD = "root";*/
// 使用自定义数据源
// private static MyDataSource myDataSource = null;
// 使用Apache的框架,提供数据源,实现了DataSource接口
private static DataSource myDataSource = null;
/**
* 私有构造方法
*/
private JdbcUtil()
/**
* 静态代码注册驱动
*/
static
try
Class.forName("org.postgresql.Driver").newInstance();
// 使用自定义数据源
// myDataSource = new MyDataSource();
// 使用Apache的框架,提供数据源,实现了DataSource接口
Properties prop = new Properties();
InputStream is = JdbcUtil.class.getClassLoader().
getResourceAsStream("dbcpconfig.properties");
prop.load(is);
myDataSource = BasicDataSourceFactory.createDataSource(prop);
catch (Exception e)
e.printStackTrace();
/**
* 获取datasource
* @return
*/
public static final DataSource GetDataSource()
return myDataSource;
/**
* 获取数据库连接
* @return
*/
public static final Connection GetConnection()
Connection conn = null;
try
// conn = DriverManager.getConnection(URL, USER, PASSWORD);
conn = myDataSource.getConnection();
catch (SQLException e)
e.printStackTrace();
return conn;
/**
* 关闭相关资源
* @param rs
* @param st
* @param conn
*/
public static final void Free(ResultSet rs, Statement st, Connection conn)
try
if (rs != null)
rs.close();
catch (SQLException e)
e.printStackTrace();
finally
try
if (st != null)
st.close();
catch (SQLException e)
e.printStackTrace();
finally
if (conn != null)
try
conn.close();
// myDataSource.free(conn);
catch (Exception e)
e.printStackTrace();
以上是关于JDBC/InvocationHandler动态代理实现数据库连接池数据源的主要内容,如果未能解决你的问题,请参考以下文章
Android 逆向Android 中常用的 so 动态库 ( /system/lib/libc.so 动态库 | libc++.so 动态库 | libstdc++.so 动态库 )(代
设计模式 结构型模式 -- 代理模式(动态代理(JDK动态代理(JDK动态代理要求必须定义接口,对接口进行代理。)动态代理原理(使用arthas-boot.jar查看代理类的结构)动态代理的作用)(代
Centos7-yum部署配置LAMP-之LAMP及php-fpm实现反代动态资源
Android 安装包优化使用 lib7zr.so 动态库处理压缩文件 ( 拷贝 lib7zr.so 动态库到 Android Studio 工程 | 配置 build.gradle 构建脚本 )(代
Android 安装包优化动态库打包配置 ( “armeabi-v7a“, “arm64-v8a“, “x86“, “x86_64“ APK 打包 CPU 指令集配置 | NDK 完整配置参考 )(代