1. 自定义连接池

2021-10-12 16:16:59 浏览数 (2)

1. 自定义连接池

连接池概念

1. 为什么要使用连接池

Connection对象在JDBC使用的时候就会去创建一个对象,使用结束以后就会将这个对象给销毁了(close).每次创建和销毁对象都是耗时操作.

这个时候,我们就需要使用连接池对其进行优化.

程序初始化的时候,初始化多个连接,将多个连接放入到池(集合)中.每次获取的时候,都可以直接从连接池中进行获取.使用结束以后,将连接归还到池中.

2. 生活里面的连接池例子

  • 老方式: 下了地铁需要骑车, 跑去生产一个, 然后骑完之后,直接把车销毁了.
  • 连接池方式 摩拜单车: 骑之前, 有一个公司生产了很多的自行车, 下了地铁需要骑车, 直接扫码使用就好了, 然后骑完之后, 还回去。这样就可以避免每个人都去弄个自行车,减少了消耗。

3. 连接池原理

img

  1. 程序一开始就创建一定数量的连接,放在一个容器(集合)中,这个容器称为连接池。
  2. 使用的时候直接从连接池中取一个已经创建好的连接对象, 使用完成之后 归还到池子
  3. 如果池子里面的连接使用完了, 还有程序需要使用连接, 先等待一段时间(eg: 3s), 如果在这段时间之内有连接归还, 就拿去使用; 如果还没有连接归还, 新创建一个, 但是新创建的这一个不会归还了
  4. 集合选择LinkedList
    • 增删比较快
    • LinkedList里面的removeFirst()和addLast()方法和连接池的原理吻合

下面我们来写几个示例,演示一下连接池的基础用法。

自定义连接池-初级版本

1.目标

根据连接池的原理, 使用 LinkedList 自定义连接池

2.分析

  1. 创建一个类 MyDataSource, 定义一个集合 LinkedList
  2. 程序 初始化 的时候, 创建 5个连接 存到 LinkedList
  3. 定义 getConnection() 从 LinkedList 取出 Connection 返回
  4. 定义 addBack() 方法归还 Connection 到 LinkedList

3.实现

3.1 创建一个类 MyDataSource, 定义一个集合 LinkedList
代码语言:javascript复制
public class MyDataSource {

    // 定义一个集合 LinkedList 用来存储连接
    LinkedList<Connection> connectPool = new LinkedList<>();
    
}
3.2 程序 初始化 的时候, 创建 5个连接 存到 LinkedList
代码语言:javascript复制
public class MyDataSource {

    // 定义一个集合 LinkedList 用来存储连接
    LinkedList<Connection> connectPool = new LinkedList<>();
    
