springboot提供了很多的自动配置,借助其内部的实现机制,我们在使用一些模块时只需要简单的依赖引入即可实现相关功能的注入与切换。在web开发过程中,与数据库经常会有联系,同样在springboot中对数据源(DataSource)也提供了相应的处理流程来方便开发者。下面会从几个方面做简单的说明,作为学习的记录:
- 说明
此次分析的springboot版本为 2.2.0.RELEASE,不同版本在一些方面会存在一定的差异,比如现在在默认的数据源为hikari。
- 简介
此版本中内置了3个数据源的创建逻辑,分别为:
com.zaxxer.hikari.HikariDataSource, org.apache.tomcat.jdbc.pool.DataSource org.apache.commons.dbcp2.BasicDataSource
- 源码
想知道springboot自动配置的入口,无疑需要从spring-boot-autoconfigure/META-INF/spring.factories开始,搜索下关键字DataSource,可以 看到引入了 org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,那么这就是数据源自动配置。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@Conditional(EmbeddedDatabaseCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import(EmbeddedDataSourceConfiguration.class)
protected static class EmbeddedDatabaseConfiguration {
}
@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 {
}
....一些Conditional...
}
相对与其他模块,代码非常的少,我们可以关注两点,一是@Import,二是里面的内容,
-
@Import分别引入 DataSourcePoolMetadataProvidersConfiguration,DataSourceInitializationConfiguration DataSourcePoolMetadataProvidersConfiguration:主要将数据源包装成DataSourcePoolMetadataProvider对象,具体什么用可以注入看下其提供的功能 DataSourceInitializationConfiguration:数据库创建完成后由DataSourceInitializerInvoker执行的一些初始化语句也就是配置文件中的spring.datasource.schema、spring.datasource.data,分别对应了ddl、dml语句;另一个作用就是注册了一个BeanPostProcessor,DataSourceInitializerPostProcessor(主要作用是DataSource注入后,DataSourceInitializerInvoker能载入到IOC)
-
代码中主要是通过@Conditional选择数据源类型EmbeddedDatabase,PooledDataSource + DataSource:普通的数据源 + EmbeddedDatabaseType:嵌入式数据库,可以不用单独启用数据库服务器,H2,hsql,derby
一般情况我们用的比较多的是PooledDataSource:
@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 {
}
可以看到,这里定义了之前说到的几种数据源配置,以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 {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
HikariDataSource dataSource(DataSourceProperties properties) {
HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class);
if (StringUtils.hasText(properties.getName())) {
dataSource.setPoolName(properties.getName());
}
return dataSource;
}
}
protected static <T> T createDataSource(DataSourceProperties properties, Class<? extends DataSource> type) {
return (T) properties.initializeDataSourceBuilder().type(type).build();
}
public DataSourceBuilder<?> initializeDataSourceBuilder() {
return DataSourceBuilder.create(getClassLoader()).type(getType()).driverClassName(determineDriverClassName())
.url(determineUrl()).username(determineUsername()).password(determinePassword());
}
可见,最终的创建过程都是在DataSourceBuilder#build中完成。
- 分析
上面说到,在DataSourceBuilder中完成了DataSource的创建工作,但是此时的数据源只有基础的一些参数,比如url、driverClass等,在使用数据库连接池时,绝非这么些参数,此时是如何配置进去的?
回到刚Hikari的配置上,
@Bean @ConfigurationProperties(prefix = "spring.datasource.hikari")
同时结合application.properties中的配置,发现会有spring.datasource.xxx与spring.datasource.hikari.xxx,其区别在于使用前者时,会根据基础的参数创建数据源,但是在DataSource的生命周期中,会将后者的值注入到DataSource bean中从而完成DataSource的属性注入,那么上面说到的连接池相关的配置,也是从这里加进去的。
所有我们在配置数据源时,不要使用spring.datasource.xxx,否则数据库连接池将会使用默认的配置。
- 示例
下面以阿里的druid数据源为例,
1、引入依赖jar
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.18</version>
</dependency>
2、加入配置
spring.datasource.druid.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.druid.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&useUnicode=true&useSSL=false&serverTimezone=UTC
spring.datasource.druid.username=root
spring.datasource.druid.password=123456
spring.datasource.druid.filters=stat
spring.datasource.druid.minIdle=5
3、定义DataSource
@Configuration
public class DruidDatasource {
@ConfigurationProperties(prefix = "spring.datasource.druid")
@Bean
public DataSource druidDataSource(){
return DataSourceBuilder.create().type(DruidDataSource.class).build();
}
}