【SpringBoot教程】Spring Boot + MySQL + HikariCP 连接池整合教程

🙋大家好!我是毛毛张!
🌈个人首页: 神马都会亿点点的毛毛张 在前面一篇文章中毛毛张介绍了SpringBoot中数据源与数据库连接池相关概念,今天毛毛张要分享的是关于SpringBoot整合HicariCP连接池相关知识点以及底层源码分析

文章目录

  • [1 HicariCP连接池概述](#1 HicariCP连接池概述)
    • [1.1 HicariCP连接池简介](#1.1 HicariCP连接池简介)
  • [2 快速入门案例](#2 快速入门案例)
    • [2.1 后端代码](#2.1 后端代码)
      • [2.1.1 创建项目](#2.1.1 创建项目)
      • [2.1.2 导入依赖](#2.1.2 导入依赖)
        • [方式1:手动导入 `HikariCP` 依赖](#方式1:手动导入 HikariCP 依赖)
        • [方式 2:自动引入依赖 - 推荐](#方式 2:自动引入依赖 - 推荐)
        • 本项目完整依赖
      • [2.1.3 编写配置文件](#2.1.3 编写配置文件)
      • [2.1.4 初始化数据库并创建对应实体类](#2.1.4 初始化数据库并创建对应实体类)
      • [2.1.5 编写响应封装与前端展示VO](#2.1.5 编写响应封装与前端展示VO)
      • [2.1.6 编写业务逻辑](#2.1.6 编写业务逻辑)
      • [2.1.7 跨域资源共享](#2.1.7 跨域资源共享)
      • [2.1.8 启动类](#2.1.8 启动类)
      • [2.1.9 测试](#2.1.9 测试)
    • [2.2 前端代码](#2.2 前端代码)
  • [3 配置详解](#3 配置详解)
    • [3.1 常见配置](#3.1 常见配置)
    • [3.2 数据源配置解析](#3.2 数据源配置解析)
    • [3.3 连接池配置详解](#3.3 连接池配置详解)
  • [4 底层源码解析](#4 底层源码解析)
    • [4.1 SpringBoot自动初始化配置](#4.1 SpringBoot自动初始化配置)
    • [4.2 默认的数据源](#4.2 默认的数据源)
  • 参考文献

1 HicariCP连接池概述

  • 在上面介绍过HicariCPSpringBoot2.0之后默认的数据库连接池,特点就是:简单,高效,史称最快的数据库连接池,毛毛张将从以下几个方面展开对HicariCP连接池的介绍:
    • 首先简单介绍以下HicariCP连接池
    • 然后通过一个案例来帮助大家能够快速使用HicariCP连接池
    • 接着通过分析SpringBoot的底层代码来解释SringBoot默认的数据库连接池为HicariCP

1.1 HicariCP连接池简介

  • HikariCP 是一个高性能的JDBC数据库连接池,基于BoneCP做了多项优化,旨在提供更高的并发性能和更低的延迟。自SpringBoot 2.x版本后(自然也包括SpringBoot 3.x),HikariCP成为默认的数据库连接池,只需导入HikariCPJAR包并配置相关参数,即可无缝集成并优化数据库连接池管理。
  • HikariCP 的高性能优化主要体现在以下两个方面:
    • FastList 替代 ArrayList
      • 传统的数据库连接池大多使用ArrayList存储Statement,HikariCP 自定义了FastList来优化这一操作。FastList的优化主要体现在两个方面:
        • 取消 ArrayListget() 方法中的范围检查(range check)。由于数据库连接池管理的 List 中的索引合法性有保证,因此不需要每次访问都进行索引合法性检查。
        • 改变 ArrayListremove() 操作的遍历方式,采用从尾部开始遍历,而不是从头开始。由于连接池中的连接通常是逆序释放的(后获取的连接先释放),这样优化后,每次关闭连接时可以更高效地找到需要释放的资源,提升了效率。
    • ConcurrentBag 替代阻塞队列
      • 大多数传统数据库连接池使用两个阻塞队列(idlebusy)来管理空闲连接和忙碌连接,使用 Lock 机制来处理线程竞争,但这种方式在高并发场景下可能会导致性能瓶颈。HikariCP通过使用 ConcurrentBag 替代了传统的阻塞队列,极大地减少了锁的竞争,提高了并发性能。
      • ConcurrentBag的核心工作原理:
        • sharedList:存储所有数据库连接的共享队列,使用 CopyOnWriteArrayList 类型,支持并发操作。
        • threadList:线程本地存储,避免了线程竞争,每个线程会缓存自己获取的连接。
        • waiters:等待连接的线程数,使用 AtomicInteger 类型。
        • handoffQueue:分配数据库连接的核心队列,使用 SynchronousQueue 类型,负责将空闲连接分配给请求的线程。
      • 这种设计通过减少线程竞争和优化连接分配,提高了连接池的效率,特别适合高并发的环境。
  • 其他优化
    • 字节码精简:HikariCP 在字节码上进行了优化,编译后的字节码量极少,这样可以使得更多的代码被 CPU 缓存,从而提高程序的执行效率。减少字节码的大小是提高性能的基础,HikariCP 在这方面做得非常好。
    • 优化代理和拦截器 :HikariCP 对代理和拦截器的实现进行了精简。例如,它的 Statement 代理类仅有 100 行代码,是 BoneCP 的十分之一。这减少了性能开销,确保数据库连接池在执行 SQL 时更加高效。
    • 自定义集合类型(FastStatementList) :为了提高对 Statement 的管理效率,HikariCP 使用了自定义的集合类型 FastStatementList 来替代传统的 ArrayList。这样避免了每次获取元素时的范围检查,并且采用逆序遍历的方式来优化 remove() 操作,使得关闭连接时更加高效。
    • 自定义集合类型(ConcurrentBag) :HikariCP 为了优化并发读写效率,采用了 ConcurrentBag 代替传统的阻塞队列。这不仅提高了数据库连接分配的效率,而且减少了锁的竞争,显著提升了高并发场景下的性能。
    • 针对 BoneCP 缺陷的优化:HikariCP 在设计时,针对 BoneCP 中的一些缺陷进行了优化,特别是在处理 CPU 时间片内的耗时方法调用时,进一步提高了性能。

接下来毛毛张将结合一个完整的项目案例来介绍SpringBoot整合MySQL和HikariCP 连接池的过程,并提供了完整的代码

2 快速入门案例

  • 案例内容:基于Spring Boot,使用MyBatis-Plus框架,结合HikariCP连接池,查询并展示数据库中的全部用户信息

2.1 后端代码

2.1.1 创建项目

2.1.2 导入依赖

  • 在 Spring Boot 项目中,默认使用 HikariCP 作为数据库连接池,因此在大多数情况下无需手动引入该依赖。若项目中使用了以下某些 starter 依赖,HikariCP 会自动作为连接池配置:
    • spring-boot-starter-jdbc
    • spring-boot-starter-data-jpa
    • mybatis-spring-boot-starter
    • mybatis-plus-boot-starter
  • 需要特别注意的是,mybatismybatis-plus 已经间接依赖了 spring-boot-starter-jdbc,因此这两个 starter 会自动引入 HikariCP,从而避免重复配置。
方式1:手动导入 HikariCP 依赖
  • 如果没有导入HikariCP,我们可以通过下面的方式手动导入HikariCP依赖

    xml 复制代码
    <dependency>
       <groupId>com.zaxxer</groupId>
       <artifactId>HikariCP</artifactId>
       <version>6.2.1</version>
    </dependency>
方式 2:自动引入依赖 - 推荐
  • 在使用SpringBoot默认配置时,不需要手动添加 HikariCP 依赖,可以通过导入下面依赖的方式来自动引入HikariCP依赖:

    xml 复制代码
    <!-- 引入 Spring Boot JDBC 模块 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    
    <!-- 引入 Spring Boot JPA 模块 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    
    <!-- 引入 MyBatis Plus 模块 -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.2</version>
    </dependency>
本项目完整依赖
  • 需要注意的是,在使用 HikariCP 作为连接池的同时,还需要单独导入 MySQL 数据库驱动,通常通过引入 mysql-connector-java 依赖来实现

  • 毛毛张在这个任务中为了方便,使用了Mybatis-plus框架,整个项目的pom.xml文件为:

    xml 复制代码
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
    
        <groupId>com.zzx</groupId>
        <artifactId>springboot-HicariCP-demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>springboot-HicariCP-demo</name>
        <description>springboot-HicariCP-demo</description>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-parent</artifactId>
            <version>2.7.6</version>
        </parent>
    
    
        <properties>
            <java.version>1.8</java.version>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <spring-boot.version>2.7.6</spring-boot.version>
        </properties>
    
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <!-- MySQL 数据库驱动 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope> <!-- 运行时依赖 -->
            </dependency>
    
            <!-- MybatisPlus 核心库 -->
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.4.2</version>
            </dependency>
    
            <!-- 热部署 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
        </dependencies>
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-dependencies</artifactId>
                    <version>${spring-boot.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.1</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <version>${spring-boot.version}</version>
                    <configuration>
                        <mainClass>com.zzx.SpringbootHicariCpDemoApplication</mainClass>
                        <skip>true</skip>
                    </configuration>
                    <executions>
                        <execution>
                            <id>repackage</id>
                            <goals>
                                <goal>repackage</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    
    </project>

2.1.3 编写配置文件

  • 下面是整个项目的配置文件application.yaml,关键部分已被注释出来了,更多详细的解释可以参见第三节的内容

    yaml 复制代码
    server:
      port: 8080
    spring:
      # HikariCP 连接池配置
      datasource:
        url: jdbc:mysql://localhost:3306/springboot?useSSL=false&autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowPublicKeyRetrieval=true&serverTimezone=UTC
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: abc123
        # 指定数据源类型为 HikariDataSource
        type: com.zaxxer.hikari.HikariDataSource
        # Hikari 连接池的详细配置
        hikari:
          # 连接池名称
          pool-name: HikariCP
          # 最小空闲连接数
          minimum-idle: 5
          # 空闲连接超时时间(毫秒)
          idle-timeout: 600000
          # 连接池的最大大小
          maximum-pool-size: 10
          # 是否自动提交事务
          auto-commit: true
          # 连接的最大生命周期(毫秒)
          max-lifetime: 1800000
          # 连接超时时间(毫秒)
          connection-timeout: 30000
          # 测试连接的 SQL 语句
          connection-test-query: SELECT 1
    
    logging:
      level:
        org.springframework.jdbc.core.JdbcTemplate: DEBUG
    
    
    mybatis-plus:
      # xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)
      mapper-locations: classpath:mapper/*.xml
      configuration:
        auto-mapping-behavior: full
        # 开启驼峰映射
        map-underscore-to-camel-case: true
        # 如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段
        call-setters-on-nulls: true
        # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

2.1.4 初始化数据库并创建对应实体类

  • 创建名为springboot的数据库,并创建user_info表:

    mysql 复制代码
    -- 创建数据库
    CREATE DATABASE IF NOT EXISTS springboot CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
    USE springboot;
    
    -- 删除已存在的 user_info 表(如果存在)
    DROP TABLE IF EXISTS `user_info`;
    
    -- 创建 user_info 表
    CREATE TABLE `user_info`  (
      `id` INT NOT NULL AUTO_INCREMENT,
      `user_name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
      `pass_word` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
      `age` INT NULL DEFAULT NULL,
      `gender` VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB AUTO_INCREMENT = 3 
      DEFAULT CHARSET = utf8mb4 
      COLLATE = utf8mb4_0900_ai_ci 
      ROW_FORMAT = Dynamic;
    
    -- 插入示例数据
    INSERT INTO `user_info` (`id`, `user_name`, `pass_word`, `age`, `gender`) VALUES 
    (1, 'sam', 'password123', 32, 'M'),
    (2, 'hah', 'password456', 10, 'F');
    
    -- 确保外键检查被重新启用
    SET FOREIGN_KEY_CHECKS = 1;
  • 对应实体类User

    java 复制代码
    package com.zzx.entity;
    
    
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;
    
    @Data
    @TableName("user_info")
    public class User {
        private Integer id;        // 对应数据库中的 `u_id`
        private String userName;   // 对应数据库中的 `u_username`
        private String passWord;   // 对应数据库中的 `u_password`
        private Integer age;       // 对应数据库中的 `u_age`
        private String gender;     // 对应数据库中的 `u_gender`
    }

2.1.5 编写响应封装与前端展示VO

  • 定义统一的响应封装类 ResVo:

    java 复制代码
    package com.zzx.reponse;
    
    public class ResVo<T> {
        private Integer code;      // 状态码
        private String message;    // 消息内容
        private T content;         // 内容,可以是任何类型的数据
    
        // 构造方法
        public ResVo(Integer code, String message, T content) {
            this.code = code;
            this.message = message;
            this.content = content;
        }
    
        // 成功的返回,通常是常用的,内容可以为空
        public static <T> ResVo<T> success(T content) {
            return new ResVo<>(200, "成功", content);
        }
    
        // 失败的返回,通常返回错误信息
        public static <T> ResVo<T> error(Integer code, String message) {
            return new ResVo<>(code, message, null);
        }
    
        // Getters and Setters
        public Integer getCode() {
            return code;
        }
    
        public void setCode(Integer code) {
            this.code = code;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
        public T getContent() {
            return content;
        }
    
        public void setContent(T content) {
            this.content = content;
        }
    }
  • 前端展示对象UserInfoVo,在查询用户信息时通过是把查询到的结果封装在一个实体类中,但是返回给前端的信息不一定是用户的全部信息,例如用户的密码就不能直接返回给前端,或者不要返回,所以毛毛张设计了一个这个类:

    java 复制代码
    package com.zzx.model.vo;
    
    import lombok.Data;
    
    @Data
    public class UserInfoVo {
        //返回给前端展示的数据,密码不能展示,性别转化成数字
        private Integer id;
        private String username;
        private Integer age;
        private Integer gender;
    }
  • 为了节省传输的字符,毛毛张将用户的信息对应的内容转化成数字再返回给前端,因此设计了一个枚举类型UserSexEnum

    java 复制代码
    package com.zzx.enums;
    
    public enum UserSexEnum {
        M(1, "男"), // M对应男,值为 1
        F(0, "女"); // F对应女,值为 0
    
        private int code;         // 对应的数字值(1 或 0)
        private String description; // 性别描述(男 或 女)
    
        // 构造方法,用于设置枚举常量的描述和对应的代码
        UserSexEnum(int code, String description) {
            this.code = code;
            this.description = description;
        }
    
        // 获取性别描述
        public String getDescription() {
            return description;
        }
    
        // 获取对应的数字代码
        public int getCode() {
            return code;
        }
    
        // 根据传入的字符串 'M' 或 'F' 获取对应的性别枚举
        public static UserSexEnum fromString(String sexStr) {
            for (UserSexEnum sex : UserSexEnum.values()) {
                if (sex.name().equalsIgnoreCase(sexStr)) {
                    return sex;
                }
            }
            throw new IllegalArgumentException("无效的性别字符串: " + sexStr);
        }
    
        // 根据 'M' 或 'F' 获取对应的数字代码
        public static int getCodeByString(String sexStr) {
            UserSexEnum sex = fromString(sexStr);
            return sex.getCode();
        }
    }
  • 由于User类和UserInfoVo不是完全一一对应的,所以为了数据转换的方便,毛毛张再这里专门写了一个转换类UserConverter

    java 复制代码
    package com.zzx.converter;
    
    import com.zzx.entity.User;
    import com.zzx.enums.UserSexEnum;
    import com.zzx.model.vo.UserInfoVo;
    
    import java.util.List;
    import java.util.stream.Collectors;
    
    public class UserConverter{
        // 单个转换
        public static UserInfoVo toUserInfoDTO(User user) {
            UserInfoVo userInfoVo = new UserInfoVo();
            userInfoVo.setId(user.getId());
            userInfoVo.setUsername(user.getUserName());
            userInfoVo.setAge(user.getAge());
            userInfoVo.setGender(UserSexEnum.getCodeByString(user.getGender()));
            return userInfoVo;
        }
    
        // 批量转换
        public static List<UserInfoVo> toUserInfoDTOList(List<User> users) {
            // 使用 Java 8 的 stream API 进行批量转换
            return users.stream()
                    .map(UserConverter::toUserInfoDTO)  // 对每个 User 对象进行转换
                    .collect(Collectors.toList());     // 收集成 List<UserInfoDTO>
        }
    }

2.1.6 编写业务逻辑

  • controller层UserController

    java 复制代码
    package com.zzx.controller;
    
    import com.zzx.converter.UserConverter;
    import com.zzx.model.vo.UserInfoVo;
    import com.zzx.reponse.ResVo;
    import com.zzx.entity.User;
    import com.zzx.service.UserService;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    @RestController
    @RequestMapping("user")
    public class UserController {
        @Resource
        private UserService userService;
    
        @GetMapping("queryAllUserInfo")  // 使用 GET 请求
        public ResVo<List<UserInfoVo>> queryAllUserInfo() {
            List<User> userInfoList = userService.queryAllUserInfo();
            return ResVo.success(UserConverter.toUserInfoDTOList(userInfoList));
        }
    }
  • service层接口UserService

    java 复制代码
    package com.zzx.service;
    
    import com.zzx.entity.User;
    
    import java.util.List;
    
    
    public interface UserService {
    
        List<User> queryAllUserInfo();
    }
  • 服务层实现类UserServiceImpl

    java 复制代码
    package com.zzx.service.impl;
    
    
    import com.zzx.entity.User;
    import com.zzx.mapper.UserMapper;
    import com.zzx.service.UserService;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    @Service
    public class UserServiceImpl implements UserService {
        @Resource
        private UserMapper userMapper;
    
        @Override
        public List<User> queryAllUserInfo() {
            return userMapper.queryAllUserInfo();
        }
    }
  • mapper层UserMapper

    java 复制代码
    package com.zzx.mapper;
    
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.zzx.entity.User;
    
    import java.util.List;
    
    public interface UserMapper extends BaseMapper<User> {
        List<User> queryAllUserInfo();
    }
  • Mapper 层 SQL 映射配置文件UserMapper.xml

    xml 复制代码
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.zzx.mapper.UserMapper">
        <!-- 声明标签写sql语句  crud  select  insert update  delete
              每个标签对应接口的一个方法!  方法的一个实现!
              注意:mapper接口不能重载!!! 因为mapper.xml无法识别! 根据方法名识别!
         -->
        <!-- 查询所有用户 -->
        <select id="queryAllUserInfo" resultType="com.zzx.entity.User">
            SELECT id, user_name, pass_word, age, gender FROM user_info
        </select>
    
    </mapper>

2.1.7 跨域资源共享

  • 由于毛毛张这个代码还有前端代码,涉及到和前端交互,还需要做一个跨域资源共享的配置,毛毛张没有使用@CrossOrigin,而是通过拦截器的方式实现的:

    java 复制代码
    package com.zzx.interceptor;
    
    import org.springframework.web.servlet.HandlerInterceptor;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class CorsInterceptor implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            response.setHeader("Access-Control-Allow-Origin", "*");  // 允许所有来源
            response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
            response.setHeader("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, Authorization");
            response.setHeader("Access-Control-Allow-Credentials", "true");
    
            // 处理OPTIONS请求
            if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
                response.setStatus(HttpServletResponse.SC_OK);
                return false;  // 返回false,表示不再执行后续的Controller方法
            }
    
            return true;  // 继续执行其他拦截器或Controller方法
        }
    }
  • 配置类:

    java 复制代码
    package com.zzx.config;
    
    import com.zzx.interceptor.CorsInterceptor;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    public class WebConfig implements WebMvcConfigurer {
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            // 注册 CORS 拦截器
            registry.addInterceptor(new CorsInterceptor())
                    .addPathPatterns("/**")  // 拦截所有路径
                    .excludePathPatterns("/login", "/error");  // 排除登录和错误页面
        }
    }

2.1.8 启动类

  • 启动类SpringbootHicariCpDemoApplication

    java 复制代码
    package com.zzx;
    
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    @MapperScan("com.zzx.mapper")
    public class SpringbootHicariCpDemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringbootHicariCpDemoApplication.class, args);
        }
    
    }

2.1.9 测试

  • 启动后端程序,可以在浏览器中输入http://localhost:8080/user/queryAllUserInfo,返回结果如下则表示后端代码正确无误:

完整的后端代码已上传至毛毛张Github仓库:https://github.com/zzxrepository/SpringBootTutorial/tree/master/springboot-mysql/springboot-HicariCP-demo

2.2 前端代码

3 配置详解

3.1 常见配置

  • 下面是HicariCP连接池常见的配置:

    yaml 复制代码
    spring:
      datasource:
        url: jdbc:mysql://localhost:3306/springboot?useSSL=false&autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowPublicKeyRetrieval=true&serverTimezone=UTC
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: abc123
        type: com.zaxxer.hikari.HikariDataSource # 指定数据源类型为 HikariDataSource
        hikari: # Hikari 连接池的详细配置
          pool-name: HikariCP       # 连接池名称
          minimum-idle: 5 # 最小空闲连接数
          idle-timeout: 600000  # 空闲连接超时时间(毫秒)
          maximum-pool-size: 10 # 连接池的最大大小
          auto-commit: true # 是否自动提交事务
          max-lifetime: 1800000 # 连接的最大生命周期(毫秒)
          connection-timeout: 30000 # 连接超时时间(毫秒)
          connection-test-query: SELECT 1 # 测试连接的 SQL 语句
  • 上面配置主要分为两部分:一部分是数据源配置,一部分是数据库连接池配置

3.2 数据源配置解析

  • 下面部分属于数据源配置:

    yaml 复制代码
    spring:
      datasource:
        # 数据库连接URL,指定数据库地址、端口、库名以及连接参数
        url: jdbc:mysql://localhost:3306/springboot?useSSL=false&autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowPublicKeyRetrieval=true&serverTimezone=UTC
        # MySQL 8.0 及以上需要 cj,5.7 以下可去掉 cj
        driver-class-name: com.mysql.cj.jdbc.Driver 
        # 数据库的用户名和密码
        username: root
        password: abc123
        # 指定使用的数据库连接池实现
        type: com.zaxxer.hikari.HikariDataSource
  • 配置解析:

    • url :定义数据库连接的详细信息,包括:
      • localhost:3306(服务器地址和端口)
      • springboot(数据库名称)
      • 连接参数
    • driver-class-name :指定数据库连接的驱动,MySQL 8.x使用 com.mysql.cj.jdbc.Driver
    • usernamepassword:提供访问数据库的身份凭证。
    • type :指定数据库连接池的实现,配置为 com.zaxxer.hikari.HikariDataSource,表示使用 HikariCP 作为数据库连接池。如果需要使用 Druid ,应将其替换为 com.alibaba.druid.pool.DruidDataSource
  • url连接参数详解:

    • useUnicode=true&characterEncoding=UTF-8
      • 确保数据库与应用之间的字符编码一致,防止乱码问题。
      • 推荐使用 utf8mb4 以支持更多字符(如表情符号)。
    • serverTimezone=UTC
      • 指定数据库服务器的时区,避免时间处理错误。
      • 可选值:
        • UTC:世界标准时间,比北京时间(CST)早 8 小时。
        • Asia/Shanghai:中国标准时间(推荐)。
      • 说明 :MySQL 6.0 及以上的 com.mysql.cj.jdbc.Driver 需要显式指定时区,否则可能导致时区不匹配的异常。
    • useSSL=false
      • 指定是否启用 SSL 连接。
      • 在生产环境(如 Linux 服务器部署)通常关闭 SSL 连接,以减少额外的安全配置。
      • 建议:在公共网络或云数据库环境中建议开启 SSL。
    • autoReconnect=true&failOverReadOnly=false
      • autoReconnect=true:允许 JDBC 驱动在连接意外断开时自动重连(已弃用,推荐使用连接池)。
      • failOverReadOnly=false:防止发生故障转移时连接错误地设置为只读模式。
    • allowPublicKeyRetrieval=true
      • 允许客户端从 MySQL 服务器检索公钥,以进行密码验证。
      • 安全建议:生产环境下尽量避免使用该参数,建议启用 SSL 进行安全通信。
    • zeroDateTimeBehavior=convertToNull
      • 当数据库中的日期时间值为 0000-00-00 00:00:00 时,转换为 null,避免 Java 应用程序在处理无效日期值时出错。
    • rewriteBatchedStatements=true
      • 启用批量执行优化,提高批量 INSERTUPDATE 的执行效率。
      • 适用于大数据量操作,不适用于 SELECT 查询。

3.3 连接池配置详解

  • 下面是关于连接池的配置部分:

    yaml 复制代码
    spring:
      datasource:
        hikari:
          # 连接池名称
          pool-name: HikariCP
          # 最小空闲连接数
          minimum-idle: 5
          # 空闲连接超时时间(毫秒)
          idle-timeout: 600000
          # 连接池的最大大小
          maximum-pool-size: 10
          # 是否自动提交事务
          auto-commit: true
          # 连接的最大生命周期(毫秒)
          max-lifetime: 1800000
          # 连接超时时间(毫秒)
          connection-timeout: 30000
          # 测试连接的 SQL 语句
          connection-test-query: SELECT 1
  • 关于HikariCP连接池的详细配置解析如下:

属性 描述 构造器默认值 默认配置 validate 之后的值 validate 重置
autoCommit 自动提交从池中返回的连接 true true --
connectionTimeout 等待来自池的连接的最大毫秒数 30000 (30秒) 30000 如果小于 250 毫秒,则重置为 30 秒
idleTimeout 连接允许在池中闲置的最长时间 600000 (10分钟) maxLifetime 影响,条件不符可能重置为 0 如果设置小于 10 秒则重置为 10 秒
maxLifetime 池中连接最长生命周期 1800000 (30分钟) 1800000 如果小于 30 秒则重置为 30 分钟
connectionTestQuery 如果支持 JDBC4,建议不设置此属性 null null --
minimumIdle 池中维护的最小空闲连接数 10 10 小于 0 或大于 maxPoolSize 则重置为 maxPoolSize
maximumPoolSize 池中最大连接数,包括闲置和使用中的连接 10 10 如果小于 1,则重置为默认 10 或 minIdle 的值
metricRegistry 指定 Codahale/Dropwizard MetricRegistry 的实例 null null --
healthCheckRegistry 指定 Dropwizard HealthCheckRegistry 的实例 null null --
poolName 连接池的用户定义名称,主要用于日志和 JMX 管理 null HikariPool-1 --
initializationFailTimeout 控制池是否在初始化连接失败时快速失败 1 1 --
isolateInternalQueries 是否在独立事务中隔离内部池查询 false false --
allowPoolSuspension 是否允许通过 JMX 暂停和恢复池 false false --
readOnly 从池中获取的连接是否默认处于只读模式 false false --
registerMbeans 是否注册 JMX 管理 Bean(MBeans) false false --
catalog 设置默认 catalog default null --
connectionInitSql 在新连接创建后执行的 SQL 语句 null null --
driverClassName JDBC URL 解析失败时指定驱动程序类名称 null null --
transactionIsolation 连接的默认事务隔离级别 null null --
validationTimeout 连接测试活动的最大时间 5000 (5秒) 5000 如果小于 250 毫秒,则重置为 5 秒
leakDetectionThreshold 检测潜在的连接泄漏的时间阈值 0 0 必须 > 0 且 ≥ 2 秒,且不能大于 maxLifetime
dataSource 设置要包装的数据源实例 null null --
schema 设置默认 schema default null --
threadFactory 设置用于创建池线程的 ThreadFactory 实例 null null --
scheduledExecutor 设置用于池内部任务的 ScheduledExecutorService 实例 null null --

4 底层源码解析

  • 前面毛毛张介绍了HikariCP连接池的配置,下面毛毛张通过分析源码的方式来介绍以下为什么说SoringBoot2.x之后默认使用的连接池是HikariCP,从SpringBoot自动初始化配置 和 默认的数据源 两个角度理解。

4.1 SpringBoot自动初始化配置

  • 找到spring-boot-autocinfigure-2.7.6.jar依赖下面的org.springframework.boot.autoconfigure.jdbc
  • 关键代码如下:
  • 找到HikariCP数据源的配置:你可以发现,为了支持动态更新配置(基于MXBean),这里还设计了一层HikariConfigMXBean接口

4.2 默认的数据源

  • 首先,springboot-starter-jdbc中默认加载了HikariCP

  • 其次,在配置初始化或者加载时都是第一个被加载的

参考文献


都看到这了,不妨一键三连再走吧!

🌈欢迎和毛毛张一起探讨和交流!
联系方式点击下方个人名片


相关推荐
幸好我会魔法2 小时前
人格分裂(交互问答)-小白想懂Elasticsearch
大数据·spring boot·后端·elasticsearch·搜索引擎·全文检索
危险、2 小时前
Spring Boot 无缝集成SpringAI的函数调用模块
人工智能·spring boot·函数调用·springai
SomeB1oody2 小时前
【Rust自学】15.2. Deref trait Pt.1:什么是Deref、解引用运算符*与实现Deref trait
开发语言·后端·rust
何中应3 小时前
从管道符到Java编程
java·spring boot·后端
boonya3 小时前
Yearning开源MySQL SQL审核平台
数据库·mysql·开源
组合缺一3 小时前
Solon Cloud Gateway 开发:Route 的过滤器与定制
java·后端·gateway·reactor·solon
SomeB1oody3 小时前
【Rust自学】15.4. Drop trait:告别手动清理,释放即安全
开发语言·后端·rust
customer084 小时前
【开源免费】基于SpringBoot+Vue.JS贸易行业crm系统(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·开源
CPU NULL4 小时前
新版IDEA创建数据库表
java·数据库·spring boot·sql·学习·mysql·intellij-idea
J不A秃V头A5 小时前
MySQL 中开启二进制日志(Binlog)
数据库·mysql