概述
DataSourceAutoConfiguration
是Spring Boot
关于关系型数据库数据源的自动配置,目的是在开发人员没有自定义数据源DataSource bean
组件时,根据开发人员所引入的包自动定义DataSource bean
组件。
如果开发人员使用的时嵌入式数据库H2
,Derby
或者HSQL
,则会使用EmbeddedDataSourceConfiguration
定义数据源bean
,实现了接口EmbeddedDatabase
。
否则,如果开发人员使用的不是嵌入式数据库,则会检测数据库连接组件是否支持连接池,支持的话进而使用PooledDataSourceConfiguration
定义数据源bean
。PooledDataSourceConfiguration
会参考spring.datasource.type
参数指定的数据源类以及数据源类的存在性定义相应的数据源bean
:
com.zaxxer.hikari.HikariDataSource
org.apache.tomcat.jdbc.pool.DataSource
org.apache.commons.dbcp2.BasicDataSource
缺省情况下Spring Boot
使用hikari
数据源。
源代码
源代码版本 : spring-boot-autoconfigure-2.1.3.RELEASE
package org.springframework.boot.autoconfigure.jdbc;
// 省略 import 行
/**
* EnableAutoConfiguration Auto-configuration for DataSource.
*
*/
@Configuration
// 仅在类 DataSource, EmbeddedDatabaseType 存在于 classpath 上时才生效,
// 这两个类属于包 spring-jdbc
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
// 确保前缀为 spring.datasource 的配置属性项被加载到 bean DataSourceProperties
@EnableConfigurationProperties(DataSourceProperties.class)
// 1. 导入 DataSourcePoolMetadataProvidersConfiguration , 该配置类根据
// 开发人员引入的数据库连接池实现提供相应的 DataSourcePoolMetadataProvider
// 2. 导入 DataSourceInitializationConfiguration, 该配置类会 :
// 2.1 导入 bean DataSourceInitializerInvoker,用于执行 schema-*.sql,data-*.sql
// 2.2 dataSourceInitializerPostProcessor DataSourceInitializerPostProcessor
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
// 内嵌配置类
@Configuration
// 仅在嵌入式数据库被使用时才生效
// 嵌入式数据库这里指的是 h2, derby, 或者 hsql
@Conditional(EmbeddedDatabaseCondition.class)
// 仅在没有类型为 DataSource/XADataSource 的 bean 定义时才生效
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
// 导入嵌入式数据库有关的数据源配置
// 提供前所使用的嵌入式数据库的数据源 bean EmbeddedDatabase
@Import(EmbeddedDataSourceConfiguration.class)
protected static class EmbeddedDatabaseConfiguration {
}
// 内嵌配置类
@Configuration
// 仅在类 PooledDataSourceCondition 条件满足时生效 ,
// PooledDataSourceCondition 会检测所使用的数据源组件是否支持连接池
@Conditional(PooledDataSourceCondition.class)
// 仅在没有类型为 DataSource/XADataSource 的 bean 定义时才生效
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
// 导入针对不同数据库类型数据源连接组件的数据源配置,这些配置仅在使用了相应的数据源连接
// 组件时才生效,一般开发人员只使用其中一种,所以也就只会有一个生效。
// 这些配置的目的都是为了定义一个 数据源 bean dataSource
@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class,
DataSourceJmxConfiguration.class })
protected static class PooledDataSourceConfiguration {
}
/**
* AnyNestedCondition that checks that either spring.datasource.type
* is set or PooledDataSourceAvailableCondition applies.
*/
static class PooledDataSourceCondition extends AnyNestedCondition {
PooledDataSourceCondition() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@ConditionalOnProperty(prefix = "spring.datasource", name = "type")
static class ExplicitType {
}
@Conditional(PooledDataSourceAvailableCondition.class)
static class PooledDataSourceAvailable {
}
}
/**
* Condition to test if a supported connection pool is available.
*/
static class PooledDataSourceAvailableCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("PooledDataSource");
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 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;
}
}
/**
* Condition to detect when an embedded DataSource type can be used.
* If a pooled DataSource is available, it will always be preferred to an
* EmbeddedDatabase.
*/
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");
if (anyMatches(context, metadata, this.pooledCondition)) {
return ConditionOutcome
.noMatch(message.foundExactly("supported pooled data source"));
}
EmbeddedDatabaseType type = EmbeddedDatabaseConnection
.get(context.getClassLoader()).getType();
if (type == null) {
return ConditionOutcome
.noMatch(message.didNotFind("embedded database").atAll());
}
return ConditionOutcome.match(message.found("embedded database").items(type));
}
}
}