Java 动态代理实现自定义连接池

源码及配置文件:https://download.csdn.net/download/u010452388/10394985,有问题可以留言沟通哈

一 为什么要用连接池?

1.1 优化前

1.当jdbc程序每次访问数据库都需要创建一个新的连接,访问完毕之后,需要释放资源。
2.那么在这样的一个过程中,连接的创建和销毁所消耗的资源是远远大于我们发送sql并执行的时间的。
3.基于这样的情况,我们发现我们的jdbc程序将大量的资源浪费在了连接的创建和销毁上
所以我们要对这样的结构进行优化

1.2 优化后

首先创建一定数量的连接,然后放到指定的地方。当我们需要获取连接的时候,直接从指定的地方获取。用完了,我们再将连接放回去。这样就能首先我们连接的回收利用。并且不用花费时间在创建和销毁连接上。

二、动态代理(核心)

1、调用Connection的close方法,不是关闭连接,而是将连接还给连接池。
2、使用动态代理可以重写类的方法,
动态代理作用:不改变原类的代码与不使用继承就可以对类的功能 进行增强。

三、数据库连接池实现的步骤

1我们自定义的数据库连接池需要实现java.sql.DataSource这个接口。这是sun公司规定。
备注: DriverManager 是JDBC 1.0规范,可以创建连接,但是不支持连接池
DataSource是JDBC 2.0规范,支持连接池
2.我们使用linkedList来存放连接。当做连接池。(想想为什么用LinkedList而不用ArrayList? 下面代码注释有解释)
3.在dataSource的构造方法中初始化数据库连接池。向池子中创建一定数量的数据库连接对象。
4.重写DataSource这个接口中的getConnection方法。注意:这个方法是从我们数据库连接池中获取数据,从连接池中获取之后需要将池子中的对象给删掉
5.提供释放资源的方法。注意:这个释放资源是将连接放回连接池中,而不是关闭连接。

四、代码实现步骤

4.1 mysql数据表

create database day05;
use day05;

create table emp(
    id int primary key auto_increment,
    name varchar(50),
    city varchar(50)
);
insert into emp values(null, '刘备', '北京');
insert into emp values(null, '关羽', '上海');
insert into emp values(null, '张飞', '广州');

这里写图片描述

4.2 Jdbc.properties配置文件

driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/day05
user=root
pwd=123

4.3 编写静态代码块

当类加载的时候会执行静态代码块的内容。并完成以下两件事:
1. 读取配置文件信息
2. 注册mysql驱动

