HikariCP源码简洁剖析——HikariDataSource_HikariCP的使用和源码

文章目录

  • HikariDataSource的作用
  • 源码剖析
    • 核心变量
    • 构造方法
    • 获取链接实例
  • HikariCP的使用和源码
    • 简介
    • HikariCP是什么?
    • HikariCP 解决了哪些问题?
    • 为什么要使用 HikariCP?
    • 本文要讲什么?
    • 如何使用 HikariCP
    • 需求
    • 项目环境
    • 引入依赖
    • 编写 hikari.properties
    • 初始化连接池
    • 如何使用 JMX 管理连接池
    • 需求
    • 开启JMX
    • 启动连接池
    • 使用jconsole查看
    • 配置参数详解
    • 必需的参数
    • 常用的参数
    • 很少用的参数
    • 源码分析
    • HikariCP为什么快?
      • 传统模型--中规中矩的模型
      • 标记模型--更少的锁
    • HikariCP主要的类
    • ConcurrentBag--最核心的类
    • HikariPool--管理连接池
    • 一些有趣的地方
      • 为什么HikariDataSource有两个HikariPool
      • 如何加载配置
    • 参考资料

HikariDataSource的作用

在Hikari中,HikariDataSource是jdk中javax.sql.DataSource的实现类,实现了对数据源的封装,通过getConnection()方法对外提供Connection的实例。

源码剖析

核心变量

fastPathPoolpoll都是数据库链接池的引用,且指向了同一个连接池对象。

之所以有两个引用的原因是用final修饰的fastPathPool引用比用volatile修饰的poll引用在使用时效率更改,这一点也可以看出来在HikariCP中作者对细节的处理。

//连接池是否已关闭,在调用close方法时设置为true
private final AtomicBoolean isShutdown = new AtomicBoolean();//fastPathPool默认和pool指向同一个对象,用final修饰在使用时更快。
//但只有用HikariConfig做构造参数时才会给fastPathPool赋值。
//因为final类型的变量不能在方法内赋值(getConnection是一个普通方法,不能给类final成员变量赋值)
private final HikariPool fastPathPool;//用double-check实现的单例poll,所以要用volatile实现线程安全
private volatile HikariPool pool;

构造方法

HikariDataSource有两个构造方法:

  1. 无参构造方法:

    默认构造方法,这种构造方法比有参构造方法在调用getConnection()的性能稍差。

    在这种构造方法中不会初始化连接池对象HikariPool,所以也不会给fastPathPoolpool赋值,而是会等到调用getConnection()时使用double check延迟初始化。

    /*** Default constructor.  Setters be used to configure the pool.  Using* this constructor vs. {@link#HikariDataSource(HikariConfig)} will* result in {@link#getConnection()} performance that is slightly lower* due to lazy initialization checks.*/
    public HikariDataSource()
    {super();fastPathPool = null;
    }
    
  2. 有参构造方法

    使用指定的HikariConfig初始化HikariDataSource。

    在该构造方法中同时会初始化HikariPool实例并赋值给poolfastPathPool

    /*** Construct a HikariDataSource with the specified configuration.**@paramconfigurationa HikariConfig instance*/
    public HikariDataSource(HikariConfig configuration)
    {//参数校验configuration.validate();//使用configuration给连接池赋值configuration.copyState(this);LOGGER.info("{} - Starting...", configuration.getPoolName());pool = fastPathPool = new HikariPool(this);
    LOGGER.info("{} - Start completed.", configuration.getPoolName());
    }
    

获取链接实例

  1. 若连接池实例已初始化,直接调用fastPathPoolgetConnection方法即可
  2. 若链接池未初始化,则初始化并将pool指向连接池对象。
  3. 因为可能有并发调用getConnection,所以用double check来初始化连接池,保证线程安全。
  4. 因为fastPathPool是final类型的变量,所以无法在成员方法中赋值。所以后续调用getConnection的时候都要使用pool来获取链接。
@Override
public Connection getConnection() throws SQLException
{if (isClosed()) {throw new SQLException("HikariDataSource " + this + " has been closed.");}if (fastPathPool != null) {return fastPathPool.getConnection();}// See http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_JavaHikariPool result = pool;if (result == null) {synchronized (this) {result = pool;if (result == null) {validate();
LOGGER.info("{} - Starting...", getPoolName());try {pool = result = new HikariPool(this);}catch (PoolInitializationException pie) {if (pie.getCause() instanceof SQLException) {throw (SQLException) pie.getCause();}else {throw pie;}}
LOGGER.info("{} - Start completed.", getPoolName());}}}return result.getConnection();
}

