数据库连接是个耗时操作.对数据库连接的高效管理影响应用程序的性能指标.
数据库连接池正是针对这个问题提出来的.
数据库连接池负责分配,管理和释放数据库连接.它允许应用程序重复使用一个现有的数据路连接,而不需要每次重新建立一个新的连接,利用数据库连接池将明显提升对数据库操作的性能.

数据库连接池技术方案:
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秒的逻辑没写
下面测试自己写的连接池
新建模块 引入依赖


不想玩了









![[kingbase运维之奇怪的现象]](https://img-blog.csdnimg.cn/e490cd4982e34efd9e8fbc17c56f0e22.jpeg#pic_center)