    // 程序 初始化 的时候, 创建 5个连接 存到 LinkedList
    public MyDataSource() {
        // 创建5个连接
        for (int i = 0; i < 5; i  ) {
            try {
                //创建连接
                String url = "jdbc:mysql://localhost:3306/test";
                String user = "root";
                String password = "Li*******密码*******0";
                Connection conn = DriverManager.getConnection(url, user, password);
                //将连接添加到connectionPool中
                connectPool.add(conn);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    
}
3.3 定义 getConnection() 从 LinkedList 取出 Connection 返回
代码语言:javascript复制
// 定义 getConnection() 从 LinkedList 取出 Connection 返回
public Connection getConnection(){
    //从容器中获取连接
    Connection conn = connectPool.removeFirst();
    return conn;
}
3.4 定义 addBack() 方法归还 Connection 到 LinkedList
代码语言:javascript复制
// 定义 addBack() 方法归还 Connection 到 LinkedList
public void addBack(Connection connection){
    connectPool.addLast(connection);
}
3.5 当前完成代码
代码语言:javascript复制
public class MyDataSource {

    // 定义一个集合 LinkedList 用来存储连接
    LinkedList<Connection> connectPool = new LinkedList<>();
    
    // 程序 初始化 的时候, 创建 5个连接 存到 LinkedList
    public MyDataSource() {
        // 创建5个连接
        for (int i = 0; i < 5; i  ) {
            try {
                //创建连接
                String url = "jdbc:mysql://localhost:3306/test";
                String user = "root";
                String password = "Li*************10";
                Connection conn = DriverManager.getConnection(url, user, password);
                //将连接添加到connectionPool中
                connectPool.add(conn);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    // 定义 getConnection() 从 LinkedList 取出 Connection 返回
    public Connection getConnection(){
        //从容器中获取连接
        Connection conn = connectPool.removeFirst();
        return conn;
    }

    // 定义 addBack() 方法归还 Connection 到 LinkedList
    public void addBack(Connection connection){
        connectPool.addLast(connection);
    }

}
3.6 编写测试方法,创建多个连接测试使用如下:

image-20210125005449154

image-20210125005526673

可以看到能够正常获取到 5 个 数据库连接,那么如果再创建多一个连接呢?

3.7 尝试创建超过连接池的数量

image-20210125005728874

image-20210125005847680

可以看到,当我们执行到 第 6 个连接的时候,则会报错:提示没有该元素。

3.8 异常的原因:由于连接池的 LinkedList 只有 5 个元素,当获取到第 6 个的时候,由于 LinkedList 的元素都被清空了,再去获取的时候会执行 removeFirst() 方法,导致报错。

image-20210125010032886

解决的办法:如果要避免异常,那么就要当获取的连接数 超过 连接池的数量的时候,则重新创建一个连接,避免异常。

修改如下:

代码语言:javascript复制
// 定义 getConnection() 从 LinkedList 取出 Connection 返回
public Connection getConnection() {
    //从容器中获取连接
    Connection conn = null;
    if (connectPool.size() > 0) {
        //1.当连接池存在连接,那么则直接返回连接
        conn = connectPool.removeFirst();
    } else {
        //2.当连接池不存在连接,则重新创建一个新的连接返回
        //创建连接
        String url = "jdbc:mysql://localhost:3306/test";
        String user = "root";
        String password = "Li**************0";
        try {
            conn = DriverManager.getConnection(url, user, password);
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
    }
    return conn;
}
3.9 再次尝试获取 6 个连接,查看能否正常获取连接

image-20210125010614182

3.10 测试在使用完毕连接之后,将连接重新设置回连接池中

image-20210125010939216

可以看到 addBack() 方法能够将连接放入 连接池,但是却把 额外新增的 第 6 个连接也放入了连接池。这就不太好了。

但是我们可以发现,addBack() 方法由于无法识别这个连接是原来就创建好,还是额外新增的,导致都会将其放入到连接池中。

另外,也存在一个问题,那就是当我们希望切换使用其他连接池的时候,由于我们的连接池是自定义的,耦合性太高,不方便项目切换使用其他连接池。

3.11 完整示例代码
代码语言:javascript复制
/**
 *
 * 自定义连接池的第一个版本
 * 1. 创建一个容器,存放连接
 * 2. 默认往容器中存放5个连接
 *    在构造函数中编写代码
 * 3. 提供一个方法,让调用者获取连接
 * 4. 提供一个方法,让调用者归还连接
 *
 * 当前第一个版本存在的问题:
 * 1. 新创建的连接(原本没有在连接池中的连接)也会归还回连接池
 * 2. 连接池使用的耦合性太高了,不便于以后项目切换连接池
 * 
 * @author Aron.li
 * @date 2021/1/25 0:39
 */
public class MyDataSource {

    // 定义一个集合 LinkedList 用来存储连接
    LinkedList<Connection> connectPool = new LinkedList<>();

    // 程序 初始化 的时候, 创建 5个连接 存到 LinkedList
    public MyDataSource() {
        // 创建5个连接
        for (int i = 0; i < 5; i  ) {
            try {
                //创建连接
                String url = "jdbc:mysql://localhost:3306/test";
                String user = "root";
                String password = "Lijw********610";
                Connection conn = DriverManager.getConnection(url, user, password);
                //将连接添加到connectionPool中
                connectPool.add(conn);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    // 定义 getConnection() 从 LinkedList 取出 Connection 返回
    public Connection getConnection() {
        //从容器中获取连接
        Connection conn = null;
        if (connectPool.size() > 0) {
            //1.当连接池存在连接,那么则直接返回连接
            conn = connectPool.removeFirst();
        } else {
            //2.当连接池不存在连接,则重新创建一个新的连接返回
            //创建连接
            String url = "jdbc:mysql://localhost:3306/test";
            String user = "root";
            String password = "Lijw**********!610";
            try {
                conn = DriverManager.getConnection(url, user, password);
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e.getMessage());
            }
        }
        return conn;
    }

    // 定义 addBack() 方法归还 Connection 到 LinkedList
    public void addBack(Connection connection) {
        connectPool.addLast(connection);
    }

    // 编写测试方法,创建多个连接测试使用如下:
    @Test
    public void test01() {
        // 测试创建5个连接
        MyDataSource dataSource = new MyDataSource();

        Connection connection1 = dataSource.getConnection();
        Connection connection2 = dataSource.getConnection();
        Connection connection3 = dataSource.getConnection();
        Connection connection4 = dataSource.getConnection();
        Connection connection5 = dataSource.getConnection();
        // 超出连接池的数量
        Connection connection6 = dataSource.getConnection();

        // 测试在使用完毕连接之后,将连接重新设置回连接池中
        dataSource.addBack(connection1);
        dataSource.addBack(connection2);
        dataSource.addBack(connection3);
        dataSource.addBack(connection4);
        dataSource.addBack(connection5);
        dataSource.addBack(connection6);
    }
}

自定义连接池-进阶版本

1.目标

实现datasource完成自定义连接池

2.分析

在初级版本版本中, 我们定义的方法是getConnection(). 因为是自定义的.如果改用李四的自定义的连接池,李四定义的方法是getAbc(), 那么我们的源码就需要修改, 这样不方便维护. 所以sun公司定义了一个接口datasource,让自定义连接池有了规范

3. datasource接口概述

Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商(用户)需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!

img

4.代码实现

4.1 创建 MyDataSource2 并且实现 DataSource 接口
代码语言:javascript复制
/**
 * @author Aron.li
 * @date 2021/1/25 1:16
 */
public class MyDataSource2 implements DataSource {
    
    @Override
    public Connection getConnection() throws SQLException {
        return null;
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        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 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;
    }
}

可以看到这个 DataSource 接口需要实现很多的方法,而我们自定义连接池的话不需要实现太多的功能,所以只要实现几个必要的方法即可。

4.2 创建无参构造器,用于创建 5 个初始连接

image-20210125082652663

代码语言:javascript复制
public class MyDataSource2 implements DataSource {

    // 定义连接池
    private LinkedList<Connection> connectionsPool =  new LinkedList<>();

    // 无参构造器,用于创建 5 个初始连接
    public MyDataSource2() {
        // 创建5个连接
        for (int i = 0; i < 5; i  ) {
            try {
                //创建连接
                String url = "jdbc:mysql://localhost:3306/test";
                String user = "root";
                String password = "Lij*****************10";
                Connection conn = DriverManager.getConnection(url, user, password);
                //将连接添加到connectionPool中
                connectionsPool.add(conn);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
4.3 重写 getConnection() 方法,提供获取连接

image-20210125082829285

代码语言:javascript复制
public class MyDataSource2 implements DataSource {

    // 定义连接池
    private LinkedList<Connection> connectionsPool =  new LinkedList<>();

    // 无参构造器,用于创建 5 个初始连接
    public MyDataSource2() {
        // 创建5个连接
        for (int i = 0; i < 5; i  ) {
            try {
                //创建连接
                String url = "jdbc:mysql://localhost:3306/test";
                String user = "root";
                String password = "Lij*****************10";
                Connection conn = DriverManager.getConnection(url, user, password);
                //将连接添加到connectionPool中
                connectionsPool.add(conn);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public Connection getConnection() throws SQLException {
        //从容器中获取连接
        Connection conn = null;
        if (connectionsPool.size() > 0) {
            //1.当连接池存在连接,那么则直接返回连接
            conn = connectionsPool.removeFirst();
        } else {
            //2.当连接池不存在连接,则重新创建一个新的连接返回
            //创建连接
            String url = "jdbc:mysql://localhost:3306/test";
            String user = "root";
            String password = "Lij*****************10";
            try {
                conn = DriverManager.getConnection(url, user, password);
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e.getMessage());
            }
        }
        return conn;
    }
4.4 实现DataSource接口后,addBack()不能调用了.

image-20210125083047911

那么我们应该怎么将线程加入回线程池呢?

这个时候我们只能默认使用连接的 close() 方法来关闭连接,可以考虑一下能否重写这个 close() 方法呢?

4.5 测试使用自定义连接池

image-20210125084455410

代码语言:javascript复制
/**
 * @author Aron.li
 * @date 2021/1/25 1:16
 */
public class MyDataSource2 implements DataSource {

    // 定义连接池
    private LinkedList<Connection> connectionsPool =  new LinkedList<>();

    // 无参构造器,用于创建 5 个初始连接
    public MyDataSource2() {
        // 创建5个连接
        for (int i = 0; i < 5; i  ) {
            try {
                //创建连接
                String url = "jdbc:mysql://localhost:3306/test";
                String user = "root";
                String password = "L*****************0";
                Connection conn = DriverManager.getConnection(url, user, password);
                //将连接添加到connectionPool中
                connectionsPool.add(conn);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public Connection getConnection() throws SQLException {
        //从容器中获取连接
        Connection conn = null;
        if (connectionsPool.size() > 0) {
            //1.当连接池存在连接,那么则直接返回连接
            conn = connectionsPool.removeFirst();
        } else {
            //2.当连接池不存在连接,则重新创建一个新的连接返回
            //创建连接
            String url = "jdbc:mysql://localhost:3306/test";
            String user = "root";
            String password = "L*****************0";
            try {
                conn = DriverManager.getConnection(url, user, password);
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e.getMessage());
            }
        }
        return conn;
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        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 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;
    }

    // 编写测试方法,创建多个连接测试使用如下:
    @Test
    public void test02() throws SQLException {

        // 测试创建5个连接
        MyDataSource2 dataSource = new MyDataSource2();

        Connection connection1 = dataSource.getConnection();
        Connection connection2 = dataSource.getConnection();
        Connection connection3 = dataSource.getConnection();
        Connection connection4 = dataSource.getConnection();
        Connection connection5 = dataSource.getConnection();
        // 超出连接池的数量
        Connection connection6 = dataSource.getConnection();

        // 关闭连接
        connection1.close();
        connection2.close();
        connection3.close();
        connection4.close();
        connection5.close();
        connection6.close();

    }
}

那么下面我们要考虑一下如何将创建好的连接 加入回 连接池呢?

5.小结

5.1编写连接池遇到的问题
  • 实现DataSource接口后,addBack()不能调用了.
  • 能不能不引入新的api,直接调用之前的connection.close(),但是这个close不是关闭,是归还
5.2解决办法
  • 继承
    • 条件:可以控制父类, 最起码知道父类的名字
  • 装饰者模式
    • 增强类和被增强类实现的是同一个接口
    • 增强类里面要拿到被增强类的引用
    • 作用:改写已存在的类的某个方法或某些方法
    • 条件:
  • 动态代理

自定义连接池-终极版本

1.目标

在上面的代码中,存在一个无法将连接 connection 返回连接池的方法,下面我们可以使用装饰者模式改写connectionclose()方法, 让connection可以通过close()方法 归还到连接池中。

2.自定义连接池终极版本

2.1分析

增强connection的close()方法, 其它的方法逻辑不改

image-20191203101709735

  1. 创建WrapperConnection实现Connection
  2. 在WrapperConnection里面需要得到被增强的connection对象(通过构造方法传进去)
  3. 改写close()的逻辑, 变成归还
  4. 其它方法的逻辑, 还是调用被增强connection对象之前的逻辑
2.2 创建装饰者 WrapperConnection 类
2.2.1 创建 装饰者 WrapperConnection 类 实现 Connection 方法,用来重写 close() 方法,提供归还连接池

image-20210125085439933

2.2.3 同时还要实现几个我们会用到的方法

image-20210125085613127

2.2.4 装饰着类代码
代码语言:javascript复制
/**
 * @author Aron.li
 * @date 2021/1/25 8:51
 */
public class WrapperConnection implements Connection {

    private Connection connection; // 定义连接
    private LinkedList<Connection> connectionPool; // 定义传入要操作的连接池

    // 构造器: 用来传入 connection 连接 以及 连接池
    public WrapperConnection(Connection connection, LinkedList<Connection> connectionPool) {
        this.connection = connection;
        this.connectionPool = connectionPool;
    }

    @Override
    public void close() throws SQLException {
        //将当前这个连接归还回原来那个连接池容器
        connectionPool.addLast(this);
    }

    @Override
    public Statement createStatement() throws SQLException {
        return connection.createStatement();
    }

    @Override
    public PreparedStatement prepareStatement(String sql) throws SQLException {
        return connection.prepareStatement(sql);
    }

 //...其他方法省略
}   
2.3 修改连接池 MyDataSource03, 使用装饰 WrapperConnection 类来创建连接

image-20210125233041401

因为装饰后的连接被我们重写了 close()方法,所以将连接池默认创建的连接为装饰后的连接。

image-20210125234227404

代码如下:

代码语言:javascript复制
public class MyDataSource3 implements DataSource {

    // 定义连接池
    private LinkedList<Connection> connectionsPool =  new LinkedList<>();

    // 无参构造器,用于创建 5 个初始连接
    public MyDataSource3() {
        // 创建5个连接
        for (int i = 0; i < 5; i  ) {
            try {
                //创建连接
                String url = "jdbc:mysql://localhost:3306/test";
                String user = "root";
                String password = "Li******************0";
                //创建一个装饰后的连接
                Connection conn = new WrapperConnection(DriverManager.getConnection(url, user, password),connectionsPool);
                //将连接添加到connectionPool中
                connectionsPool.add(conn);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public Connection getConnection() throws SQLException {
        //从容器中获取连接
        Connection conn = null;
        if (connectionsPool.size() > 0) {
            //1.当连接池存在连接,那么则直接返回连接
            conn = connectionsPool.removeFirst();
        } else {
            //2.当连接池不存在连接,则重新创建一个新的连接返回
            //创建连接
            String url = "jdbc:mysql://localhost:3306/test";
            String user = "root";
            String password = "Li******************0";
            try {
                conn = DriverManager.getConnection(url, user, password);
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e.getMessage());
            }
        }
        return conn;
    }
    
    // 省略其他方法。。。
2.4 测试使用自定义连接池

image-20210125234613999

image-20210125234703981

2.5 代码
代码语言:javascript复制
/**
 * @author Aron.li
 * @date 2021/1/25 1:16
 */
public class MyDataSource3 implements DataSource {

    // 定义连接池
    private LinkedList<Connection> connectionsPool =  new LinkedList<>();

    // 无参构造器,用于创建 5 个初始连接
    public MyDataSource3() {
        // 创建5个连接
        for (int i = 0; i < 5; i  ) {
            try {
                //创建连接
                String url = "jdbc:mysql://localhost:3306/test";
                String user = "root";
                String password = "Lijw@********!610";
                //创建一个装饰后的连接
                Connection conn = new WrapperConnection(DriverManager.getConnection(url, user, password),connectionsPool);
                //将连接添加到connectionPool中
                connectionsPool.add(conn);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public Connection getConnection() throws SQLException {
        //从容器中获取连接
        Connection conn = null;
        if (connectionsPool.size() > 0) {
            //1.当连接池存在连接,那么则直接返回连接
            conn = connectionsPool.removeFirst();
        } else {
            //2.当连接池不存在连接,则重新创建一个新的连接返回
            //创建连接
            String url = "jdbc:mysql://localhost:3306/test";
            String user = "root";
            String password = "Lij********10";
            try {
                conn = DriverManager.getConnection(url, user, password);
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e.getMessage());
            }
        }
        return conn;
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        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 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;
    }

    // 编写测试方法,创建多个连接测试使用如下:
    @Test
    public void test02() throws SQLException {

        // 测试创建5个连接
        MyDataSource3 dataSource = new MyDataSource3();

        Connection connection1 = dataSource.getConnection();
        Connection connection2 = dataSource.getConnection();
        Connection connection3 = dataSource.getConnection();
        Connection connection4 = dataSource.getConnection();
        Connection connection5 = dataSource.getConnection();
        // 超出连接池的数量
        Connection connection6 = dataSource.getConnection();

        // 关闭连接
        connection1.close();
        connection2.close();
        connection3.close();
        connection4.close();
        connection5.close();
        connection6.close();

    }
}

3.小结

  1. 创建一个MyConnection实现Connection
  2. 在MyConnection得到被增强的connection对象
  3. 改写MyConnection里面的close()方法的逻辑为归还
  4. MyConnection里面的其它方法 调用被增强的connection对象之前的逻辑
  5. 在MyDataSource03的getConnection()方法里面 返回了myConnection

自定义连接池扩展版本-使用动态代理

在上面我们已经使用 装饰模式 解决了不同连接的不同 close() 问题,下面我们再来看看 动态代理。

使用动态代理创建Connection对象的代理对象,增强Connection的close方法

image-20210125235107930

代码语言:javascript复制
public class MyDataSource4 implements DataSource {

    // 定义连接池
    private LinkedList<Connection> connectionsPool =  new LinkedList<>();

    // 无参构造器,用于创建 5 个初始连接
    public MyDataSource4() {
        // 创建5个连接
        for (int i = 0; i < 5; i  ) {
            try {
                //创建连接
                String url = "jdbc:mysql://localhost:3306/test";
                String user = "root";
                String password = "Lij******************0";
                // 创建连接
                Connection connection = DriverManager.getConnection(url, user, password);
                ClassLoader classLoader = connection.getClass().getClassLoader();
                //创建动态代理对象
                Connection connectionProxy = (Connection) Proxy.newProxyInstance(classLoader, new Class[]{Connection.class}, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //判断执行方法是否是close方法,如果是,则增强,如果不是则执行被代理者原本的方法
                        if (method.getName().equals("close")) {
                            //增强close
                            //将当前这个连接对象,添加到容器中
                            connectionsPool.addLast((Connection) proxy);
                            return null;
                        }
                        //不需要增强的方法,就执行原本的方法
                        return method.invoke(connection,args);
                    }
                });

                //将连接添加到connectionPool中
                connectionsPool.add(connectionProxy);

            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public Connection getConnection() throws SQLException {
        //从容器中获取连接
        Connection conn = null;
        if (connectionsPool.size() > 0) {
            //1.当连接池存在连接,那么则直接返回连接
            conn = connectionsPool.removeFirst();
        } else {
            //2.当连接池不存在连接,则重新创建一个新的连接返回
            //创建连接
            String url = "jdbc:mysql://localhost:3306/test";
            String user = "root";
            String password = "Lij******************0";
            try {
                conn = DriverManager.getConnection(url, user, password);
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e.getMessage());
            }
        }
        return conn;
    }

0 人点赞