在jdk的DataSource接口中还定义了另一种传数据库用户名和密码的getConnection的方式,但在HikariCP中不支持该方式:

@Override
public Connection getConnection(String username, String password) throws SQLException
{throw new SQLFeatureNotSupportedException();
}

HikariCP的使用和源码

简介

HikariCP是什么?

HikariCP 本质上就是一个数据库连接池。

HikariCP 解决了哪些问题?

创建和关闭数据库连接的开销很大,HikariCP 通过“池”来复用连接,减小开销。

为什么要使用 HikariCP?

  1. HikariCP 是目前最快的连接池。就连风靡一时的 boneCP 也停止维护,主动让位给它。SpringBoot 也把它设置为默认连接池。

img

  1. HikariCP 非常轻量。本文用到的 4.0.3 版本的 jar 包仅仅只有 156 KB,它的源码真的非常精炼。

本文要讲什么?

本文将包含以下内容(因为篇幅较长,可根据需要选择阅读):

  1. 如何使用 HikariCP(入门、JMX 等)
  2. 配置参数详解
  3. 源码分析

如何使用 HikariCP

需求

使用 HikariCP 获取连接对象,对用户数据进行简单的增删改查。

项目环境

JDK:1.8.0_231

maven:3.6.3

IDE:Spring Tool Suite 4.6.1.RELEASE

mysql-connector-java:8.0.15

mysql:5.7.28

Hikari:4.0.3

引入依赖

项目类型 Maven Project,打包方式 jar。

<!-- test --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><!-- hikari --><dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId><version>4.0.3</version></dependency><!-- mysql驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.15</version></dependency><!-- log --><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-core</artifactId><version>1.2.3</version><type>jar</type></dependency><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version><type>jar</type></dependency>

编写 hikari.properties

本文使用配置文件的方式来配置 HikariCP,当然,我们也可以在代码中显式配置,但不提倡。因为是入门例子,这里我只给出了必需的参数,其他的参数后面会详细介绍。

jdbcUrl=jdbc:mysql://localhost:3306/github_demo?characterEncoding=utf8&serverTimezone=GMT%2B8
username=root
password=root

初始化连接池

初始化连接池时,我们可以在代码中显式指定配置文件,也可以通过启动参数配置。

// 加载配置文件,也可以无参构造并使用启动参数 hikaricp.configurationFile 指定配置文件(不推荐,后面会说原因)
HikariConfig config = new HikariConfig("/hikari2.properties");
HikariDataSource dataSource = new HikariDataSource(config);

初始化连接池之后,我们可以通过HikariDataSource.getConnection()方法获取连接对象,然后进行增删改查操作,这部分内容这里就不展示了。

如何使用 JMX 管理连接池

需求

开启 JMX 功能,并使用 jconsole 管理连接池。

开启JMX

在入门例子的基础上增加配置。这要设置 registerMbeans 为 true,JMX 功能就会开启。

#-------------JMX--------------------------------
# 是否开启 JMX
# 默认 false
registerMbeans=true# 是否允许通过 JMX 挂起和恢复连接池
# 默认为 false
allowPoolSuspension=true# 连接池名称。
# 默认自动生成
poolName=zzsCP

启动连接池

为了查看具体效果,这里让主线程进入睡眠 20 分钟。

public static void main(String[] args) throws InterruptedException {HikariConfig config = new HikariConfig("/hikaricp_base.properties");HikariDataSource dataSource = new HikariDataSource(config);Thread.sleep(20 * 60 * 1000);dataSource.close();}

使用jconsole查看

运行 main 方法,使用 JDK 的工具 jconsole 连接我们的项目,在 MBean 选项卡可以看到我们的连接池。接下里,我们可以进行这样的操作:

  1. 通过 PoolConfig 动态修改配置(只有部分参数允许修改);
  2. 通过 Pool 获取连接池的连接数(活跃、空闲和所有)、获取等待连接的线程数、挂起和恢复连接池、丢弃未使用连接等。

在这里插入图片描述

想了解更多 JMX 功能可以参考我的博客文章: 如何使用JMX来管理程序?

配置参数详解

