Spring framework :基于 jdk 动态代理实现连接池复用

news2024/11/14 23:40:02

前言

在数据库开发中,连接池是一种重要的技术手段,它可以提高数据库连接的复用性和性能。连接池的原理是在应用启动时创建一定数量的数据库连接,并将这些连接保存在一个池中,应用程序需要数据库连接时,从连接池中获取一个连接进行操作,不再频繁地创建和关闭连接。

为了更好地利用连接池并实现连接的复用,我们可以借助JDK动态代理机制来实现连接对象的自动重用。通过在动态代理中拦截连接的获取和归还操作,我们可以对连接对象进行有效地管理,确保每次使用完毕后将连接归还到连接池中,从而实现连接的复用,提高性能和效率。

接下来,我们将结合JDK动态代理机制和连接池技术,实现一个基于动态代理的连接池复用功能。

如果不了解 jdk 动态代理的,可以去回顾一下我的这篇文章: http://t.csdnimg.cn/BJnzh

一、开始学习

1、新建项目,结构如下

2、导入依赖
 
    <!-- spring 的核心依赖 -->
    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.23</version>
        </dependency>
 
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.4.5</version>
        </dependency>
 
         <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>
 
 
    </dependencies>
 3、在 pool 包下新建三个类,ConnectionInvocationHandler、ConnectionPool、ConnUtils

ConnUtils 类


@Slf4j
public class ConnUtils {


    public static Connection getConnection(String url, String name, String password) {
        try {
            return DriverManager.getConnection(url, name, password);
        } catch (SQLException e) {
            throw new RuntimeException("connection erro", e);
        }
    }


}

主要是用来获取数据库连接的工具类

ConnectionPool (连接池)


@Setter
public class ConnectionPool {

    /**
     * 连接池(存放连接的集合)
     */
    private LinkedList<Connection> pool = new LinkedList<>();

    /**
     * 连接属性
     */
    private String url;
    private String name;
    private String password;
    /**
     * 池大小
     */
    private Integer poolSize;


    /**
     * 在构造方法中初始化连接池大小
     *
     */
    public void init() throws SQLException {
        for (int i = 0; i < poolSize; i++) {
            // 1、从数据库获取连接对象
            Connection connection = ConnUtils.getConnection(url,name,password);

            // 2、对连接对象创建代理
            connection = createProxy(connection);

            // 3、将连接对象返回连接池中
            connection.close();

        }
    }

    /**
     * 为连接对象创建代理
     *
     * @param connection
     * @return
     */
    private Connection createProxy(Connection connection) {
        // 创建回调处理器
        ConnectionInvocationHandler connectionInvocationHandler = new ConnectionInvocationHandler(connection,pool);
        // 获取连接对象的所有接口
        Class<?>[] interfaces = new Class[]{Connection.class};
        // 获取类加载器
        ClassLoader loader = ConnectionPool.class.getClassLoader();
        // 创建代理
        connection = (Connection) Proxy.newProxyInstance(loader,interfaces,connectionInvocationHandler);
        return connection;
    }

    /**
     * 从池里获取代理连接
     *
     * @return
     */
    public Connection getConnection() {
        return pool.removeFirst();
    }

    /**
     * 查看连接池的大小
     * @return
     */
    public int size(){
        return pool.size();
    }

}

这个类是一个连接池的实现,用于管理数据库连接对象。连接池的作用是在应用程序初始化时创建一定数量的数据库连接,并将这些连接保存在连接池中,当应用程序需要使用数据库连接时,可以从连接池中获取连接,使用完毕后将连接放回连接池,以便其他线程或请求继续使用。

连接池可以提高数据库的性能和效率,主要有以下几个方面的作用:

  1. 连接的复用:连接池中已经创建好的连接可以被反复利用,避免频繁地创建和关闭连接,从而减少了连接的创建和销毁开销。

  2. 连接的管理:连接池可以对连接进行有效的管理,包括连接的创建、销毁、状态的监控等,确保连接的可用性和稳定性。

  3. 连接的限制:连接池可以设置最大连接数,防止因为连接过多导致数据库负载过高或内存资源耗尽,从而提高系统的稳定性和安全性。

通过使用连接池,可以减少数据库连接的创建和销毁次数,提高数据库访问的效率,同时能够更好地管理和控制数据库连接的使用,确保系统的性能和可靠性。

 ConnectionInvocationHandler类


public class ConnectionInvocationHandler implements InvocationHandler {

