[源码]HikariCP浅析1-SpringBoot与HikariCP
背景
HikariCP现如今已经是spring-boot-2.*数据库连接默认的连接池组件,在这篇博客会演示基本使用以及HikariCPDataSource实例化过程。
项目主要依赖: spring-data-jdbc + hikari。
主要会涉及到以下内容:
- Hikari如何被Spring加载的
- HikariDataSource如何被使用的
依赖与配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
初始化
- spring项目的启动将会扫描spring-boot-autoconfigure-*.jar包的spring.factories,获取配置的org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration自动装配类,并加载。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
- DataSourceAutoConfiguration中开始了对数据库连接以及相关连接池和健康检查等内容的初始化。以下的方法决定了默认初始化的优先级,Hikari作为默认的初始化,如果Hikari初始化完成后后续的其它连接池将不会被初始化(如,DataSourceConfiguration.Tomcat的初始化需要无DataSource的实例,如果DataSourceConfiguration.Hikari的DataSource初始化完成就不符合DataSourceConfiguration.Tomcat初始化的条件了)。
Hikari -> Tomcat -> Dbcp2 -> Generic
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)
//引入新的需要加载的配置DataSourcePoolMetadataProvidersConfiguration,DataSourceInitializationConfiguration
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
//指定连接池 初始化的优先级
@Configuration(proxyBeanMethods = false)
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class,
DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class })
protected static class PooledDataSourceConfiguration {
}
}
abstract class DataSourceConfiguration {
//初始化 Hikari 连接池
@Configuration(proxyBeanMethods = false)
//必须要存在HikariDataSource的依赖
@ConditionalOnClass(HikariDataSource.class)
//上下文没有DataSource实例,所以如果我们以及在项目内部构建了DataSource对象,则Hikari就不会再初始化了
@ConditionalOnMissingBean(DataSource.class)
//spring.datasource.type 配置为 com.zaxxer.hikari.HikariDataSource或者,不配置,则使用Hikari作为默认
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource",
matchIfMissing = true)
static class Hikari {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
HikariDataSource dataSource(DataSourceProperties properties) {
HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class);
if (StringUtils.hasText(properties.getName())) {
dataSource.setPoolName(properties.getName());
}
return dataSource;
}
}
}
//元数据相关内容:监控、健康、状态
@Configuration(proxyBeanMethods = false)
public class DataSourcePoolMetadataProvidersConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HikariDataSource.class)
static class HikariPoolDataSourceMetadataProviderConfiguration {
//构建了数据库连接池信息与状态的"提供对象":DataSourcePoolMetadataProvider,spring可以通过这个对象构建信息对象,对数据库连接池做监控以及状态的查看,包括健康检查等。
@Bean
DataSourcePoolMetadataProvider hikariPoolDataSourceMetadataProvider() {
return (dataSource) -> {
HikariDataSource hikariDataSource = DataSourceUnwrapper.unwrap(dataSource, HikariDataSource.class);
if (hikariDataSource != null) {
return new HikariDataSourcePoolMetadata(hikariDataSource);
}
return null;
};
}
}
/**
* Configures DataSource initialization.
*
* @author Stephane Nicoll
*/
//数据库初始化相关内容:初始化话过程中需要执行的sql或者表结构更改、刷数据等
@Configuration(proxyBeanMethods = false)
@Import({ DataSourceInitializerInvoker.class, DataSourceInitializationConfiguration.Registrar.class })
class DataSourceInitializationConfiguration {
/**
* {@link ImportBeanDefinitionRegistrar} to register the
* {@link DataSourceInitializerPostProcessor} without causing early bean instantiation
* issues.
*/
static class Registrar implements ImportBeanDefinitionRegistrar {
private static final String BEAN_NAME = "dataSourceInitializerPostProcessor";
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(BEAN_NAME)) {
AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
.genericBeanDefinition(DataSourceInitializerPostProcessor.class,
DataSourceInitializerPostProcessor::new)
.getBeanDefinition();
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// We don't need this one to be post processed otherwise it can cause a
// cascade of bean instantiation that we would rather avoid.
beanDefinition.setSynthetic(true);
registry.registerBeanDefinition(BEAN_NAME, beanDefinition);
}
}
}
}
使用
由于上面章节构建出来的HikariDatasource对象已经注入到Spring上下文当中。接下来是被项目使用或者被其它组件使用。
原始Connection例子
代码位置:https://github.com/Wanxp/hikaricpdemo
@SpringBootApplication
@Slf4j
public class HikaricpdemoApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(HikaricpdemoApplication.class, args);
}
//注入上下文中的Hikari datasource
@Autowired
private DataSource dataSource;
static String sql = "select id, name, birthday, active_level from user";
@Override
public void run(String... args) throws Exception {
log.warn("input is empty");
//使用HikariDatasource
Connection connection = dataSource.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql);
ResultSet resultSet = preparedStatement.executeQuery();
int row = 0;
while (resultSet.next()) {
Long id = resultSet.getLong("id");
String name = resultSet.getString("name");
Date birthday = resultSet.getDate("birthday");
Boolean active = resultSet.getBoolean("active_level");
log.warn("row[{}]: id:{}, name:{}, birthday:{}, active:{}", row++, id, name, birthday, active);
}
preparedStatement.close();
connection.close();
}
}