一般情况下我们的数据库配置一般这么写
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username:
password:
url: jdbc:mysql://
从配置文件点进去找到 org.springframework.boot.autoconfigure.jdbc.DataSourceProperties 这么一个类。
@ConfigurationProperties(prefix = "spring.datasource")
匹配配置文件前缀为 spring.datasource 的值。
在 org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration中使用
@EnableConfigurationProperties(DataSourceProperties.class)
引入该配置文件。同时这个类是数据库的自动配置类。主要通过下面这一段来配置数据源
@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 {
}
PooledDataSourceConfiguration这个类上的条件也比较清楚,第二行中的PooledDataSourceCondition是一个静态内部类,第三行是没有DataSource和XADataSource这样的两个bean,第四行是导入一些类进入容器。由于@Import会按照定义的先后顺序进行导入,所以Hikari.class会优先处理。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HikariDataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@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;
}
}
所以Hikari 是DataSourceAutoConfiguration默认使用的数据源。createDataSource 源码如下
protected static <T> T createDataSource(DataSourceProperties properties, Class<? extends DataSource> type) {
return (T) properties.initializeDataSourceBuilder().type(type).build();
}
initializeDataSourceBuilder中的一些设置操作
public DataSourceBuilder<?> initializeDataSourceBuilder() {
return DataSourceBuilder.create(getClassLoader()).type(getType()).driverClassName(determineDriverClassName())
.url(determineUrl()).username(determineUsername()).password(determinePassword());
}
build
public T build() {
Class<? extends DataSource> type = getType();
DataSource result = BeanUtils.instantiateClass(type);
maybeGetDriverClassName();
bind(result);
return (T) result;
}
javax.sql.DataSource
public interface DataSource extends CommonDataSource, Wrapper {
Connection getConnection() throws SQLException;
Connection getConnection(String username, String password)
throws SQLException;
}
这个接口负责SQL连接的获取。
Hikari
https://github.com/brettwooldridge/HikariCP
HikariDataSource初始化过程
public HikariDataSource()
{
super();
fastPathPool = null;
}
super是调用HikariConfig中的构造函数。
public HikariConfig()
{
dataSourceProperties = new Properties();
healthCheckProperties = new Properties();
minIdle = -1;
maxPoolSize = -1;
//30分钟
maxLifetime = MAX_LIFETIME;
//30
connectionTimeout = CONNECTION_TIMEOUT;
//5
validationTimeout = VALIDATION_TIMEOUT;
//10分钟
idleTimeout = IDLE_TIMEOUT;
//
initializationFailTimeout = 1;
//自动提交
isAutoCommit = true;
String systemProp = System.getProperty("hikaricp.configurationFile");
if (systemProp != null) {
loadProperties(systemProp);
}
}
一些时间常量
private static final long CONNECTION_TIMEOUT = SECONDS.toMillis(30);
private static final long VALIDATION_TIMEOUT = SECONDS.toMillis(5);
private static final long IDLE_TIMEOUT = MINUTES.toMillis(10);
private static final long MAX_LIFETIME = MINUTES.toMillis(30);
可以看到并没有读取配置文件的操作。
当我们在配置文件中写下这么一段配置后框架帮我们做了什么呢?
hikari:
idle-timeout: 10000
这个值是通过setIdleTimeout方法设置的。
public void setIdleTimeout(long idleTimeoutMs)
{
if (idleTimeoutMs < 0) {
throw new IllegalArgumentException("idleTimeout cannot be negative");
}
this.idleTimeout = idleTimeoutMs;
}
setIdleTimeout方法的调用链路如下
getConnection
@Override
public Connection getConnection() throws SQLException
{
//判断数据源是否关闭
if (isClosed()) {
throw new SQLException("HikariDataSource " + this + " has been closed.");
}
//判断HikariPool连接池,不为空从池子中拿一个
if (fastPathPool != null) {
return fastPathPool.getConnection();
}
//连接池为空,创建连接池
HikariPool result = pool;
if (result == null) {
synchronized (this) {
result = pool;
if (result == null) {
validate();
LOGGER.info("{} - Starting...", getPoolName());
try {
pool = result = new HikariPool(this);
this.seal();
}
catch (PoolInitializationException pie) {
if (pie.getCause() instanceof SQLException) {
throw (SQLException) pie.getCause();
}
else {
throw pie;
}
}
LOGGER.info("{} - Start completed.", getPoolName());
}
}
}
return result.getConnection();
}
服务启动第一次请求数据库会创建HikariPool
com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
close
@Override
public void close()
{
if (isShutdown.getAndSet(true)) {
return;
}
HikariPool p = pool;
if (p != null) {
try {
LOGGER.info("{} - Shutdown initiated...", getPoolName());
p.shutdown();
LOGGER.info("{} - Shutdown completed.", getPoolName());
}
catch (InterruptedException e) {
LOGGER.warn("{} - Interrupted during closing", getPoolName(), e);
Thread.currentThread().interrupt();
}
}
}
com.zaxxer.hikari.pool.HikariPool
getConnection
public Connection getConnection() throws SQLException
{
return getConnection(connectionTimeout);
}
public Connection getConnection(final long hardTimeout) throws SQLException
{
//获取锁
suspendResumeLock.acquire();
final long startTime = currentTime();
try {
long timeout = hardTimeout;
do {
PoolEntry poolEntry = connectionBag.borrow(timeout, MILLISECONDS);
if (poolEntry == null) {
break; // We timed out... break and throw exception
}
final long now = currentTime();
if (poolEntry.isMarkedEvicted() || (elapsedMillis(poolEntry.lastAccessed, now) > aliveBypassWindowMs && !isConnectionAlive(poolEntry.connection))) {
closeConnection(poolEntry, poolEntry.isMarkedEvicted() ? EVICTED_CONNECTION_MESSAGE : DEAD_CONNECTION_MESSAGE);
timeout = hardTimeout - elapsedMillis(startTime);
}
else {
metricsTracker.recordBorrowStats(poolEntry, startTime);
return poolEntry.createProxyConnection(leakTaskFactory.schedule(poolEntry), now);
}
} while (timeout > 0L);
metricsTracker.recordBorrowTimeoutStats(startTime);
throw createTimeoutException(startTime);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new SQLException(poolName + " - Interrupted during connection acquisition", e);
}
finally {
suspendResumeLock.release();
}
}