相比其他连接池,HikariCP 的配置参数非常简单,其中有几个功能需要注意:

  1. HikariCP 借出连接时强制检查连接的活性,不像其他连接池一样可以选择不检查;
  2. 默认会检查 idleTimeout、maxLifetime,可以选择禁用,但不推荐;
  3. 默认不检查 keepaliveTime、leakDetectionThreshold,可以选择开启,推荐开启 leakDetectionThreshold 即可。

必需的参数

注意,这里 jdbcUrl 和 dataSourceClassName 二选一。

#-------------必需的参数--------------------------------
# JDBC 驱动中 DataSource 的实现类全限定类名。不支持 XA DataSource
# 如果指定, HikariCP 将使用 DataSouce.getConnection 获取连接而不是使用 DriverManager.getConnection,官方建议指定(mysql 除外)
# dataSourceClassName=# 如果指定, HikariCP 将使用 DriverManager.getConnection 获取连接而不是使用 DataSouce.getConnection
jdbcUrl=jdbc:mysql://localhost:3306/github_demo?characterEncoding=utf8&serverTimezone=GMT%2B8# 用户名和密码
username=root
password=root

常用的参数

# 从池中借出的连接是否默认自动提交事务
# 默认 true
autoCommit=true# 当我从池中借出连接时,愿意等待多长时间。如果超时,将抛出 SQLException
# 默认 30000 ms,最小值 250 ms。支持 JMX 动态修改
connectionTimeout=30000# 一个连接在池里闲置多久时会被抛弃
# 当 minimumIdle < maximumPoolSize 才生效
# 默认值 600000 ms,最小值为 10000 ms,0表示禁用该功能。支持 JMX 动态修改
idleTimeout=600000# 多久检查一次连接的活性
# 检查时会先把连接从池中拿出来(空闲的话),然后调用isValid()或执行connectionTestQuery来校验活性,如果通过校验,则放回池里。
# 默认 0 (不启用),最小值为 30000 ms,必须小于 maxLifetime。支持 JMX 动态修改
keepaliveTime=0# 当一个连接存活了足够久,HikariCP 将会在它空闲时把它抛弃
# 默认 1800000  ms,最小值为 30000 ms,0 表示禁用该功能。支持 JMX 动态修改
maxLifetime=1800000# 用来检查连接活性的 sql,要求是一个查询语句,常用select 'x'
# 如果驱动支持 JDBC4.0,建议不设置,这时默认会调用  Connection.isValid() 来检查,该方式会更高效一些
# 默认为空
# connectionTestQuery=# 池中至少要有多少空闲连接。
# 当空闲连接 < minimumIdle,总连接 < maximumPoolSize 时,将新增连接
# 默认等于 maximumPoolSize。支持 JMX 动态修改
minimumIdle=5# 池中最多容纳多少连接(包括空闲的和在用的)
# 默认为 10。支持 JMX 动态修改
maximumPoolSize=10# 用于记录连接池各项指标的 MetricRegistry 实现类
# 默认为空,只能通过代码设置
# metricRegistry=# 用于报告连接池健康状态的 HealthCheckRegistry 实现类
# 默认为空,只能通过代码设置
# healthCheckRegistry=# 连接池名称。
# 默认自动生成
poolName=zzsCP

很少用的参数

# 如果启动连接池时不能成功初始化连接,是否快速失败 TODO
# >0 时,会尝试获取连接。如果获取时间超过指定时长,不会开启连接池,并抛出异常
# =0 时,会尝试获取并验证连接。如果获取成功但验证失败则不开启池,但是如果获取失败还是会开启池
# <0 时,不管是否获取或校验成功都会开启池。
# 默认为 1
initializationFailTimeout=1# 是否在事务中隔离 HikariCP 自己的查询。
# autoCommit 为 false 时才生效
# 默认 false
isolateInternalQueries=false# 是否允许通过 JMX 挂起和恢复连接池
# 默认为 false
allowPoolSuspension=false# 当连接从池中取出时是否设置为只读
# 默认值 false
readOnly=false# 是否开启 JMX
# 默认 false
registerMbeans=true# 数据库 catalog
# 默认由驱动决定
# catalog=# 在每个连接创建后、放入池前,需要执行的初始化语句
# 如果执行失败,该连接会被丢弃
# 默认为空
# connectionInitSql=# JDBC 驱动使用的 Driver 实现类
# 一般根据 jdbcUrl 判断就行,报错说找不到驱动时才需要加
# 默认为空
# driverClassName=# 连接的默认事务隔离级别
# 默认值为空,由驱动决定
# transactionIsolation=# 校验连接活性允许的超时时间
# 默认 5000 ms,最小值为 250 ms,要求小于 connectionTimeout。支持 JMX 动态修改
validationTimeout=5000# 连接对象可以被借出多久
# 默认 0(不开启),最小允许值为 2000 ms。支持 JMX 动态修改
leakDetectionThreshold=0# 直接指定 DataSource 实例,而不是通过 dataSourceClassName 来反射构造
# 默认为空,只能通过代码设置
# dataSource=# 数据库 schema
# 默认由驱动决定
# schema=# 指定连接池获取线程的 ThreadFactory 实例
# 默认为空,只能通过代码设置
# threadFactory=# 指定连接池开启定时任务的 ScheduledExecutorService 实例(建议设置setRemoveOnCancelPolicy(true))
# 默认为空,只能通过代码设置
# scheduledExecutor=# JNDI 配置的数据源名
# 默认为空
# dataSourceJndiName=

