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的初始化。

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

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

总结

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

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

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

(0)

相关推荐