    /**
     * 目标对象(被代理对象)
     */
    private Connection connection;

    /**
     * 连接池
     */
    private LinkedList<Connection> pool;

    public ConnectionInvocationHandler(Connection connection, LinkedList<Connection> pool) {
        this.connection = connection;
        this.pool = pool;
    }

    public ConnectionInvocationHandler(Connection connection) {
        this.connection = connection;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 如果当前调用的是 Connection 的 close 方法则将它放回到池中
        if ("close".equals(method.getName())) {
            // 从池的尾部放回去
            // 注意:这里放回池中的必须是代理对象,而不是目标 Connection
            pool.addLast((Connection) proxy);
            return null;
        } else {
            // 除 close 以外的其他方法则正常调用目标对象的行为
            return method.invoke(connection, args);
        }
    }
}

 这是一个代理类,实现了InvocationHandler接口,用于代理连接对象。主要作用是拦截连接对象的close方法,并将其放回连接池中,以便其他线程或请求继续使用。

在代理对象调用close方法时,ConnectionInvocationHandler会将代理对象(即当前正在被使用的连接)放回到连接池中,而不是直接关闭代理对象。这样可以确保连接池中的连接资源得到充分利用,提高应用程序的性能。

在代理对象调用除close以外的方法时,ConnectionInvocationHandler会将方法调用转发给目标对象(即被代理的连接对象),并返回方法的结果。这样可以保证应用程序的正常逻辑,对于使用者来说,无需关心连接对象被代理的具体实现,只需要按照正常的方式使用连接对象即可。

总之,ConnectionInvocationHandler是整个连接池的核心之一,它负责管理连接对象的生命周期,确保连接对象能够得到充分利用和正确释放,保障应用程序的性能和可靠性。

4、在 resources 包下新建一个 db.properties 文件
url = jdbc:mysql://localhost:3306/psm
name = root
password = 123456
poolSize = 5
5、在 config 包下新建一个 AppConfig 类

@Configuration
@PropertySource("classpath:db.properties")
@Slf4j
public class AppConfig {

    @Value("${url}")
    private String url;
    @Value("${name}")
    private String userName;
    @Value("${password}")
    private String password;
    @Value("${poolSize}")
    private Integer poolSize;

    /**
     * 装配连接池
     *
     * @return
     */
    @Bean
    public ConnectionPool connectionPool() throws SQLException {
        log.info("url:" + url);
        log.info("name:" + userName);
        log.info("password:" + password);
        log.info("size:" + poolSize);

        // 创建连接池并设置相关的属性
        ConnectionPool pool = new ConnectionPool();
        pool.setUrl(url);
        pool.setName(userName);
        pool.setPassword(password);
        pool.setPoolSize(poolSize);

        // 初始化连接池
        pool.init();

        return pool;
    }

}

这是一个用于配置连接池的类。通过@Configuration注解将它标记为一个配置类,并使用@PropertySource注解指定了属性文件的位置。

在该类中,使用了@Value注解来注入属性值。根据属性文件中的配置,注入了urlnamepasswordpoolSize等属性值,分别表示数据库的URL、用户名、密码和连接池的大小。

connectionPool()方法中,创建了一个ConnectionPool对象,并通过setter方法设置了相关属性的值。然后调用init()方法初始化连接池,使其预先创建一定数量的数据库连接。

最后将创建好的连接池对象返回,以供其他组件或类进行使用。

整个配置类的作用是将属性文件中的配置值注入到连接池对象中,并初始化连接池,以方便应用程序使用数据库连接。

6、测试

@Slf4j
public class Main {

    public static void main(String[] args) throws SQLException {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        // 从容器中获取连接池
        ConnectionPool connectionPool = context.getBean(ConnectionPool.class);
        log.info("连接数:" + connectionPool.size());
        Connection conn1 = connectionPool.getConnection();
        Connection conn2 = connectionPool.getConnection();
        log.info("连接数:" + connectionPool.size());
        conn1.close();
        log.info("连接数:" + connectionPool.size());
    }
}

运行结果

二、 这个案例是干嘛的

