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