3.2 SpringBoot数据库连接池加载顺序剖析
HikariCP是Spring Boot 2.x版本官方宣布默认的数据库连接池,SpringBoot对HikariCP的采纳也从侧面说明HikariCP在使用广泛性、认可度、性能、稳定性等方面是经得起检验的,也表明HikariCP的应用前景和未来发展是一片光明。
如图3-2所示,在SpringBoot当前最新2.x版本的文档中也强烈建议使用HikariCP作为默认的数据库连接池。文档中是这么描述的:我们更喜欢HikariCP的性能和并发性,如果HikariCP可用,我们总是选择它;否则,如果Tomcat池DataSource可用,我们将使用它;如果HikariCP和Tomcat数据库连接池都不可用,并且Commons DBCP2可用,我们就会使用它。当然,用户也可以通过设置spring.datasource.type属性来绕过上述使用顺序,进而指定想要使用的数据库连接池。
图3-2 SpringBoot最新版本建议使用HikariCP
当然,如果这维服务使用spring-boot-starter-jdbc或者spring-boot-starter-data-jpa的starter,会自动获得HikariCP的依赖。
其实Spring Boot 1.5.x是默认使用tomcat-jdbc作为连接池的,如下是其寻找连接池顺序的规则:Tomcat pool → HikariCP → Commons DBCP → Commons DBCP2。然而在其2.x版本中,HikariCP被提升为默认的数据库连接池,数据库连接池的默认配置顺序是HikariCP → Tomcat pool → Commons DBCP2。接下来,我们就从源码角度给读者详细介绍这个规则的原理。
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration是Spring Boot 2.0.3.Release版本中的加载数据源的核心类。在该类中,PooledDataSource-Configuration是需要重点关注的,它决定了连接池的选择。我们可以看到图3-2中的注解包含了HikariCP、Tomcat、DBCP2等主流连接池,选择连接池的不同,application. properties配置也不同。
@Configuration @ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) @EnableConfigurationProperties(DataSourceProperties.class) @Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class }) public class DataSourceAutoConfiguration { @Configuration @Conditional(EmbeddedDatabaseCondition.class) @ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) @Import(EmbeddedDataSourceConfiguration.class) protected static class EmbeddedDatabaseConfiguration {} @Configuration @Conditional(PooledDataSourceCondition.class) @ConditionalOnMissingBean({ DataSource.class, XADataSource.class }) @Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat. class, DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic. class, DataSourceJmxConfiguration.class }) protected static class PooledDataSourceConfiguration {}
在上述PooledDataSourceCondition的代码中,PooledDataSourceAvailableCondition有一段findType的代码用来加载资源文件。
public static Class<? extends DataSource> findType(ClassLoader classLoader){ for (String name : DATA_SOURCE_TYPE_NAMES) { try { return (Class<? extends DataSource>) ClassUtils.forName(name, classLoader); }catch (Exception ex) { // Swallow and continue } } return null; }
而这里的常量数组DATA_SOURCE_TYPE_NAMES就是关键,在Spring Boot 2.x的定义如下:
private static final String[] DATA_SOURCE_TYPE_NAMES = new String[] { "com.zaxxer.hikari.HikariDataSource", "org.apache.tomcat.jdbc.pool.DataSource", "org.apache.commons.dbcp2.BasicDataSource" };
在findType()方法中,for循环顺序遍历数组,加载到哪个类就使用哪个类的连接池,所以就是HikariCP → Tomcat pool → Commons DBCP2这样的顺序。
同理,在1.5.3.Release版本的SpringBoot的数组也是这么定义的,所以就是Tomcat Pool → HikariCP → Commons DBCP → Commons DBCP2的顺序。
private static final String[] DATA_SOURCE_TYPE_NAMES = new String[] { "org.apache.tomcat.jdbc.pool.DataSource", "com.zaxxer.hikari.HikariDataSource", "org.apache.commons.dbcp.BasicDataSource", // deprecated "org.apache.commons.dbcp2.BasicDataSource" };
所以若要在SpringBoot 1.5.3.Release版本中使用HikariCP,除了在spring.datasource. type属性中设置以外,还有一个简单的方法就是Maven依赖排除Tomcat-JDBC。
SpringBoot 2.0发布的时候,官方也发布了“db方面,默认引入了HikariCP,替代了之前的Tomcat-Pool作为底层的数据库连接池。对比Tomcat-Pool, HikariCP拥有更好的性能,总而言之就是提高了db的访问速度”这样的版本变更信息。读到这里读者应该能够清晰地理解SpringBoot在数据库连接池选型这件事上的来龙去脉了。