1. 首页
  2. >
  3. 技术专题
  4. >
  5. SpringBoot

SpringBoot数据源配置原理

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

SpringBoot数据源配置原理

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

DataSourceAutoConfiguration

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


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

EmbeddedDatabaseCondition

PooledDataSourceAvailableCondition

PooledDataSourceCondition


PooledDataSourceConfiguration(池化数据源自动配置类)


EmbeddedDatabaseConfiguration(内嵌数据源自动配置类)

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

EmbeddedDatabaseConfiguration

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

@Import({EmbeddedDataSourceConfiguration.class})1复制代码类型:[java]


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

@Configuration(     proxyBeanMethods = false ) @EnableConfigurationProperties({DataSourceProperties.class}) public class EmbeddedDataSourceConfiguration implements BeanClassLoaderAware {     private ClassLoader classLoader;     public EmbeddedDataSourceConfiguration() {     }     public void setBeanClassLoader(ClassLoader classLoader) {         this.classLoader = classLoader;     }     //向容器中添加 Spring Boot 内嵌的数据源     @Bean(         destroyMethod = "shutdown"     )     public EmbeddedDatabase dataSource(DataSourceProperties properties) {         return (new EmbeddedDatabaseBuilder()).setType(EmbeddedDatabaseConnection.get(this.classLoader).getType()).setName(properties.determineDatabaseName()).build();     } }12345678910111213141516171819复制代码类型:[java]

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


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

@Conditional({DataSourceAutoConfiguration.EmbeddedDatabaseCondition.class})1复制代码类型:[java]

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

PooledDataSourceConfiguration


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

@Conditional({DataSourceAutoConfiguration.PooledDataSourceCondition.class})1复制代码类型:[java]

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


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

@Import({Hikari.class, Tomcat.class, Dbcp2.class, OracleUcp.class, Generic.class, DataSourceJmxConfiguration.class})1复制代码类型:[java]


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

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

@Configuration(     proxyBeanMethods = false ) @ConditionalOnClass({HikariDataSource.class}) @ConditionalOnMissingBean({DataSource.class}) @ConditionalOnProperty(     name = {"spring.datasource.type"},     havingValue = "com.zaxxer.hikari.HikariDataSource",     matchIfMissing = true ) static class Hikari {     Hikari() {     }     @Bean     @ConfigurationProperties(         prefix = "spring.datasource.hikari"     )     HikariDataSource dataSource(DataSourceProperties properties) {         HikariDataSource dataSource = (HikariDataSource)DataSourceConfiguration.createDataSource(properties, HikariDataSource.class);         if (StringUtils.hasText(properties.getName())) {             dataSource.setPoolName(properties.getName());         }         return dataSource;     } }12345678910111213141516171819202122232425复制代码类型:[java]

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

@Configuration:表示当前类是一个配置类;

@ConditionalOnMissingBean({DataSource.class}):表示容器中没有用户自定义的数据源时,该配置类才会被实例化;

@ConditionalOnClass({HikariDataSource.class}):表示必须在类路径中存在HikariDataSource类时,Hikari才会实例化。而HikariDataSource类是由spring-boot-starter-jdbc默认将其引入的,因此只要我们在pom.xml中引入了该starter,Hikari就会被实例化(这也是SpringBoot2.x默认使用HikariCP作为其数据源的原因);

@ConditionalOnProperty(name={"spring.datasource.type"},havingValue="
com.zaxxer.hikari.HikariDataSource",matchIfMissing=true):表示当SpringBoot配置文件中,配置了spring.datasource.type=
com.zaxxer.hikari.HikariDataSource(明确指定使用Hikari数据源)或者不配置spring.datasource.type(即默认情况)时,Hikari才会被实例化。

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

@Bean @ConfigurationProperties(     prefix = "spring.datasource.hikari" ) HikariDataSource dataSource(DataSourceProperties properties) {     HikariDataSource dataSource = (HikariDataSource)DataSourceConfiguration.createDataSource(properties, HikariDataSource.class);     if (StringUtils.hasText(properties.getName())) {         dataSource.setPoolName(properties.getName());     }     return dataSource; }1234567891011复制代码类型:[java]

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

protected static <T> T createDataSource(DataSourceProperties properties, Class<? extends DataSource> type) {     return properties.initializeDataSourceBuilder().type(type).build(); }123复制代码类型:[java]


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

public DataSourceBuilder<?> initializeDataSourceBuilder() {     return DataSourceBuilder.create(this.getClassLoader()).type(this.getType()).           driverClassName(this.determineDriverClassName()).url(this.determineUrl()).username(this.determineUsername()).password(this.determinePassword()); }1234复制代码类型:[java]

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

SpringBoot数据源配置原理

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

SpringBoot数据源配置原理

通过对SpringBoot数据源自动配置原理的分析可知:

总结

通过对SpringBoot数据源自动配置原理的分析可知:

在用户没有配置数据源的情况,若容器中存在HikariDataSource类,则SpringBoot就会自动实例化Hikari,并将其作为其数据源。

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