数据库连接是个耗时操作.对数据库连接的高效管理影响应用程序的性能指标.
数据库连接池正是针对这个问题提出来的.
数据库连接池负责分配,管理和释放数据库连接.它允许应用程序重复使用一个现有的数据路连接,而不需要每次重新建立一个新的连接,利用数据库连接池将明显提升对数据库操作的性能.
数据库连接池技术方案:
1.C3P0
2.DBCP
3.Proxool
4.Tomcat jdbc Oppl
5.BoneCP
6.Druid
7.HikariCP
数据库连接池属于一种池化技术:
常见的有:http访问(httpclient),redis访问(redisPool),线程(线程池)等
新建个空项目
可能是版本原因idea创建空项目总要重启一下
设置下maven和encoding
新建模块
DataSource是JDK里的规范,用来专门放连接的.是一个连接工厂
自定义一个接口
里面需要实现的两个方法是: 其他的父接口的其他方式可以暂时不管
这样基础结构就有了
package com.pool;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* @author hrui
* @date 2023/9/10 3:06
*/
public class MyAbstractDataSource implements MyDataSourceInterface{
private String url;
private String driver;
private String username;
private String password;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public Connection getConnection() throws SQLException {
return getConnection(username,password);
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return doGetConnection(username,password);
}
private Connection doGetConnection(String username, String password) throws SQLException {
Connection connection = DriverManager.getConnection(url, username, password);
return connection;
}
}
上面三个方法,都是获得连接
下面用动态代理方式实现对数据库连接的代理
package com.pool;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
/**
* 采用动态代理实现对数据库连接的代理
* @author hrui
* @date 2023/9/10 3:45
*/
public class ConnectionProxy implements InvocationHandler {
//真正连接
private Connection realConnection;
//代理连接
private Connection proxyConnection;
//数据源对象
private MyDataSource myDataSource;
public ConnectionProxy(Connection realConnection, MyDataSource myDataSource) {
//初始化真实连接和数据源
this.realConnection = realConnection;
this.myDataSource = myDataSource;
//初始化代理连接...需要一个代理对象 JDK动态代理
this.proxyConnection= (Connection)Proxy.newProxyInstance(Connection.class.getClassLoader(),
new Class<?>[]{Connection.class},
this);
}
public Connection getRealConnection() {
return realConnection;
}
public void setRealConnection(Connection realConnection) {
this.realConnection = realConnection;
}
public Connection getProxyConnection() {
return proxyConnection;
}
public void setProxyConnection(Connection proxyConnection) {
this.proxyConnection = proxyConnection;
}
public MyDataSource getMyDataSource() {
return myDataSource;
}
public void setMyDataSource(MyDataSource myDataSource) {
this.myDataSource = myDataSource;
}
/**
* 当调用Connection对象里面的任何方法时,该方法会进行拦截
* 主要目的为了拦截Connection的close方法,当关闭时进行拦截,将Connection对象放入连接池中
* 其他方法无需拦截
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取当前调用了Connection对象的什么方法
String methodName=method.getName();
if(methodName.equals("close")){
//TODO 把连接放入连接池
return null;
}else{
//其他方法
return method.invoke(realConnection, args);
}
}
}
}
下面写个类继承MyAbstractDataSource 在里面放入连接池
package com.pool;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* 数据源连接池
* @author hrui
* @date 2023/9/10 4:15
*/
public class MyDataSource extends MyAbstractDataSource{
//空闲连接
private final List<ConnectionProxy> idleConnections=new ArrayList<>();
//激活的连接池
private final List<ConnectionProxy> activeConnections=new ArrayList<>();
//下面三个可以抽取放到父类抽象类中,由用户配置
//最大的连接数量
private int poolMaxActiveConnection=10;
//最大空闲连接数
private int poolMaxIdIeConnection=5;
//当连接都在使用时候,最大检出时间(等待时间),毫秒
private int poolTimeToWait=20000;
//用于同步监视器对象
private final Object monitor=new Object();
//用于同步监视器对象(关闭)
//private final Object watch=new Object();
/**
* 重写:用于获取代理连接
* @return
* @throws SQLException
*/
@Override
public Connection getConnection() throws SQLException {
ConnectionProxy connectionProxy=getConnectionProxy(super.getUsername(),super.getPassword());
//返回代理连接
return super.getConnection();
}
/**
* 获取代理连接
* @param username
* @param password
* @return
*/
private ConnectionProxy getConnectionProxy(String username,String password) throws SQLException {
//TODO
//是否需要等待
boolean wait=false;
ConnectionProxy connectionProxy=null;
//刚开始没有连接
while(connectionProxy==null){
synchronized (monitor){
//看连接池有没有空闲连接,如果不为空,直接获取连接
if(!idleConnections.isEmpty()){
connectionProxy=idleConnections.remove(0);
//如果是空的
}else{
//没有空闲连接,那么需要获取新的连接(创建连接)
//这里先判断最大连接数是不是小于配置数量 10
if(activeConnections.size()<poolMaxActiveConnection){
//创建新连接 需要传入真实连接
connectionProxy=new ConnectionProxy(super.getConnection(),this);
}
//否则需要等待20秒 上面定义了poolTimeToWait=20000
}
}
if(!wait){
wait=true;
}
if(connectionProxy==null){
try {
monitor.wait(poolTimeToWait);
} catch (InterruptedException e) {
e.printStackTrace();
//万一等待被线程打断,退出一下
break;
}
}
}
if(connectionProxy!=null){
//连接对象不是空,说明已经拿到连接了,放入容器
activeConnections.add(connectionProxy);
}
return connectionProxy;
}
//用于关连接
//不是把连接关系,而是还给连接池
public void closeConnection(ConnectionProxy connectionProxy){
synchronized (monitor){
//最大连接(激活连接)里删除
activeConnections.remove(connectionProxy);
//如果空闲连接<定义的数量则放入空闲连接
if(idleConnections.size()<poolMaxIdIeConnection){
idleConnections.add(connectionProxy);
}
//通知一下,唤醒上面哪个等待获取连接的线程
monitor.notify();
}
}
}
那么代理对象中把连接还给连接池的方法也有了
就是当所有连接用完了,等待20秒的逻辑没写
下面测试自己写的连接池
新建模块 引入依赖
不想玩了