本文基于SpringBoot2.2.8
数据库相关的配置是一个项目最基本的配置,在使用SpringBoot自动配置数据库是什么原理?
首先对于JAVA而言,是有一个统一的标准JDBC,无论是MySQL还是Oracle,都会尽量去按照JDBC标准提供驱动,同时JAVA应用程序也会按照JDBC标准来使用连接。
数据库使用也是分层次的,以MySQL为例,先是保证连接的驱动层,通常是mysql-connector.jar,这个驱动是MySQL官方提供,可以到MySQL官网下载到,然后是应用连接层,这里可以使用各种数据库连接池,用于管理和数据库之间的连接,再往上就是建立在连接之上SQL使用方式,这一层的代表是spring-jdbc包,是Spring对jdbc的封装,提供给用户使用的,当然这一层的可用性不是那么友好,对于有大量查询的应用,使用起来还是比较麻烦,再往上就是mybatis之类的ORM框架了,他们提供了以对象方式来操作数据库,大大提升了开发效率。在这些分层的体系中,数据源(datasource)这个概念是最常见的,他是JDBC标准中定义的,对操作数据库的一个抽象,数据源提供获取数据库连接的方法,所有他是一个承上启下的层,确定了数据源就确定了数据库,也确定了使用的应用。
一、DataSource自动配置
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
数据库使用的第一步是配置数据源,这里的数据源通常都是带了连接池的,SpringBoot2默认使用Hikari连接池。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class }) // 引入jdk中java.sql包,引入了spring-jdbc包
@EnableConfigurationProperties(DataSourceProperties.class) // 使用spring.datasource配置
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@Conditional(EmbeddedDatabaseCondition.class) // 取决于指定类的matches()方法
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import(EmbeddedDataSourceConfiguration.class) // 导入内置数据源配置
protected static class EmbeddedDatabaseConfiguration {
// 空类纯粹是为了导入配置类
}
@Configuration(proxyBeanMethods = false)
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class,
DataSourceJmxConfiguration.class }) // 导入多个连接池配置,能不能加载还要看各个具体数据源的加载条件
protected static class PooledDataSourceConfiguration {
// 空类纯粹是为了导入配置类
}
// 下面的3个内部类,都是为了检查是否加载的条件
/**
* {@link AnyNestedCondition} that checks that either {@code spring.datasource.type}
* is set or {@link 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 {
}
}
/**
* {@link 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 (DataSourceBuilder.findType(context.getClassLoader()) != null) {
return ConditionOutcome.match(message.foundExactly("supported DataSource"));
}
return ConditionOutcome.noMatch(message.didNotFind("supported DataSource").atAll());
}
}
/**
* {@link Condition} to detect when an embedded {@link DataSource} type can be used.
* If a pooled {@link DataSource} is available, it will always be preferred to an
* {@code EmbeddedDatabase}.
*/
static class EmbeddedDatabaseCondition extends SpringBootCondition {
private static final String DATASOURCE_URL_PROPERTY = "spring.datasource.url";
private final SpringBootCondition pooledCondition = new PooledDataSourceCondition();
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage.forCondition("EmbeddedDataSource");
if (hasDataSourceUrlProperty(context)) {
return ConditionOutcome.noMatch(message.because(DATASOURCE_URL_PROPERTY + " is set"));
}
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));
}
private boolean hasDataSourceUrlProperty(ConditionContext context) {
Environment environment = context.getEnvironment();
if (environment.containsProperty(DATASOURCE_URL_PROPERTY)) {
try {
return StringUtils.hasText(environment.getProperty(DATASOURCE_URL_PROPERTY));
}
catch (IllegalArgumentException ex) {
// Ignore unresolvable placeholder errors
}
}
return false;
}
}
}
二、