深入浅出DataSourceAutoConfiguration

一般情况下我们的数据库配置一般这么写

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();
      }
   }
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值