美文网首页
1、DataSource初始化

1、DataSource初始化

作者: timar | 来源:发表于2019-10-23 18:11 被阅读0次

    这次想以一个最简单的增删改查应用来观察SQL执行过程以及事务提交回滚时机。

    SpringBoot版本是2.1.0.RELEASE,以下是pom依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
     
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
     
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
     
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>1.3.1</version>
    </dependency>
    

    进入正题之前,先提一下Condition和SpringBootCondition,Condition只有一个matches方法,返回true或false,表示条件满足或不满足

    SpringBootCondition实现了Condition,SpringBootCondition的matches方法,由ConditionOutcome来决定true或false

    public final boolean matches(ConditionContext context,
          AnnotatedTypeMetadata metadata) {
       String classOrMethodName = getClassOrMethodName(metadata);
       try {
            // getMatchOutcome返回一个ConditionOutCome,相当于匹配结果,这时候matches返回true或false已经确定了。
            // 所以SpringBootCondition的条件判断逻辑都在getMatchOutcome中
          ConditionOutcome outcome = getMatchOutcome(context, metadata);
          logOutcome(classOrMethodName, outcome);
          recordEvaluation(context, classOrMethodName, outcome);
            // ConditionOutCome有一个属性match,直接返回
          return outcome.isMatch();
       }
       catch (NoClassDefFoundError ex) {
          throw new IllegalStateException(
                "Could not evaluate condition on " + classOrMethodName + " due to "
                      + ex.getMessage() + " not "
                      + "found. Make sure your own configuration does not rely on "
                      + "that class. This can also happen if you are "
                      + "@ComponentScanning a springframework package (e.g. if you "
                      + "put a @ComponentScan in the default package by mistake)",
                ex);
       }
       catch (RuntimeException ex) {
          throw new IllegalStateException(
                "Error processing condition on " + getName(metadata), ex);
       }
    }
    

    进入本次正题。

    SpringBoot很多组件的加载都是由各种各样的*AutoConfiguration类实现,数据源也不例外。

    自动配置类是DataSourceAutoConfiguration,默认加载的数据源是HikariDataSource

    @Configuration
    @ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
    @EnableConfigurationProperties(DataSourceProperties.class)
    @Import({ DataSourcePoolMetadataProvidersConfiguration.class,
          DataSourceInitializationConfiguration.class })
    public class DataSourceAutoConfiguration {
     
       @Configuration
        // EmbeddedDatabaseConfiguration生效条件1,下面会讲
       @Conditional(EmbeddedDatabaseCondition.class)
        // EmbeddedDatabaseConfiguration生效条件2,还没有生成DataSource、XADataSource类型的实例。XADataSource是涉及分布式事务的数据源,很高大上
       @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
        // 上面两个condition都满足的话,EmbeddedDataSourceConfiguration被加载
       @Import(EmbeddedDataSourceConfiguration.class)
        // 内置数据源配置类,加载H2,DERBY,HSQL这3种默认数据源
       protected static class EmbeddedDatabaseConfiguration {
     
       }
     
       @Configuration
        // EmbeddedDatabaseConfiguration生效条件1,下面会讲
       @Conditional(PooledDataSourceCondition.class)
        // EmbeddedDatabaseConfiguration生效条件2,还没有生成DataSource、XADataSource类型的实例。XADataSource是涉及分布式事务的数据源,很高大上
       @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
        // 上面两个condition都满足的话,import以下配置类
       @Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
             DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class,
             DataSourceJmxConfiguration.class })
        // 加载池化的数据源,Hikari、Tomcat、Dbcp2、Generic都代表一种数据源
       protected static class PooledDataSourceConfiguration {
     
       }
    

    EmbeddedDatabaseCondition,加载内置数据源H2、DERBY、HSQL

    static class EmbeddedDatabaseCondition extends SpringBootCondition {
     
       private final SpringBootCondition pooledCondition = new PooledDataSourceCondition();
     
       @Override
       public ConditionOutcome getMatchOutcome(ConditionContext context,
             AnnotatedTypeMetadata metadata) {
          ConditionMessage.Builder message = ConditionMessage
                .forCondition("EmbeddedDataSource");
            // 如果PooledDataSourceCondition满足条件的话,返回不匹配
            // PooledDataSourceCondition的条件是
            // 1、在yaml文件中手动指定了数据源的类型,如spring.datasource.type=com.zaxxer.hikari.HikariDataSource
            // 2、classpath下有com.zaxxer.hikari.HikariDataSource,org.apache.tomcat.jdbc.pool.DataSource,org.apache.commons.dbcp2.BasicDataSource其中一个就表示满足条件
            // 这里先判断PooledDataSourceCondition的原因可能是PooledDataSource优先级比H2、DERBY、HSQL高。实在没得选才会加载H2、DERBY、HSQL
          if (anyMatches(context, metadata, this.pooledCondition)) {
             return ConditionOutcome
                   .noMatch(message.foundExactly("supported pooled data source"));
          }
             
            // classpath下有没有H2、DERBY、HSQL这3种类,如果有就返回
          EmbeddedDatabaseType type = EmbeddedDatabaseConnection
                .get(context.getClassLoader()).getType();
          if (type == null) {
            // classpath下找不到H2、DERBY、HSQL,返回不匹配
             return ConditionOutcome
                   .noMatch(message.didNotFind("embedded database").atAll());
          }
            // 表示加载到了H2、DERBY、HSQL中的一种
          return ConditionOutcome.match(message.found("embedded database").items(type));
       }
     
    }
    

    PooledDataSourceCondition和PooledDataSourceAvailableCondition,PooledDataSourceCondition继承自AnyNestedCondition,表示只要满一个条件即可,类似与或非的或,有一个为真即为真。
    这两个condition最后加载出了

    static class PooledDataSourceCondition extends AnyNestedCondition {
     
       PooledDataSourceCondition() {
          super(ConfigurationPhase.PARSE_CONFIGURATION);
       }
     
        // 条件1,在yaml文件中手动指定了数据源的类型,如spring.datasource.type=com.zaxxer.hikari.HikariDataSource
       @ConditionalOnProperty(prefix = "spring.datasource", name = "type")
       static class ExplicitType {
     
       }
     
        // 条件2,是另外一个条件。。
       @Conditional(PooledDataSourceAvailableCondition.class)
       static class PooledDataSourceAvailable {
     
       }
     
    }
     
    static class PooledDataSourceAvailableCondition extends SpringBootCondition {
     
       @Override
       public ConditionOutcome getMatchOutcome(ConditionContext context,
             AnnotatedTypeMetadata metadata) {
          ConditionMessage.Builder message = ConditionMessage
                .forCondition("PooledDataSource");
            // classpath下有com.zaxxer.hikari.HikariDataSource,org.apache.tomcat.jdbc.pool.DataSource,org.apache.commons.dbcp2.BasicDataSource其中一个就表示满足条件
          if (getDataSourceClassLoader(context) != null) {
             return ConditionOutcome
                   .match(message.foundExactly("supported DataSource"));
          }
            // 没有就返回不满足条件
          return ConditionOutcome
                .noMatch(message.didNotFind("supported DataSource").atAll());
       }
     
       /**
        * Returns the class loader for the {@link DataSource} class. Used to ensure that
        * the driver class can actually be loaded by the data source.
        * @param context the condition context
        * @return the class loader
        */
       private ClassLoader getDataSourceClassLoader(ConditionContext context) {
          Class<?> dataSourceClass = DataSourceBuilder
                .findType(context.getClassLoader());
          return (dataSourceClass != null) ? dataSourceClass.getClassLoader() : null;
       }
     
    }
    

    // DataSourceBuilder.findType(context.getClassLoader());
    @SuppressWarnings("unchecked")

    public static Class<? extends DataSource> findType(ClassLoader classLoader) {
        // DATA_SOURCE_TYPE_NAMES是com.zaxxer.hikari.HikariDataSource,org.apache.tomcat.jdbc.pool.DataSource,org.apache.commons.dbcp2.BasicDataSource的集合
       for (String name : DATA_SOURCE_TYPE_NAMES) {
          try {
                // 返回的是HikariDataSource.class
             return (Class<? extends DataSource>) ClassUtils.forName(name,
                   classLoader);
          }
          catch (Exception ex) {
             // Swallow and continue
          }
       }
       return null;
    }
    

    所以PooledDataSourceCondition的2个条件是

    1、在yaml文件中手动指定了数据源的类型,如spring.datasource.type=com.zaxxer.hikari.HikariDataSource

    2、classpath下有com.zaxxer.hikari.HikariDataSource,org.apache.tomcat.jdbc.pool.DataSource,org.apache.commons.dbcp2.BasicDataSource其中一个就表示满足条件

    满足其中任意一个条件都会加载以下配置类

    @Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
             DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class,
             DataSourceJmxConfiguration.class })
    
    DataSourceConfiguration.Hikari.class
    // classpath下找得到HikariDataSource类
    @ConditionalOnClass(HikariDataSource.class)
    // 当前还没有DataSource被加载
    @ConditionalOnMissingBean(DataSource.class)
    // yaml文件中指定了数据源类型为HikariDataSource,但是不指定默认也是true
    @ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource", matchIfMissing = true)
    static class Hikari {
     
        // HikariDataSource加载成功
       @Bean
       @ConfigurationProperties(prefix = "spring.datasource.hikari")
       public HikariDataSource dataSource(DataSourceProperties properties) {
          HikariDataSource dataSource = createDataSource(properties,
                HikariDataSource.class);
          if (StringUtils.hasText(properties.getName())) {
             dataSource.setPoolName(properties.getName());
          }
          return dataSource;
       }
     
    }
    

    DataSourceConfiguration.Tomcat.class, DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class类似,但是由于先加载Hikari,所以其他几个就不会加载了。

    相关文章

      网友评论

          本文标题:1、DataSource初始化

          本文链接:https://www.haomeiwen.com/subject/ynwkvctx.html