程序员码农|SpringBoot数据库配置源码解析:自动配置内部实现解析


上节我们了解了 DataSourceAutoConfiguration 自动配置的注解部分 , 本节继续深入讲解该类中的内部实现 。
DataSourceAutoConfiguration中共有5个静态内 部 类, 包括EmbeddedDatabaseConfiguration 和 PooledDataSourceConfiguration 两 个 声 明 有@Configuration 注解的自动配置类 , 以及另外 3 个限制条件类:
PooledDataSourceCondition、PooledDataSourceAvailableCondition、Em-beddedDatabaseCondition 。
下面 , 将对以上涉及内容进行详解 。
EmbeddedDatabaseConfiguration
首先我们来看内部类 EmbeddedDatabaseConfiguration,该类其实并没有方法实现 , 它的主要功能是通过@Import 引入类来完成 , 源代码如下 。
@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 。
static class EmbeddedDatabaseCondition extends Spr ingBootCondition {private final SpringBootCondition pooledCondition = new PooledDataSource-Condition();@Overridepublic ConditionOutcome ge tMatchOutcome (ConditionContext context, Anno-tatedTypeMetadata metadata) {ConditionMessage . Builder message = ConditionMessage . forCondition("Emb-eddedDataSource");//是否支持池化的数据源 , 支 持则返回不匹配if (anyMatches(context, metadata, this. pooledCondition)) {retu~n Condit ionOutcome . noMatch(message . foundExactly(" supportedpooled 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 , 下 面为该类的源代码 。
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@Overridepublic ConditionOutcome getMatchOutcome(ConditionContext context, Annotated-TypeMetadata 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").}}分页标题PooledDataSourceAvailableCondition 的判断逻辑非常简单 , 就是检查当前类加载器中是否存在指定的数据源对象 。 在判断的过程中使用到了 DataSourceBuilder 的 findType 方法 。
我们看一下相关代码 ,加深理解 。
public final class DataSourceBuilder {private static final String[] DATA_ SOURCE _TYPE_ NAMES = new String[] { "com. zaxxer. hikari. HikariDataSource","org. apache . tomcat. jdbc. pool.DataSource",' org . apache. commons . dbcp2.BasicDataSource" };@SuppressWarnings("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 实现的 。
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(), classLoader)) {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 暴露出来了 。分页标题
@Configuration(proxyBeanMethods = false)@EnableConf igurationProperties (DataSourceProperties . class)public class EmbeddedDataSourceConfiguration implements BeanClassL oaderAware{private ClassLoader classLoader;@0verridepublic void setBeanClassLoader(ClassLoader classLoader) {this. classLoader = classLoader;@Bean(destroyMethod = "shutdown")public EmbeddedDatabase dataSource (DataSourceProperties properties) {return new EmbeddedDatabaseBuilder(). setType( EmbeddedDatabaseConnec -tion. get(this. classLoader). getType()). setName(properties . determineDatabaseName()). build();}}我们重点看以上代码中的 dataSource 方法 , 该方法的注解指定了销毁方法为“shutdown” , 也 就 是 EmbeddedDatabase 的 shutdown 方 法。在 方 法 内 部,首 先 创 建 了 一 个Embedded-DatabaseBuilder,用于构建内嵌数据库 EmbeddedDatabase 。 根据命名可知Embedded-DatabaseBuilder 是可以链式调用的 。
因此 , EmbeddedDatabaseBuilder 连续调用 了设置数据库类型(. 上面已经讲到获取实现)、设置内嵌数据库名称 。 最后 , 通过 build 方法完成 EmbeddedDatabase 的构建 , 并注入容器 。
至此 , 关于 EmbeddedDatabaseConfiguration 相关的 自动配置已经讲解完毕 。 在下节 , 我们将继续学习池化的数据源配置类 PooledDataSourceConfiguration 。

程序员码农|SpringBoot数据库配置源码解析:自动配置内部实现解析
本文插图
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 的实例化的 , 源代码如下 。
@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 {}}PooledDataSourceConfiguration 中同样没有具体实现 。 @Conditional 的筛选条件也是由内部类 PooledDataSourceCondition 来完成的 , 这些内容前面已经讲过 , 这里不再赘述 。 该实例化对象的优先级要高于内嵌 DataSource 的 。
我们重点看@lmport 引入的前 4 个类 , 它们是 DataSourceConfiguration 的内部类 , 提供了Hikari、Tomcat、 Dbcp2、 Generic 的 DataSource 配置 。
DataSourceConfiguration 就 是 用 于 DataSourceAutoConfiguration 导 入 的 实 际 的DataSource 配置 , 这里我们以 Hikari 为例来进行讲解 。 Hikari 是 spring-boot-starter-jdbc默认弓|入的数据源 , Hikari 相关自动配置代码如下 。
abstract class DataSourceConfiguration@SuppressWarnings ("unchecked" )protected staticT 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 =http://news.hoteastday.com/a/”com. zaxxer. hikari. HikariDataSource",matchIfMissing = true)static class Hikari {@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;}}}分页标题
@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,源码如下 。
public DataSourceBuilder> initializeDataSourceBuilder() {return DataSourceBuilder . create(getClassLoader()). type (getType()) .driver-ClassName(determineDriverClassName()). url(determineUrl()). username (determineUsername()) . password(determinePassword());}}
initializeDataSourceBuilder 方 法 是 通 过 DataSourceBuilder 的 create 创 建 了DataSource-Builder 对象 , 并依次设置数据源类型、驱动类名、连接 url、 用户名和密码等信息 。 其中上述部分默认参数获取的方法名均为 determine 开头 , 也就是说在获取的过程中进行了一些推断及默认值的设定 , 该实现逻辑很简单 , 读者朋友可以自行查看 。
createDataSource 中 获 得 了 DataSourceBuilder 之 后,设 置 其 type 为HikariDataSource.class 便进行了 HikariDataSource 的初始化 。 在 dataSource 方法中获得该初始化对象 , 并设置了连接池的名字 , 注入容器中 。
PooledDataSourceConfiguration 最后导入了 DataSourceJmxConfiguration 配置类 , 主要用于配置与数据源相关的 MBean,非核心内容就不再展开了 。
至此 , 关于 DataSourceAutoConfiguration 自动配置 相关的内容便讲解完了 。

  1. 下篇文章给大家讲解的是Jdbc Template 实例化操作;
  2. 觉得文章不错的朋友可以转发此文关注小编;
  3. 感谢大家的支持!
【程序员码农|SpringBoot数据库配置源码解析:自动配置内部实现解析】