• Spring Boot JDBC:加载DataSource过程的源码分析及yml中DataSource的配置


    装载至:https://www.cnblogs.com/storml/p/8611388.html

    Spring Boot实现了自动加载DataSource及相关配置。当然,使用时加上@EnableAutoConfiguration注解是必须的。下面就是对这一部分的源码分析。

    (1)Spring Boot启动后会调用org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration。下面是部分源码。

    复制代码
     1 @Configuration
     2 @ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
     3 @EnableConfigurationProperties(DataSourceProperties.class)
     4 @Import({ DataSourcePoolMetadataProvidersConfiguration.class,
     5         DataSourceInitializationConfiguration.class })
     6 public class DataSourceAutoConfiguration {
     7 
     8     @Configuration
     9     @Conditional(EmbeddedDatabaseCondition.class)
    10     @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
    11     @Import(EmbeddedDataSourceConfiguration.class)
    12     protected static class EmbeddedDatabaseConfiguration {
    13     }
    14 
    15     @Configuration
    16     @Conditional(PooledDataSourceCondition.class)
    17     @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
    18     @Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
    19             DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class,
    20             DataSourceJmxConfiguration.class })
    21     protected static class PooledDataSourceConfiguration {
    22     }
    23     ......
    24 }
    复制代码

    我们从中可以看出,DataSourceAutoConfiguration中有两个嵌套类,一个是EmbeddedDatabaseConfiguration,另一个是PooledDataSourceConfiguration

    EmbeddedDatabaseConfiguration表示已经嵌入Spring Boot的DataSource,除了Maven中加入相应的Driver,可以不做其他额外配置就能使用。从EmbeddedDatabaseType类可以看出,Spring Boot的内嵌DataSource支持HSQL,H2,DERBY这三种DB。

    PooledDataSourceConfiguration表示Spring Boot还支持一些实现Pool的DataSource。从org.springframework.boot.jdbc.DataSourceBuilder中可以看出,当前版本的Spring Boot(2.0)只支持com.zaxxer.hikari.HikariDataSource,org.apache.tomcat.jdbc.pool.DataSource,org.apache.commons.dbcp2.BasicDataSource。其中,性能更加优秀的HikariDataSource是Spring Boot的默认选择(DataSourceBuilderDATA_SOURCE_TYPE_NAMES[0] = com.zaxxer.hikari.HikariDataSource)。所以,当application.yml文件中做如下配置时,Spring Boot默认使用HikariDataSource数据库连接池。

    复制代码
    spring:
      datasource:
        url: jdbc:mysql://localhost:3306/sas
        username: root
        password: ****
        driver-class-name: com.mysql.jdbc.Driver
        #type: com.zaxxer.hikari.HikariDataSource
    复制代码

    (2)我们以HikariDataSource举例,接下来调用PooledDataSourceConfigurationorg.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration抽象类的Hikari嵌套类(DataSourceConfiguration抽象类的一个实现类)。

    复制代码
     1 abstract class DataSourceConfiguration {
     2 
     3     @SuppressWarnings("unchecked")
     4     protected <T> T createDataSource(DataSourceProperties properties,
     5             Class<? extends DataSource> type) {
     6         return (T) properties.initializeDataSourceBuilder().type(type).build();
     7     }
     8 
     9     /* Omit Tomcat Pool DataSource configuration.*/
    10     /**
    11      * Hikari DataSource configuration.
    12      */
    13     @ConditionalOnClass(HikariDataSource.class)
    14     @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource", matchIfMissing = true)
    15     static class Hikari extends DataSourceConfiguration {
    16 
    17         @Bean
    18         @ConfigurationProperties(prefix = "spring.datasource.hikari")
    19         public HikariDataSource dataSource(DataSourceProperties properties) {
    20             HikariDataSource dataSource = createDataSource(properties,
    21                     HikariDataSource.class);
    22             if (StringUtils.hasText(properties.getName())) {
    23                 dataSource.setPoolName(properties.getName());
    24             }
    25             return dataSource;
    26         }
    27     }
    28     /* Omit DBCP DataSource configuration.*/
    29 }
    复制代码

     我们从黄色部分可以看出,当application.yml文件中配置spring.datasource.type = com.zaxxer.hikari.HikariDataSource时,会使用HikariDataSource作为数据库连接池(当然上面也分析了,它是默认选择)。我们从绿色部分可以看出它的配置信息主要从两个类中读取,一个是org.springframework.boot.autoconfigure.jdbc.DataSourceProperties,另一个则是本类HikariDataSource的父类com.zaxxer.hikari.HikariConfig

    @ConfigurationProperties(prefix = "spring.datasource")
    public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {}
    @ConfigurationProperties(prefix = "spring.datasource.hikari")
    public HikariDataSource dataSource(DataSourceProperties properties) {}

    我们从@ConfigurationProperties配置及两个具体的类所包含的的域可以得出配置HikariDataSource信息。下面是例子。

    复制代码
    spring:
      datasource:
        name: #Name of the datasource. Default to "testdb" when using an embedded database.
        driverClassName: #Fully qualified name of the JDBC driver. Auto-detected based on the URL by default.
        url: #DBC URL of the database.
        type: #Fully qualified name of the connection pool implementation to use. By default, it is auto-detected from the classpath.
        username: #Login username of the database.
        password: #Login password of the database.
        ## For more details please see DataSourceProperties.
        hikari:
          connectionTimeout:
          validationTimeout:
          maxPoolSize:
          minIdle:
          dataSourceProperties:
          ## For more details please see HikariConfig.
    复制代码

    (3)当读完配置后,则会通过HikariDataSource.getConnection()方法创建HikariPool对象。HikariPool及其父类PoolBase做了许多复杂的工作,包括创建Pool,创建Connection,读取Config,验证等等。调用HikariDataSource.getConnection()方法最终得到了这个Connection对象。这个过程中主要做了以下几步:

    ① 创建HikariPool对象。

    ② 调用HikariPool对象的父类对象PoolBase的构造器,读取HikariConfig配置信息配置PoolBase的属性。

    ③ 调用PoolBase的构造器的initializeDataSource方法,利用com.zaxxer.hikari.util.DriverDataSource创建DataSource对象(这里主要指JDBC URL方式)。DriverDataSource中会把所有的DataSource信息封装到driverProperties属性中,这是为了适配java.sql.Driverconnect(String url, java.util.Properties info)方法。

    复制代码
     1 public final class DriverDataSource implements DataSource {
     2 
     3     private final String jdbcUrl;
     4     private final Properties driverProperties;
     5     private Driver driver;
     6 
     7     public DriverDataSource(String jdbcUrl, String driverClassName, Properties properties, String username, String password) {
     8         this.jdbcUrl = jdbcUrl;
     9         this.driverProperties = new Properties();
    10         Iterator e = properties.entrySet().iterator();
    11 
    12         while(e.hasNext()) {
    13             Entry driverClass = (Entry)e.next();
    14             this.driverProperties.setProperty(driverClass.getKey().toString(), driverClass.getValue().toString());
    15         }
    16 
    17         if(username != null) {
    18             this.driverProperties.put("user", this.driverProperties.getProperty("user", username));
    19         }
    20 
    21         if(password != null) {
    22             this.driverProperties.put("password", this.driverProperties.getProperty("password", password));
    23         }
    24     ......
    25     }
    26 
    27    @Override
    28    public Connection getConnection() throws SQLException
    29    {
    30       return driver.connect(jdbcUrl, driverProperties);
    31    }
    32 }
    复制代码

    ④ 调用HikariPool对象的构造器,同样也是配置一堆线程池信息。

    ⑤ 返回HikariPool.getConnection()。这个过程中,做了包含PoolBase.newPoolEntry()PoolBase.newConnection()的许多复杂方法。从PoolBase.newConnection()可以看出,最终还是调用的步骤③的getConnection()方法获取到了这个Connection对象。

    复制代码
     1    private Connection newConnection() throws Exception
     2    {
     3       final long start = currentTime();
     4 
     5       Connection connection = null;
     6       try {
     7          String username = config.getUsername();
     8          String password = config.getPassword();
     9 
    10          connection = (username == null) ? dataSource.getConnection() : dataSource.getConnection(username, password);
    11          if (connection == null) {
    12             throw new SQLTransientConnectionException("DataSource returned null unexpectedly");
    13          }
    14 
    15          setupConnection(connection);
    16          lastConnectionFailure.set(null);
    17          return connection;
    18       }
    19       catch (Exception e) {
    20          if (connection != null) {
    21             quietlyCloseConnection(connection, "(Failed to create/setup connection)");
    22          }
    23          else if (getLastConnectionFailure() == null) {
    24             LOGGER.debug("{} - Failed to create/setup connection: {}", poolName, e.getMessage());
    25          }
    26 
    27          lastConnectionFailure.set(e);
    28          throw e;
    29       }
    30       finally {
    31          // tracker will be null during failFast check
    32          if (metricsTracker != null) {
    33             metricsTracker.recordConnectionCreated(elapsedMillis(start));
    34          }
    35       }
    36    }
    复制代码
  • 相关阅读:
    How to become a hacker
    逻辑地址、线性地址、物理地址和虚拟地址
    java configuration
    Java 理论与实践: 变还是不变?
    Teach Yourself Programming in Ten Years
    border padding margin , the difference among them
    hashCode方法,equals方法,HashSet,HasMap之间的关系
    工程名 显示红色叹号
    记The_C_Programming_Language的错误
    VIM简单介绍学习1
  • 原文地址:https://www.cnblogs.com/mkl34367803/p/11632846.html
Copyright © 2020-2023  润新知