博客列表 >25、【网摘】Spring Boot数据源配置原理

25、【网摘】Spring Boot数据源配置原理

自由之上
自由之上原创
2022年11月14日 10:00:17501浏览

在数据库访问过程中,“数据源”无疑是最重要的概念之一,它不仅可以对与数据库访问相关的各种参数进行封装和统一管理,还可以管理数据库连接池,提高数据库连接性能。

目前,在市面上有很多优秀的开源数据源,例如 DBCP、C3P0、Druid、HikariCP 等等。在 Spring Boot 2.x 中,则采用目前性能最佳的 HikariCP 作为其默认数据源。接下来,我们就来具体介绍下 Spring Boot 的默认数据源配置及其原理。

1、DataSourceAutoConfiguration

我们知道,Spring Boot 中几乎所有的默认配置都是通过配置类 XxxAutoConfiguration 进行配置的,Spring Boot 数据源也不例外,它的自动配置类是:DataSourceAutoConfiguration。

DataSourceAutoConfiguration 中 共包括以下 5 个内部静态类:

  • EmbeddedDatabaseCondition
  • PooledDataSourceAvailableCondition
  • PooledDataSourceCondition
  • PooledDataSourceConfiguration(池化数据源自动配置类)
  • EmbeddedDatabaseConfiguration(内嵌数据源自动配置类)

其中,PooledDataSourceConfiguration 和 EmbeddedDatabaseConfiguration 为使用了 @Configuration 注解的自动配置类,其余 3 个为限制条件类。

1、EmbeddedDatabaseConfiguration

顾名思义,EmbeddedDatabaseConfiguration 是内嵌数据源的自动配置类,该类中并没有任何的方法实现,它的主要功能都是通过 @Import 注解引入 EmbeddedDataSourceConfiguration 类来实现的。

  1. @Import({EmbeddedDataSourceConfiguration.class})

EmbeddedDataSourceConfiguration 向容器中添加了一个 Spring Boot 内嵌的数据源,该数据源支持 HSQL,H2 和 DERBY 三种数据库,其部分代码如下。

  1. @Configuration(proxyBeanMethods = false)
  2. @EnableConfigurationProperties({DataSourceProperties.class})
  3. public class EmbeddedDataSourceConfiguration implements BeanClassLoaderAware {
  4. private ClassLoader classLoader;
  5. public EmbeddedDataSourceConfiguration() {
  6. }
  7. public void setBeanClassLoader(ClassLoader classLoader) {
  8. this.classLoader = classLoader;
  9. }
  10. //向容器中添加 Spring Boot 内嵌的数据源
  11. @Bean(
  12. destroyMethod = shutdown
  13. )
  14. public EmbeddedDatabase dataSource(DataSourceProperties properties) {
  15. return (new EmbeddedDatabaseBuilder()).setType(EmbeddedDatabaseConnection.get(this.classLoader).getType()).setName(properties.determineDatabaseName()).build();
  16. }
  17. }

通过上面的分析,我们知道自动配置类 EmbeddedDatabaseConfiguration 的作用是向容器中添加一个内嵌的数据源(DataSource),但这是有条件限制的。

在 EmbeddedDatabaseConfiguration 类上还使用一个 @Conditional 注解,该注解使用了 DataSourceAutoConfiguration 的内部限制条件类 EmbeddedDatabaseCondition 来进行条件判断。

  1. @Conditional({DataSourceAutoConfiguration.EmbeddedDatabaseCondition.class})

EmbeddedDatabaseCondition 主要用来检测容器中是否已经存在池化数据源(PooledDataSource)。若容器中存在池化数据源时,则 EmbeddedDatabaseConfiguration 不能被实例化。只有当容器中不存在池化数据源时,EmbeddedDatabaseConfiguration 才能被实例化,才能向容器中添加内嵌数据源(EmbeddedDataSource)。

2、PooledDataSourceConfiguration

PooledDataSourceConfiguration 是池化数据源的自动配置类,该类上使用了一个 @Conditional 注解,该注解使用了 DataSourceAutoConfiguration 的内部限制条件类 PooledDataSourceCondition 来进行条件判断。

  1. @Conditional({DataSourceAutoConfiguration.PooledDataSourceCondition.class})

PooledDataSourceCondition 与 EmbeddedDatabaseCondition 一样,也是用来检测容器中是否已经存在池化数据源的,但不同的是,PooledDataSourceConfiguration 是只有当容器中存在池化数据源时, 才可以被实例化,才可以向容器中添加池化数据源。

与 EmbeddedDatabaseConfiguration 一样,PooledDataSourceConfiguration 类中也没有任何的方法实现,它的所有功能都是通过 @Import 注解引入其他的类实现的。

  1. @Import({Hikari.class, Tomcat.class, Dbcp2.class, OracleUcp.class, Generic.class, DataSourceJmxConfiguration.class})

PooledDataSourceConfiguration 通过 @Import 注解引入了 Hikari、Tomcat、Dbcp2、OracleUcp 和 Generic 五个数据源配置类,它们都是 DataSourceConfiguration 的内部类,且它们的功能类似,都是向容器中添加指定的数据源。

