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作为其数据源。