1、DataSource初始化

2019-10-23  本文已影响0人  timar

这次想以一个最简单的增删改查应用来观察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,所以其他几个就不会加载了。

上一篇下一篇

猜你喜欢

热点阅读