下面我们以 Hikari 为例进行分析,Hikari 的源码如下。

  1. @Configuration(
  2. proxyBeanMethods = false
  3. )
  4. @ConditionalOnClass({HikariDataSource.class})
  5. @ConditionalOnMissingBean({DataSource.class})
  6. @ConditionalOnProperty(
  7. name = {“spring.datasource.type”},
  8. havingValue = com.zaxxer.hikari.HikariDataSource”,
  9. matchIfMissing = true
  10. )
  11. static class Hikari {
  12. Hikari() {
  13. }
  14. @Bean
  15. @ConfigurationProperties(
  16. prefix = spring.datasource.hikari
  17. )
  18. HikariDataSource dataSource(DataSourceProperties properties) {
  19. HikariDataSource dataSource = (HikariDataSource)DataSourceConfiguration.createDataSource(properties, HikariDataSource.class);
  20. if (StringUtils.hasText(properties.getName())) {
  21. dataSource.setPoolName(properties.getName());
  22. }
  23. return dataSource;
  24. }
  25. }

在 Hikari 类中,主要使用以下注解:

  • @Configuration:表示当前类是一个配置类;
  • @ConditionalOnMissingBean({DataSource.class}):表示容器中没有用户自定义的数据源时,该配置类才会被实例化;
  • @ConditionalOnClass({HikariDataSource.class}) :表示必须在类路径中存在 HikariDataSource 类时,Hikari 才会实例化。而 HikariDataSource 类是由 spring- boot-starter-jdbc 默认将其引入的,因此只要我们在 pom.xml 中引入了该 starter, Hikari 就会被实例化(这也是 Spring Boot 2.x 默认使用 HikariCP 作为其数据源的原因)。;
  • @ConditionalOnProperty( name = {“spring.datasource.type”},havingValue = “com.zaxxer.hikari.HikariDataSource”,matchIfMissing = true): 表示当 Spring Boot 配置文件中,配置了 spring.datasource.type = com.zaxxer.hikari.HikariDataSource(明确指定使用 Hikari 数据源)或者不配置 spring.datasource.type(即默认情况)时,Hikari 才会被实例化。

Hikari 类通过 @Bean 注解向容器中添加了 HikariDataSource 组件,该组件的实例对象是通过调用 DataSourceConfiguration 的 createDataSource() 方法得到的,代码如下。

  1. @Bean
  2. @ConfigurationProperties(
  3. prefix = spring.datasource.hikari
  4. )
  5. HikariDataSource dataSource(DataSourceProperties properties) {
  6. HikariDataSource dataSource = (HikariDataSource)DataSourceConfiguration.createDataSource(properties, HikariDataSource.class);
  7. if (StringUtils.hasText(properties.getName())) {
  8. dataSource.setPoolName(properties.getName());
  9. }
  10. return dataSource;
  11. }

在 createDataSource() 方法中,调用 DataSourceProperties 的 initializeDataSourceBuilder() 来初始化 DataSourceBuilder,源码如下。

  1. protected static T createDataSource(DataSourceProperties properties, Class<? extends DataSource> type) {
  2. return properties.initializeDataSourceBuilder().type(type).build();
  3. }

initializeDataSourceBuilder() 方法通过调用 DataSourceBuilder 的 create() 方法创建 DataSourceBuilder 对象,并根据 Spring Boot 的配置文件(application.properties/yml)中的配置,依次设置数据源类型、驱动类名、连接 url、 用户名和密码等信息。

  1. public DataSourceBuilder<?> initializeDataSourceBuilder() {
  2. return DataSourceBuilder.create(this.getClassLoader()).type(this.getType()).
  3. driverClassName(this.determineDriverClassName()).url(this.determineUrl()).username(this.determineUsername()).password(this.determinePassword());
  4. }

上面提到 spring.datasource.type 默认是可以不用配置的,因此在 createDataSource() 方法在获取到回传回来的 DataSourceBuilder 对象后,还需要将其 type 属性再次设置为 HikariDataSource,并调用 DataSourceBuilder 的 build() 方法,完成 HikariDataSource 的初始化。

图1:初始化 HikariDataSource

dataSource() 方法获得数据源对象,并设置了连接池的名字(name),注入到容器中。

图2:设置连接池 name

自此,我们就完成了对 Spring Boot 数据源自动配置原理的分析。

总结

通过对 Spring Boot 数据源自动配置原理的分析可知:
在用户没有配置数据源的情况,若容器中存在 HikariDataSource 类,则 Spring Boot 就会自动实例化 Hikari,并将其作为其数据源。

Spring Boot 的 JDBC 场景启动器(spring-boot-starter-data-jdbc)通过 spring- boot-starter-jdbc 默认引入了 HikariCP 数据源(包含 HikariDataSource 类),因此 Spring Boot 默认使用 HikariCP 作为其数据源。


加入
QQ群:722461036
微信群:
一起督促、学习、练习、温习、复习 ~ ~ ~

声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议