这个案例主要用于配置和初始化连接池,以便在应用程序中高效地管理数据库连接。它具有以下几个作用:

  1. 抽象化数据库连接:通过使用连接池,应用程序可以从简单的直接获取和释放数据库连接的方式转变为通过连接池来获取和释放连接。连接池负责维护一定数量的数据库连接,并在需要时提供连接给应用程序使用。这样可以避免频繁地创建和关闭连接,提高数据库操作的效率。

  2. 提高性能和资源利用率:连接池可以预先创建一定数量的数据库连接,并将其保存在池中。当应用程序需要数据库连接时,可以从连接池中获取连接,而不是每次都创建一个新的连接。这样可以避免了频繁地创建和销毁连接的开销,提高了数据库操作的性能和资源的利用率。

  3. 简化配置和管理:通过将连接池的配置信息(如URL、用户名、密码、连接池大小等)放入属性文件中,并通过配置类进行加载和注入,可以方便地进行配置和管理。这使得连接池的配置可以与应用程序的其他部分分离,易于维护和修改。

总之,这个案例提供了一种在应用程序中配置和初始化连接池的方式,帮助管理数据库连接,提高数据库操作的性能和资源利用率,同时简化了配置和管理的过程。

三、总结

这只是一个利用 jdk 动态代理实现的一个简单的连接池的案例,仅仅知识实现了连接池的复用,连接池中还有很多的 API 方法。

四、gitee 案例

