[源码]HikariCP浅析1-SpringBoot与HikariCP

353

背景

HikariCP现如今已经是spring-boot-2.*数据库连接默认的连接池组件,在这篇博客会演示基本使用以及HikariCPDataSource实例化过程。
项目主要依赖: spring-data-jdbc + hikari。
主要会涉及到以下内容:

  1. Hikari如何被Spring加载的
  2. 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>

初始化

  1. spring项目的启动将会扫描spring-boot-autoconfigure-*.jar包的spring.factories,获取配置的org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration自动装配类,并加载。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
  1. 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();
	}
}

优秀的参考文章

  1. 带你读《HikariCP数据库连接池实战》之三:初识HikariCP
  2. Springboot 2.0选择HikariCP作为默认数据库连接池的五大理由