源码分析

HikariCP 的源码少且精,可读性非常高。如果你没见过像诗一样的代码,可以来看看 HikariCP。

提醒一下,在阅读 HiakriCP 源码之前,需要掌握CopyOnWriteArrayListAtomicIntegerSynchronousQueueSemaphoreAtomicIntegerFieldUpdaterLockSupport等 JDK 自带类的使用。

注意,考虑到篇幅和可读性,以下代码经过删减。

HikariCP为什么快?

数据库连接池已经发展了很久了,也算是比较成熟的技术,使用比较广泛的类库有 boneCP、DBCP、C3P0、Druid 等等。眼看着数据库连接池已经发展到了瓶颈,所谓的性能提升也仅仅是一些代码细节的优化,这个时候,HikariCP 出现并快速地火了起来,与其他连接池相比,它的快不是普通的快,而是跨越性的快。下面是 JMH 测试的结果([测试项目地址](https://zhuanlan.zhihu.com/p/107748071/brettwooldridge/HikariCP-benchmark: JHM benchmarks for JDBC Connection Pools (github.com)))。

在这里插入图片描述

HikariCP 为什么快?我看网上有很多的解释,例如,大量使用 JDK 并发包的工具来避免粗颗粒度的锁、FastList 等自定义类的使用、动态代理类等等。我觉得,这些都不是主要原因。

HikariCP 之所以快,更多的还是由于抽象层面的优化

传统模型–中规中矩的模型

连接池,顾名思义,就是一个存放连接对象的池塘。几乎所有的连接池都会从代码层面抽象出一个池塘。池里的连接数量不是一成不变的,例如,连接失效了需要移除、新连接创建、用户借出或归还连接,等等,总结起来,对连接池的操作不外乎四个:borrow、return、add、remove

连接池一般是这样设计的:borrow、remove 动作会将连接从池塘里拿出来,add、return 动作则会往池塘里添加连接。我把这种模型称为“传统模型”。

在这里插入图片描述

“传统模型”是比较中规中矩的模型,从抽象层面讲,它非常符合我们的现实生活,例如,某人借走我的钱,钱就不在我的钱包里了。我们熟知的 DBCP、C3P0、Druid 等等都是基于“传统模型”开发的。

标记模型–更少的锁

但是 HikariCP 就不一样了,它没有走老路,而是优化了“传统模型”,让连接池真正意义地实现了提速。

在“传统模型”中,borrow、return、add、remove 四个动作都需要加同一把锁,即同一时刻只允许一个线程操作池,并发高时线程切换将非常频繁。因为多个线程操作同一个池塘,连接出入池需要加锁来保证线程安全。针对这一点,我们是不是能做些什么呢?

HikariCP 是这样做的,borrow 的连接不会从池塘里取出,而是打上“已借出”的标记,return 的时候,再把这个连接的“已借出”标记去掉。我把这种做法称为“标记模型”。“标记模型”可以实现 borrow 和 return 动作不加锁。具体怎么做到的呢?

在这里插入图片描述

首先,我要 borrow 时,我需要看看池塘里哪一个连接可以借出。这里就涉及到读连接池的操作,因为池塘里的连接数量不是一成不变的,为了一致性,我们就必须加锁。但是,HikariCP 没有加,为什么呢?因为 HikariCP 容忍了读的不一致。borrow 的时候,我们实际上读的不是真正的池塘,而是当前池塘的一份快照。我们看看 HikariCP 存放连接的地方,是一个CopyOnWriteArrayList对象,我们知道,CopyOnWriteArrayList是一个写安全、读不安全的集合。

public class ConcurrentBag<T extends IConcurrentBagEntry> implements AutoCloseable {// 存放连接的集合private final CopyOnWriteArrayList<T> sharedList;
}

接着,当我们找到了一个可借出的连接时,需要给它打上借出的标记。注意,这时有可能出现多个线程都想给它打标记的情况,该怎么办呢?难道要加锁了吗?别忘了我们可以用 CAS 机制来更新连接的标记,这个时候就不需要加锁了。看看 HikariCP 就是这么实现的。

public T borrow(long timeout, final TimeUnit timeUnit) throws InterruptedException {final int waiting = waiters.incrementAndGet();try {for (T bagEntry : sharedList) {if (bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {return bagEntry;}}return null;}finally {waiters.decrementAndGet();}}

于是,在“标记模型”里,只有 add 和 remove 才需要加锁,borrow 和 return 不需要加锁。通过这种颠覆式的设计,连接池的性能得到极大的提高。

这就是我认为的 HikariCP 快的最主要原因。

HikariCP主要的类

那么,我们来看看 HikariCP 的源码吧。

HikariCP 的类不多,最主要的就这几个,如图。不难发现,这种类结构和 DBCP2 很像。

在这里插入图片描述

这几个类可以分成四个部分:

  1. 用户接口。用户一般会使用DataSource.getConnection()来获取连接对象。
  2. JMX 支持。
  3. 配置信息。使用HikariConfig加载配置文件,或手动配置HikariConfig的参数,它一般会作为入参来构造HikariDataSource对象;
  4. 连接池。获取连接的过程为HikariDataSource.getConnection()->HikariPool.getConnection()->ConcurrentBag.borrow(long, TimeUnit)需要注意的是,ConcurrentBag才是真正的连接池,而HikariPool是用来管理连接池的

ConcurrentBag–最核心的类

ConcurrentBag可以算是 HikariCP 最核心的一个类,它是 HikariCP 底层真正的连接池,上面说的“标记模型”只要就是靠它来实现的。如果大家不想看太多代码的话,只看它就足够了。

在设计上,ConcurrentBag是一个比较通用的资源池,它可以是数据库连接的池,也可以是其他对象的池,只要存放的资源对象实现了IConcurrentBagEntry接口即可。所以,如果我们的项目中需要自己构建池的话,可以直接拿这个现成的组件来用。

public class ConcurrentBag<T extends IConcurrentBagEntry> implements AutoCloseable {private final CopyOnWriteArrayList<T> sharedList;
}

下面简单介绍下ConcurrentBag的几个字段:

在这里插入图片描述

属性描述
CopyOnWriteArrayList sharedList存放着状态为使用中、未使用和保留三种状态的资源对象
ThreadLocal threadList存放着当前线程归还的资源对象
SynchronousQueue handoffQueue这是一个无容量的阻塞队列,出队和入队都可以选择是否阻塞
AtomicInteger waiters当前等待获取元素的线程数

这几个字段在ConcurrentBag中如何使用呢,这里拿borrow方法来说明下:

public T borrow(long timeout, final TimeUnit timeUnit) throws InterruptedException{// 1. 首先从threadList获取资源final List<Object> list = threadList.get();for (int i = list.size() - 1; i >= 0; i--) {final Object entry = list.remove(i);final T bagEntry = weakThreadLocals ? ((WeakReference<T>) entry).get() : (T) entry;if (bagEntry != null && bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {return bagEntry;}}// 等待获取连接的线程数+1final int waiting = waiters.incrementAndGet();try {// 2.如果还没获取到,会从sharedList中获取对象for (T bagEntry : sharedList) {if (bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {// 这一步我不是很懂,好像可有可无if (waiting > 1) {listener.addBagItem(waiting - 1);}return bagEntry;}}// 从sharedList中获取不到资源,通知监听器创建资源(不一定会创建)listener.addBagItem(waiting);// 3.如果还没获取到,会堵塞等待空闲连接timeout = timeUnit.toNanos(timeout);do {final long start = currentTime();// 这里会出现三种情况,// 1.超时,返回null// 2.获取到资源,但状态为正在使用,继续循环// 3.获取到资源,元素状态为未使用,修改为已使用并返回final T bagEntry = handoffQueue.poll(timeout, NANOSECONDS);if (bagEntry == null || bagEntry.compareAndSet(STATE_NOT_IN_USE, STATE_IN_USE)) {return bagEntry;}timeout -= elapsedNanos(start);} while (timeout > 10_000);// 4.超时了还是没有获取到,返回nullreturn null;}finally {// 等待获取连接的线程数-1waiters.decrementAndGet();}}

在上面的方法中,唯一会造成线程阻塞的就是handoffQueue.poll(timeout, NANOSECONDS),除此之外,我们没有看到任何的 synchronized 和 lock。

HikariPool–管理连接池

除了ConcurrentBagHikariPool也是一个比较重要的类,它用来管理连接池

在这里插入图片描述

HikariPool 的几个字段说明如下:

属性类型和属性名说明
DataSource dataSource用于获取原生连接对象的数据源。一般我们不指定的话,使用的是DriverDataSource
ThreadPoolExecutor addConnectionExecutor执行创建连接任务的线程池。只开启一个线程执行任务
ThreadPoolExecutor closeConnectionExecutor执行关闭原生连接任务的线程池。只开启一个线程执行任务
ScheduledExecutorService houseKeepingExecutorService用于执行检查 idleTimeout、leakDetectionThreshold、keepaliveTime、maxLifetime 等任务的线程池。

为了更清晰地理解上面几个字段的含义,我简单画了个图,不是很严谨,将就看下吧。在这个图中,客户端线程可以调用进行 borrow、requite 和 remove 操作,houseKeepingExecutorService 线程可以调用进行 remove 操作,只有 addConnectionExecutor 可以进行 add 操作。

在这里插入图片描述

一些有趣的地方

掌握了上面的两个类,HikariCP 的整个源码视图应该就比较完整了。下面再说一些有趣的地方。

为什么HikariDataSource有两个HikariPool

在下面的代码中,HikariDataSource里竟然有两个HikariPool

public class HikariDataSource extends HikariConfig implements DataSource, Closeable
{private final HikariPool fastPathPool;private volatile HikariPool pool;
}

为什么要这样做呢?

首先,从性能方面考虑,使用 fastPathPool 来创建连接会比 pool 更好一些,因为 pool 被 volatile 修饰了,为了保证可见性不能使用缓存。那为什么还要用到 pool 呢?

我们打开HikariDataSource.getConnection(),可以看到,pool 的存在可以用来支持双重检查锁。这里我比较好奇的是,为什么不把 HikariPool的引用给 fastPathPool??这个问题大家感兴趣可以研究一下。

public Connection getConnection() throws SQLException{if (fastPathPool != null) {return fastPathPool.getConnection();}HikariPool result = pool;if (result == null) {synchronized (this) {result = pool;if (result == null) {validate();pool = result = new HikariPool(this);this.seal();}}}return result.getConnection();}

其实,这两个HikariPool对象有两种取值情况:

取值一:fastPathPool = pool = new HikariPool(this)。当通过有参构造new HikariDataSource(HikariConfig configuration)来创建HikariDataSource就会出现这样取值;

取值二:fastPathPool = null;pool = new HikariPool(this)。当通过无参构造new HikariDataSource()来创建HikariDataSource就会出现这样取值。

所以,我更推荐使用new HikariDataSource(HikariConfig configuration)的方式,因为这样做的话,我们将使用 fastPathPool 来获取连接。

如何加载配置

HikariCP 加载配置的代码非常简洁。我们直接从PropertyElf.setTargetFromProperties(Object, Properties)方法开始看,如下。

// 这个方法就是将properties的参数设置到HikariConfig中public static void setTargetFromProperties(final Object target, final Properties properties){if (target == null || properties == null) {return;}// 获取HikariConfig的所有方法List<Method> methods = Arrays.asList(target.getClass().getMethods());properties.forEach((key, value) -> {// 如果是dataSource.*的参数,直接加入到dataSourceProperties属性if (target instanceof HikariConfig && key.toString().startsWith("dataSource.")) {((HikariConfig) target).addDataSourceProperty(key.toString().substring("dataSource.".length()), value);}else {// 找到参数对应的setter方法并赋值setProperty(target, key.toString(), value, methods);}});}

相比其他类库(尤其是 druid),HikariCP 加载配置的过程非常简洁,不需要按照参数名一个个地加载,这样后期会更好维护。当然,这种方式我们也可以运用到实际项目中。

另外,配置 HikariCP 的时候不允许写错参数或者添加一些无关的参数,否则会因为找不到对应的 setter 方法而报错。

以上基本讲完 HikariCP 的源码。后续发现其他有趣的地方再做补充,也欢迎大家指正不足的地方。

最后,感谢阅读。

参考资料

HikariCP github

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

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

相关文章

MySQL如何创建沙箱,沙箱环境搭建 - osc_y8w65yuq的个人空间 - OSCHINA - 中文开源技术交流社区...

[toc]测试环境搭建沙箱环境&#xff1a;-------测试环境搭建基础配置&#xff1a;# 1、在沙箱环境下实名认证&#xff1a;https://openhome.alipay.com/platform/appDaily.htm?tabinfo# 2、电脑网站支付API&#xff1a;https://docs.open.alipay.com/270/105898/# 3、完成RSA密…

PHP单选框实现的方法,jQuery简单实现遍历单选框的方法

本文实例讲述了jQuery简单实现遍历单选框的方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;1、问题背景&#xff1a;有四个单选框&#xff0c;分别为一年四季&#xff0c;现在需要判断是否选中&#xff0c;如果选中这个单选框&#xff0c;就将其值赋值给输入框2、…

String怎么转成BigDecimal_Java.math.BigDecimal类的使用

1.引言 浮点数值不适用于无法接受舍入误差的金融计算中。 例如&#xff0c;命令System.out.prmtln (2.0-1.1)将打印出0.8999999999999999&#xff0c;而不是人们想象的0.9 。 2.0-1.1的运算结果 这种舍入误差的主要原因是浮点数值采用二进制系统表示&#xff0c;而在二进制系统…

JTS Java空间几何计算、距离、最近点、subLine等计算

文章目录前言地理坐标系和投影坐标系地理坐标系投影坐标系地图投影墨卡托/Web墨卡托常见坐标系地理坐标系和投影坐标系互转EPSG:3857和EPSG:4326Java各坐标系之间的转换&#xff08;高斯、WGS84经纬度、Web墨卡托、瓦片坐标&#xff09;GeotoolsJTSvividsolutions和locationtec…

JTS学习笔记

简介 JTS由加拿大的VividSolutions公司开发&#xff0c;是一个用Java语言描述的几何拓扑套件&#xff0c;遵循OpenGIS的Simple Feature Specification&#xff0c;封装了2D几何类型和非常多的空间分析操作&#xff0c;而且包含了不少常见的计算几何算法实现。 JTS被广泛地应用…

填坑:Maven工程引用GeoTools依赖

这两天在做一个系统的后台&#xff0c;需要用到GeoTools做后端空间分析&#xff0c;记录一下自己遇到的问题。 项目通过Maven进行构建&#xff0c;参照文档Maven Quickstart添加GeoTools依赖和远程仓库地址。 <dependencies><dependency><groupId>org.geotoo…

墨卡托投影介绍

一、墨卡托投影 墨卡托投影&#xff0c;又称正轴等角圆柱投影&#xff0c;由荷兰地图学家墨卡托(G.Mercator)于1569年创拟。假设地球被套在一个圆柱中&#xff0c;赤道与圆柱相切&#xff0c;然后在地球中心放一盏灯&#xff0c;把球面上的图形投影到圆柱体上&#xff0c;再把…

GIS算法:可视化工具JTS TestBuilder

java、python、js都有可以引用的第三方包&#xff0c;实现GIS的空间算法。 java是jts&#xff0c;python是shapely&#xff0c;js是turf。 其中jts值得首先拥有&#xff0c;因为jts提供了一个界面工具JTS TestBuilder&#xff0c;可以在上面绘制图形&#xff0c;验证各种算法…

wkt区域围栏

API文档 http://shengshifeiyang.gitee.io/geotools-learning/ /** * 判断以x,y为坐标的点point(x,y)是否在geometry表示的Polygon中 * param x * param y * param geometry wkt格式 POLYGON((0 0, 10 0, 10 10, 0 10,0 0)) * return */ public static boolean withinGeo(doub…

oracle关联字段和序列,oracle(9) 序列和约束

序列 SEQUENCE也是数据库对象之一&#xff0c;作用&#xff1a;根据指定的规则生成一些列数字。序列通常是为某张表的主键提供值使用。主键&#xff1a;通常每张表都会有主键字段&#xff0c;该字段的值要求非空且唯一&#xff0c;使用该字段来确定表中的每一条记录。CREATE SE…

lambda表达式处理异常_lambda表达式内出现异常无法throw抛出

lambda体中有受检异常,为什么不能在lambda表达式外层try catch,只能在lambda里面try catch xxx.getUpdateList().forEach((map) ->{xxxVO vo new xxxVO();BeanUtils.populate(vo,map); // populate方法往外抛了异常list.add(vo);});因为lambda表达式本身没有处理异常的机制…

lambda处理异常四种方式

最近对接第三方呼叫系统&#xff0c;第三方SDK的所有方法里都有异常抛出&#xff0c;因为用到了lambda&#xff0c;所以异常处理还是很必要的。 本文主要用到了四种解决方案&#xff1a; 直接代码块处理自定义函数式接口&#xff0c;warp静态方法通过Either 类型包装通过Pair…

linux 历史命令快捷键,Linux历史命令及bash快捷键

本文环境 Centos71.历史命令1.1 在使用linux中熟练的使用历史命令&#xff0c;能给我很多便捷&#xff0c;下面我来总结一些常用的一些历史命令。a) 执行上一条命令,这个很重要 &#xff0c;执行上一条命令有四种方法&#xff0c;如下:[rootCentOS-7-64data]#[rootCentOS-7-64 …

linux的shell命令 a,linux shell命令大全(都是随堂笔记)

1. Tftp服务器(上传下载文件)注意&#xff1a;上传和下载文件时不能用目录表示 。因此&#xff0c;需要先cd到当前目录。注意目录权限&#xff1a;chmod 0777目录文件 8进制表示&#xff1a;Chmod的文件、目录的权限U自己 g 所属组 o其它 可加减步骤&#xff1a;安装服务器&…

异常 —— throws

1.1 异常概念 1.2 异常体系 java.lang.Throwable:类是 Java 语言中所有错误或异常的超类。 Exception:编译期异常,进行编译(写代码)java程序出现的问题 RuntimeException:运行期异常,java程序运行过程中出现的问题 异常就相当于程序得了一个小毛病(感冒,发烧),把异常处理掉,程序…

制作 小 linux 教程,【NanoPi NEO Plus2开发板试用体验】编译uboot和linux制作最小根文件系统制作刷机包---详细教程...

二、Linux命令基础1、查找文件查找a.c的文件 find -name "a.c"在当前目录搜索a字样的文件 grep "a" *-nR2、解压解压tar.gz文件 tar zxvf xxxxx.tar.gz解压tar.xz文件 先 xz -d xxx.tar.xz 将 xxx.tar.xz解压成 xxx.tar 然后&#xff0c;再用 tar xvf xx…

执行throw后 后面代码还会执行吗?

1.当我们对throw的对象进行try catch之后 public void re(int i) {if (i > 5){this.i i;}else {try {throw new Exception("数据非法&#xff01;");} catch (Exception e) {e.printStackTrace();}System.out.println("123");}结果显示&#xff1a;12…

linux怎么查看sklearn版本,Sklearn——Sklearn的介绍与安装

文章目录1.Sklearn简介2.Sklean安装2.1.pip安装2.2.conda安装1.Sklearn简介Scikit learn 也简称 sklearn, 是机器学习领域当中最知名的 python 模块之一.Sklearn 包含了很多种机器学习的方式:Classification 分类Regression 回归Clustering 非监督分类Dimensionality reduction…

一些防止 Java 代码被反编译的方法

由于Java字节码的抽象级别较高&#xff0c;因此它们较容易被反编译。本节介绍了几种常用的方法&#xff0c;用于保护Java字节码不被反编译。通常&#xff0c;这些方法不能够绝对防止程序被反编译&#xff0c;而是加大反编译的难度而已&#xff0c;因为这些方法都有自己的使用环…

linux 离线安装中文,linux离线安装及配置redis-Go语言中文社区

本文以centos7系统为例&#xff0c;介绍离线安装redis步骤一、环境准备检查服务器上是否存在gcc-c的环境&#xff0c;使用命令&#xff1a;rpm -qa | grep gcc-c如果没有该环境&#xff0c;则需要安装该环境&#xff0c;离线安装步骤为&#xff1a;1、获取相关rpm包&#xff0c…