地址:ch17/src/main/java/edu/nf/ch17/pool · qiuqiu/spring-framework - 码云 - 开源中国 (gitee.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1114184.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

智能巡检系统有什么特点和功能?它是如何推动企业高效管理?

随着科技的快速发展&#xff0c;智能化管理逐渐成为各行各业的重要发展方向。巡检工作作为企业管理中的重要环节&#xff0c;智能巡检系统综合平台正逐渐成为关注的焦点。本文将从功能、特点和应用场景三个方面&#xff0c;详细解析“的修”工单管理系统的优势和价值。 一、智能…

什么是美颜sdk?直播实时美颜sdk的工作流程和架构分析

在现代社交媒体和娱乐行业中&#xff0c;直播已经成为了一种受欢迎的娱乐形式&#xff0c;同时实时美颜也变得越来越重要。直播实时美颜SDK的工作流程和架构在这一领域起到了关键作用。本文将深入探讨这些SDK的内部机制&#xff0c;从而理解它们如何为用户提供出色的美颜效果。…

推荐一种更高效的打字输入法——双拼输入法

简介 双拼&#xff08;也称双打&#xff09;是一种建立在拼音输入法基础之上的文字输入方法&#xff0c;可视为全拼的一种改进。它通过将每个汉字拼音的声母和韵母各自映射到某个按键上&#xff0c;使得每个汉字最多用两个按键表示&#xff0c;从而极大地提高了拼音输入法的输…

激光雷达反射率标定可提高自动驾驶道路安全

实现自动驾驶从辅助驾驶阶段到部分自动驾驶阶段&#xff0c;再到完全自动驾驶阶段&#xff0c;自动驾驶技术正在逐步实现。虽然完全自动驾驶技术仍需要时间来进一步发展和完善&#xff0c;但其所带来的可能性是无限的。激光雷达在自动驾驶中的应用非常广泛&#xff0c;它能够提…

请问嵌入式或迁移学习要学什么?

请问嵌入式或迁移学习要学什么&#xff1f; 学习嵌入式和迁移学习是一个很好的方向&#xff0c;尤其是在军I领域。以下是一些你可以提前学习的基本 知识和步骤: 嵌入式系统:最近很多小伙伴找我&#xff0c;说想要一些嵌入式资料&#xff0c;然后我根据自己从业十年经验&#…

大模型、实时需求推动湖仓平台走向开放

大模型、实时需求高涨 AGI 时代&#xff0c;以 ChatGPT、Midjourney 等为代表的大模型迅速应用加速了 AI 普及&#xff0c;越来越多的企业选择搭建自己的 AI 基础设施&#xff0c;训练行业大模型。 另一方面&#xff0c;企业为了在瞬息万变的市场环境中更快的做出商业决策&…

华为云文件上传(单个上传和分段上传)

首先引入静态文件&#xff1a; 华为云官网提供js下载的链接 然后后端提供一个公用接口&#xff0c;返回华为云上传的基本配置&#xff1a; 官网提供的链接&#xff1a;华为云obs信息配置 一&#xff1a;单个上传 huaweiyunUpload (file, fileName, name, url, size, callb…

外包干了3年,技术不进步还退了?

外包没有很高的工资是真不能干呀&#xff01;干了3年&#xff0c;把自己废了 先说情况&#xff0c;大专毕业&#xff0c;20年通过校招进入深圳某软件公司&#xff0c;干了接近3年的&#xff08;点点点&#xff09;功能测试&#xff0c;今年年中&#xff0c;感觉自己不能够在这样…

为什么说指针是c语言的灵魂?

为什么说指针是c语言的灵魂&#xff1f; 语言主要操作的对象是数据&#xff0c;c语言里面能够有大容量数据的地方就是指针指向的heap内存。从这 个角度来看&#xff0c;确实指针就是数据的灵魂。最近很多小伙伴找我&#xff0c;说想要一些c语言资料&#xff0c;然后我根据自己…

C语言求解汉诺塔问题

完整代码&#xff1a; /*Hanoi(汉诺)塔问题。这是一个古典的数学问题&#xff1a;古代有一个梵塔&#xff0c;塔内有 3 个 座 A&#xff0c;B&#xff0c;C&#xff0c;开始时 A 座上有 64 个盘子&#xff0c;盘子大小不等&#xff0c;大的在下&#xff0c;小的在上。有一个老…

软件测试/测试开发丨南科大计算机系本科生获“火焰杯”软件测试高校就业选拔赛一等奖

2022年12月2日&#xff0c;计算机系党总支书记、副系主任王琦副教授在工学院南楼551会议室为19级徐驰同学颁发第二届“火焰杯”软件测试开发选拔赛一等奖奖项&#xff0c;为刘烨庞助理教授颁发赛事优秀指导老师奖项。徐驰同学于2022年4月获得该赛事全国总决赛第一名&#xff0c…

Cross-Modal Joint Embedding with Diverse Semantics

计算两个嵌入之间的相似度得分&#xff0c;然后利用损失函数进行联合嵌入损失最小化优化并更新参数 辅助信息 作者未提供代码

CSS 效果 圆形里一个文字居中

效果实现源码&#xff1a; 宽度&#xff0c;高度必须确认&#xff0c;且相等 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><style>.circlew {width: 45px;height: 45p…

用3D扫描生成合成数据

合成数据集&#xff08;Synthetic Datasets&#xff09;正在成为计算机视觉模型训练的标准部分。 虽然新工具使合成数据集变得更容易访问&#xff0c;但除了标准机器学习过程之外&#xff0c;许多工具还需要对 3D 建模有基本的了解。 最简单的捷径是从现实世界中获取现有对象并…

文件夹图片相似图片检测并删除相似图片

项目开源地址 pip install imagededupgit clone https://github.com/idealo/imagededup.git cd imagededup pip install "cython>0.29" python setup.py installQuick Start from imagededup.methods import PHash phasher PHash()# Generate encodings for all…

【数据提取】 Python 提取PDF中的文字和图片

从PDF中提取内容能帮助我们获取文件中的信息&#xff0c;以便进行进一步的分析和处理。此外&#xff0c;在遇到类似项目时&#xff0c;提取出来的文本或图片也能再次利用。要使用Python从PDF文件中提取文本和图片&#xff0c;可以借助 Spire.PDF for Python 这个第三方库。具体…

Allegro两种自动对齐方法

本法基于cadence的allegro可以在PCB设计运用&#xff0c;使用方法如下&#xff1a; 方式一&#xff1a;allegro软件的自动对齐——使用过程繁琐一点 1.在“setup”下拉选项中选择“application mode”&#xff0c;在副选项中选择“placement edit”&#xff1b; 2.在“Find”…

vue3+element-plus 高度封装搜索组件,支持多种类型

目录 一、应用场景 二、开发流程 三、详细开发流程 1.新建文件 2.开始步骤 3.详细代码 (1).index.vue (2).搜索组件 (3).单个搜索组件 总结 一、应用场景 一般很多网站&#xff0c;有很多数据列表&#xff0c;基本都要做搜索的功能&#xff0c;如果涉及很多页面&…

【笔记】centos7 python2.7.5安装paramiko

更直接的方式&#xff0c;参考: 离线安装_离线安装paramiko 这个更简单。 准备 资源链接: https://download.csdn.net/download/qq_26834611/88445708https://download.csdn.net/download/qq_26834611/88445708 或者选择自己下载 1. 下载python-devel 在一台能联网的cent…

静电放电保护和片式压敏电阻器

1 引言 随着电子设备功能的增加&#xff0c;输入∕输出连接器也随之增多&#xff0c;这为静电放电&#xff08;ESD&#xff09;提供了进入电路的路径&#xff0c;静电放电保护问题变得不容忽视。因此&#xff0c;有必要采用静电放电保护元件&#xff0c;在静电放电进入电路板之…