自动配置内部实现解析
上节我们了解了 DataSourceAutoConfiguration 自动配置的注解部分,本节继续深入讲解该类中的内部实现。
DataSourceAutoConfiguration中共有5个静态内 部 类 ,包括EmbeddedDatabaseConfiguration 和 PooledDataSourceConfiguration 两 个 声 明 有@Configuration 注解的自动配置类,以及另外 3 个限制条件类:
PooledDataSourceCondition、 PooledDataSourceAvailableCondition、Em-beddedDatabaseCondition。
下面,将对以上涉及内容进行详解。
EmbeddedDatabaseConfiguration
首先我们来看内部类 EmbeddedDatabaseConfiguration,该类其实并没有方法实现,它的主要功能是通过@Import 引入类来完成,源代码如下。
代码语言:javascript复制@Configuration(proxyBeanMethods = false)
@Conditional( EmbeddedDatabaseCondition. class)
@ConditionalOnMissingBean({ DataSource. class, XADataSource. class })
@Import ( EmbeddedDataSourceConfiguration. class)
protected static class EmbeddedDatabaseConfiguration {
}
@Conditional使用了 DataSourceAutoConfiguration的内部类EmbeddedDatabaseCondition 来进行条件断 EmbeddedDatabaseCondition 主要用来检测何时可以使用内嵌 DataSource,如果已经存在池化(pooled) 的 DataSource, 该类则不会被实例化,优先选择池化 Data-Source。
代码语言:javascript复制static class EmbeddedDatabaseCondition extends Spr ingBootCondition {
private final SpringBootCondition pooledCondition = new PooledDataSource-
Condition();
@Override
public ConditionOutcome ge tMatchOutcome (ConditionContext context, Anno-
tatedTypeMetadata metadata) {
ConditionMessage . Builder message = ConditionMessage . forCondition("Emb-
eddedD
ataSource");
//是否支持池化的数据源,支 持则返回不匹配
if (anyMatches(context, metadata, this. pooledCondition)) {
retu~n Condit ionOutcome . noMatch(message . foundExactly(" supported
pooled data source"));//基于枚举类 EmbeddedDatabaseType,通过类加裁器获得嵌入的数据库连接信息EmbeddedDatabaseType type = EmbeddedDatabaseConnection. get(context. get-ClassLoader()). getType();
if (type == null) {
return ConditionOutcome . noMatch(message . didNotF ind(" embedded database").
atAll());
//如果枚举类中存在,则返回匹配
return Condit ionOutcome . match(message . found(" embedded database") . items
(type));
}
}
在 EmbeddedDatabaseCondition 中首先看其属性 SpringBootCondition 的初始化,首先创建了一个 PooledDataSourceCondition,该类同样是 DataSourceAutoConfiguration 的内部类,继承自 AnyNestedCondition。
AnyNestedCondition 主要用于内嵌类的条件匹配场景。
PooledDataSourceCondition 类的主要作用是检查是否设置了 spring.datasource.type 或 DataSourceAutoConfiguration.PooledDataSourceAvailableCondition,下 面为该类的源代码。
代码语言:javascript复制static class PooledDataSourceCondition extends AnyNestedCondition {
PooledDataSourceCondition() {
//没置 condition 的配置阶段
// @Configurat ion 注解的类解析阶段判断 Condition
//如果 Condition 不匹配,@Configurat ion 注解的类不会加裁 super(ConfigurationPhase .
PARSE_ _CONF IGURATION);
// spring. datasource. type 配置条件判断
@ConditionalOnProperty(prefix = "spring . datasource", name = "type")
static class ExplicitType {
/内部类 PooledDataSourceAvailableCondition 作为条件判断
@Conditional(PooledDataSourceAvailableCondition. class)
static class PooledDataSourceAvailable {
PooledDataSourceCondition 的构造方法中调用父类构造方法并传递枚举
类 Configuration-Phase 的 PARSE_ CONFIGURATION 值,表示被
@Configuration 注解的类在解析阶段的判断条件,如果 Condition 不匹配, 则
@Configuration 注解的类不会加载。
其中PooledDataSourceAvailable类的注解又用到了
PooledDataSourceAvailableCondi-tion 同样为 DataSourceAutoConfiguration 的内部类。static class PooledDataSourceAvailableCondition extends SpringBootCondition
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, Annotated-
TypeMetadata metadata) {
ConditionMessage . Builder message = ConditionMessage . forCondition("Poole
dDataSource" );
//检查指定的类加裁器中是否存在默认指定的数据源,存在则返回匹配
if (DataSourceBuilder. findType( context . getClassLoader()) != null)
return ConditionOutcome . match( message . foundExactly(”supported DataSou
rce"));
return ConditionOutcome . noMatch(message . didNotFind("supported DataSourc
e").
}
}
PooledDataSourceAvailableCondition 的判断逻辑非常简单,就是检查当前类加载器中是否存在指定的数据源对象。在判断的过程中使用到了 DataSourceBuilder 的 findType 方法。
我们看一下相关代码, 加深理解。
代码语言:javascript复制public final class DataSourceBuilder<T extends DataSource> {
private static final String[] DATA_ SOURCE _TYPE_ NAMES = new String[] { "co
m. zaxxer. hikari. HikariDataSource",
"or
g. apache . tomcat. jdbc. pool.DataSource",' org . apache. commons . dbcp2.
Ba
sicDataSource" };
@S
uppressWarnings("unchecked")
public static Class<? extends DataSource> findType(ClassLoader classLoader) {
For (String name : DATA_ SOURCE TYPE_ NAMES) {
try {
return (Class<? extends DataSource>) ClassUtils . forName(name ,
classLoader);
} catch (Exception ex) {
//忽略,继续执行
turn null;
}}
判断方法遍历了所支持的数据源类型( HikariDataSource、DataSource和BasicData-Source)的常量数组,然后分别通过类加载器进行加载。如果存在对应的类,则返回对应的 Class, 否则返回 null。
了 解 了 内 部 类 PooledDataSourceCondition 之 后 , 我 们 继 续 看EmbeddedDatabaseCondition 的判断逻辑。在 getMatchOutcome 方法中, 第一个便是根据 PooledDataSourceCondition 判断是否存在支持池化的数据源,存在则返回不匹配。然后 , 判 断 是 否 存 在 适 合 的 内 嵌 数据 库类型 , 该判断是通过枚举类 EmbeddedDatabaseConnection 实现的。
代码语言:javascript复制public enum EmbeddedDatabaseConnection {
// 2
改旺坡
NONE(null, null, null),
// H2 数据库链接
H2(EmbeddedDatabaseType .H2,"org.h2 . Driver", "jdbc :h2:mem:%s;DB_ CLOSE_
DELAY=-1;DB_ CLOSE_ _ON_ EXIT=FALSE"),
// Derby 数据库链接
DERBY( EmbeddedDatabaseType . DERBY, "org . apache . derby . jdbc .
EmbeddedDrive
",
"jdbc : derby: memory:%s; create=true"),
// HSQL 数据库链接
HSQL (EmbeddedDatabaseType . HSQL, "org. hsqldb. jdbcDriver", "jdbc:hsqldb:
mem:%s");
public static EmbeddedDatabaseConnection get(ClassLoader classLoader)
for ( EmbeddedDatabaseConnection candidate : EmbeddedDatabaseConne -
ction. values()) {
if (candidate != NONE && ClassUtils. isPresent(candidate . getDriver-
ClassName(), classLo
ader)) {
return candidate;
return NONE ;}}
枚举类 EmbeddedDatabaseConnection 中定义了支持的数据库连接类型、驱动类名、urI以及相关工具的方法。通过枚举项的定义,我们也可以看出 Spring Boot 内嵌的 DataSource支持 HSQL、H2、 DERBY 这 3 种数据库。
程序在调用 get 方法时会遍历枚举类中定义的枚举项,然后尝试加载驱动类名来判断该类是否存在。如果存在则返回对应的 EmbeddedDatabaseConnection 枚举项;如果不存在,则返回 NONE。
在 EmbeddedDatabaseCondition 的 代 码 中 , 通 过 get 方 法 先 获 得 EmbeddedDatabaseConnection,然后通过 getType 方法获得 EmbeddedDatabaseType类型,判断其是否为 null。如果为 null, 则表示该类加载器中不存在默认的内嵌数据库类型,返回不匹配。经过以上两轮判断之后,其他情况则表示匹配。
通这 EmbeddedDatabaseCondition 上@Conditional( EmbeddedDatabaseCondition.class)注解,我们已经把 DataSourceAutoConfiguration 中关于 Condition 的内部类讲解完毕。下面 继 续 看 EmbeddedDatabaseCondition 上 的 通 过 @Import 引 入 的 配 置 类EmbeddedDataSource-Configuration。
EmbeddedDataSourceConfiguration 的主要作用是对内嵌数据源进行配置。由于该类需要用到类加载器,因此实现了 BeanClassLoaderAware,将 ClassL oader 暴露出来了。
代码语言:javascript复制@Configuration(proxyBeanMethods = false)
@EnableConf igurationProperties (DataSourceProperties . class)
public class EmbeddedDataSourceConfiguration implements BeanClassL oaderAwar
e{
private ClassLoader classLoader;
@0verride
public void setBeanClassLoader(ClassLoader classLoader) {
this. classLoader = classLoader;
@Bean(destroyMethod = "shutdown")
public EmbeddedDatabase dataSource (DataSourceProperties properties) {
return new EmbeddedDatabaseBuilder(). setType( EmbeddedDatabaseConnec -
tion. get(this. classLoade
r). getType())
. setName(properties . determineDatabaseName()). build();
}
}
我们重点看以上代码中的 dataSource 方法,该方法的注解指定了销毁方法为“shutdown”,也 就 是 EmbeddedDatabase 的 shutdown 方 法 。在 方 法 内 部 , 首 先 创 建 了 一 个Embedded-DatabaseBuilder,用于构建内嵌数据库 EmbeddedDatabase。根据命名可知Embedded-DatabaseBuilder 是可以链式调用的。
因此,EmbeddedDatabaseBuilder 连续调用 了设置数据库类型(. 上面已经讲到获取实现)、设置内嵌数据库名称。最后,通过 build 方法完成 EmbeddedDatabase 的构建,并注入容器。
至此,关于 EmbeddedDatabaseConfiguration 相关的 自动配置已经讲解完毕。在下节,我们将继续学习池化的数据源配置类 PooledDataSourceConfiguration。
PooledDataSourceConfiguration
除了支持内嵌的 DataSource,SpringBoot 还支持一些实现 Pool 的 DataSource。从上节讲到 的 DataSourceBuilder 的 静 态 数 组 可 以 看 出 , 目 前 支 持 com.zaxxer.hikari.Hikari-DataSourceorg.apache.tomcat.jdbc.pool.DataSource和org.apache.commons.dbcp2. Basic-DataSource 这 3 种 DataSource 。而 性 能 更 加 优 秀 的HikariDataSource 作为了 Spring Boot 中的默认选项。
在 DataSourceAutoConfiguration 中的 PooledDataSourceConfiguration 就是来完成实现Pool 的 DataSource 的实例化的,源代码如下。
代码语言:javascript复制@Configuration(proxyBeanMethods = false)
@Conditional (PooledDataSourceCondition.class)
@ConditionalOnMissingBean({ DataSource. class, XADataSource.class })
@Import({ DataSourceConfiguration . Hikari. class, DataSourceConfiguration. Tom
cat.class,
DataSourceConfiguration. Dbcp2. class, DataSourceConfiguration. Generic. class,
DataSourceJmxConfiguration.class })
protected static class PooledDataSourceConfiguration {}
}
PooledDataSourceConfiguration 中同样没有具体实现。@Conditional 的筛选条件也是由内部类 PooledDataSourceCondition 来完成的,这些内容前面已经讲过,这里不再赘述。该实例化对象的优先级要高于内嵌 DataSource 的。
我们重点看@lmport 引入的前 4 个类,它们是 DataSourceConfiguration 的内部类,提供了Hikari、Tomcat、 Dbcp2、 Generic 的 DataSource 配置。
DataSourceConfiguration 就 是 用 于 DataSourceAutoConfiguration 导 入 的 实 际 的DataSource 配置,这里我们以 Hikari 为例来进行讲解。Hikari 是 spring-boot-starter-jdbc默认弓|入的数据源,Hikari 相关自动配置代码如下。
代码语言:javascript复制abstract class DataSourceConfiguration
@SuppressWarnings ("unchecked" )
protected static <T> T createDataSource(DataSourceProperties properties,
Class<? extends DataSource> type )
return (T) properties . initializeDataSourceBuilder() . type(type) . buildQ);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass (HikariDataSource.class)
@Conditiona lOnMiss ingBean(DataSource.class)
@ConditionalOnProperty(name = "spring . datasource. type",
havingValue =”com. zaxxer. hikari. HikariDataSource",
matchIfMissing = true)
static class Hikari {
@Bean
@ConfigurationProperties(prefix = "spring . datasource . hikari")
public HikariDataSource dataSource(DataSourceProperties properties) {
HikariDataSource dataSource = createDataSource(properties, HikariData
Source.class);
if (StringUtils . hasText(properties . getName())) {
dataSource . setPoolName (properties . getName());
return dataSource;
}
}
}
@ConditionalOnClass 指定必须在 classpath 中存在 HikariDataSource 才会进行实例化操作。而该类由 spring- boot-starter-jdbc 默认将其引入,因此当引入该 starter 时,只有 Hikari的自动配置满足条件,会被实例化。
@ConditionalOnProperty 注 解 可 以 通 过 在 application.properties 文 件 中 配 置 key 为spring.datasource.type,值为 com.zaxxer.hikari.HikariDataSource的配置项用来明确启动使用 Hikari 数据源。matchlfMissing 为 true, 说明如果没有配置则默认操作生效。
Hikari 类 内 部 的 Bean 代 码 很 简 单 , 主 要 是 调 用 DataSourceConfiguration 的createData-Source 方法实例化 HikariDataSource。
在 createDataSource 方法中,使用 DataSourceProperties 的 initializeDataSourceBuilder来初始化 DataSourceBuilder,源码如下。
代码语言:javascript复制public DataSourceBuilder<?> initializeDataSourceBuilder() {
return DataSourceBuilder . create(getClassLoader()). type (getType()) .driver-
ClassName(determineDriverClassName())
. url(determineUrl()). username (determineUsername()) . password(determinePass
word());
}
}
initializeDataSourceBuilder 方 法 是 通 过 DataSourceBuilder 的 create 创 建 了DataSource-Builder 对象,并依次设置数据源类型、驱动类名、连接 url、 用户名和密码等信息。其中上述部分默认参数获取的方法名均为 determine 开头,也就是说在获取的过程中进行了一些推断及默认值的设定,该实现逻辑很简单,读者朋友可以自行查看。
createDataSource 中 获 得 了 DataSourceBuilder 之 后 , 设 置 其 type 为HikariDataSource.class 便进行了 HikariDataSource 的初始化。在 dataSource 方法中获得该初始化对象,并设置了连接池的名字,注入容器中。
PooledDataSourceConfiguration 最后导入了 DataSourceJmxConfiguration 配置类,主要用于配置与数据源相关的 MBean,非核心内容就不再展开了。
至此,关于 DataSourceAutoConfiguration 自动配置 相关的内容便讲解完了。
本文给大家讲解的内容是SpringBoot数据库配置源码解析:自动配置内部实现解析
- 下篇文章给大家讲解的是Jdbc Template 实例化操作;
- 觉得文章不错的朋友可以转发此文关注小编;
- 感谢大家的支持!
本文就是愿天堂没有BUG给大家分享的内容,大家有收获的话可以分享下,想学习更多的话可以到微信公众号里找我,我等你哦。