public static String driverClass = null;
    public static String url = null;
    public static String user = null;
    public static String pwd = null;
    // 注册驱动
    static {
        //创建配置文件对象
        Properties p = new Properties();
        try {
            //加载src下的配置文件信息
            p.load(new FileInputStream("src/jdbc.properties"));
            driverClass = p.getProperty("driverClass");
            url = p.getProperty("url");
            user = p.getProperty("user");
            pwd = p.getProperty("pwd");
            // 注册驱动
            Class.forName(driverClass);
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

4.4 编写构造函数代码

当创建对象的时候,执行此代码内容,完成一下内容:
1.创建拥有5个连接的连接池

// 声明连接池变量,由于操作连接池主要是增加,删除操作,
//LinkedList是增删块,查询慢;ArrayList增删慢,查询块,所以这里选用LinkedList
    private LinkedList<Connection> pools = new LinkedList<Connection>();
    // 创建对象时,连接池中就有5个连接了
    public MyDataSource() throws SQLException {
        for (int i = 0; i < 5; i++) {
            Connection conn = DriverManager.getConnection(url, user, pwd);
            // 这里添加的时候添加到第一个,后面获取的时候,获取最后一个
            pools.addFirst(conn);
        }
        System.out.println("连接池初始化:"+pools.size());
    }

4.5 关键点-动态代理重写close方法

此步骤中要对Connection的close方法进行重写,但是其他的方法应该保持原有的功能,该怎么处理?
我们可以用继承去重写,但是这里Connection是一个接口,无法继承,所以我们可以使用动态代理实现close方法,即当调用者调用close方法关闭连接的时候,实际上是将连接放回到连接池,而不是关闭连接

@Override
    public Connection getConnection() throws SQLException {
        // 获取的时候,获取最后一个连接
        final Connection conn = pools.removeLast();
        System.out.println("获得连接后,连接池有:"+pools.size());
        ClassLoader loader = conn.getClass().getClassLoader();
        Class<?>[] interfaces = conn.getClass().getInterfaces();
        InvocationHandler h = new InvocationHandler() {
            //对conn对象的方法进行重写
            @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判断conn对象执行的方法是不是close方法,如果是,则将连接返回到连接池,而不是关闭连接
                if("close".equals(method.getName())){
                    pools.addFirst(conn);
                    System.out.println("关闭后连接池有:"+pools.size());
                    return null;
                }else{
                    return method.invoke(conn, args);
                }
            }
        };
Connection proxyConn = (Connection) Proxy.newProxyInstance(loader, interfaces, h);
        return proxyConn;
    }

4.6 自定义连接池完整代码

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.Properties;
import java.util.logging.Logger;

import javax.sql.DataSource;

public class MyDataSource implements DataSource {
    public static String driverClass = null;
    public static String url = null;
    public static String user = null;
    public static String pwd = null;
    // 注册驱动
    static {
        //创建配置文件对象
        Properties p = new Properties();
        try {
            //加载src下的配置文件信息
            p.load(new FileInputStream("src/jdbc.properties"));
            driverClass = p.getProperty("driverClass");
            url = p.getProperty("url");
            user = p.getProperty("user");
            pwd = p.getProperty("pwd");
            // 注册驱动
            Class.forName(driverClass);
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

// 声明连接池变量,由于操作连接池主要是增加,删除操作,
//LinkedList比ArrayList增删快,所以这里选用LinkedList
    private LinkedList<Connection> pools = new LinkedList<Connection>();
    // 创建对象时,连接池中就有5个连接了
    public MyDataSource() throws SQLException {
        for (int i = 0; i < 5; i++) {
            Connection conn = DriverManager.getConnection(url, user, pwd);
            // 这里添加的时候添加到第一个,后面获取的时候,获取最后一个
            pools.addFirst(conn);
        }
        System.out.println("连接池初始化:"+pools.size());
    }

    @Override
    public Connection getConnection() throws SQLException {
        // 获取的时候,获取最后一个连接
        final Connection conn = pools.removeLast();
        System.out.println("获得连接后,连接池有:"+pools.size());
        ClassLoader loader = conn.getClass().getClassLoader();
        Class<?>[] interfaces = conn.getClass().getInterfaces();
        InvocationHandler h = new InvocationHandler() {
            //对conn对象的方法进行重写
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判断conn对象执行的方法是不是close方法,如果是,则将连接返回到连接池,而不是关闭连接
                if("close".equals(method.getName())){
                    pools.addFirst(conn);
                    System.out.println("关闭后连接池有:"+pools.size());
                    return null;
                }else{
                    return method.invoke(conn, args);
                }
            }
        };
        Connection proxyConn = (Connection) Proxy.newProxyInstance(loader, interfaces, h);
        return proxyConn;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {
        // TODO Auto-generated method stub

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

}

4.7 测试Demo完整代码


import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class TestDemo {

    public static void main(String[] args) {
        ResultSet resultSet=null;
        PreparedStatement statement=null;
        Connection conn=null;
        try {
//对象创建好之后,两件事已经完成:1.注册驱动已完成 2.拥有5个连接的连接池已经创建完成
            MyDataSource myDataSource = new MyDataSource();
            //从连接池中获取连接
            conn = myDataSource.getConnection();
            String sql="select * from emp";
            statement = conn.prepareStatement(sql);
            resultSet = statement.executeQuery();
            while(resultSet.next()){
                String id = resultSet.getString("id");
                String name = resultSet.getString("name");
                String city = resultSet.getString("city");
                System.out.println(id+"="+name+"="+city);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally{
            try {
                resultSet.close();
                statement.close();
                conn.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    }

}

五、文件目录

这里写图片描述

六、测试结果

这里写图片描述

源码及配置文件:https://download.csdn.net/download/u010452388/10394985,有问题可以给我留言哈

猜你喜欢

转载自blog.csdn.net/u010452388/article/details/80210975