1. Principle of SpringBoot data source injection

We know that spring. Datasource can be injected after being configured in application.yaml, but why?

spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/test? serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 username: root password: rootCopy the code

(1) Default data source

In SpringBootspring-boot-autoconfigureIn the package, there is a file named Spring.factories in the meta-INF folder

Open this file and you can see that it is configuredorg.springframework.boot.autoconfigure.EnableAutoConfiguration

This is the annotation for SpringBoot autowiring

Spring. factories is an spi provided by SpringBoot. SpringBoot provides annotations that automatically assemble all beans configured here.

In the spring. A configuration can be found in the factories: org. Springframework. Boot. Autoconfigure. JDBC. DataSourceAutoConfiguration

This is the key to data source injection.

In DataSourceAutoConfiguration, you can see the default support two types of data sources: EmbeddedDatabaseConfiguration (embedded database) and PooledDataSourceConfiguration (pooling data sources).

@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.OracleUcp.class, DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class }) protected static class PooledDataSourceConfiguration { }Copy the code

As you can see, they all specify injection conditions with the @Conditional annotation, so you can see for yourself that pooled datasource conditions are either spring.datasource.type configured, Either meet PooledDataSourceAvailableCondition condition, this condition is to through the following class class loader to load respectively the following categories:

com.zaxxer.hikari.HikariDataSource org.apache.tomcat.jdbc.pool.DataSource org.apache.commons.dbcp2.BasicDataSource Existing oracle. JDBC. OracleConnection oracle. Ucp contains. JDBC. PoolDataSourceImplCopy the code

Spring.datasource. Url is not configured for the pooled datasource. If the pooled datasource does not match the pooled datasource, the pooled database will be created.

(2) Druid data source

We at the beginning of the application. The yaml configuration in the spring. The datasource. The type of DruidDataSource, shows that using the Druid data source may have a look DruidDataSourceAutoConfigure the code:

@Configuration @ConditionalOnClass(DruidDataSource.class) @AutoConfigureBefore(DataSourceAutoConfiguration.class) @EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class}) @Import({DruidSpringAopConfiguration.class, DruidStatViewServletConfiguration.class, DruidWebStatFilterConfiguration.class, DruidFilterConfiguration.class}) public class DruidDataSourceAutoConfigure { private static final Logger LOGGER = LoggerFactory.getLogger(DruidDataSourceAutoConfigure.class); @Bean(initMethod = "init") @ConditionalOnMissingBean public DataSource dataSource() { LOGGER.info("Init DruidDataSource"); return new DruidDataSourceWrapper(); }}Copy the code

ConditionalOnClass specifies that the configuration is valid only if the DruidDataSource exists.

@ AutoConfigureBefore shows that automatic configuration before DataSourceAutoConfiguration effect. DataSourceAutoConfiguration whilst on the subject of data source is the key of the automatic assembly, instructions before assembling SpringBoot default data source, Will assembly priority Druid in the data source and DataSourceAutoConfiguration @ ConditionalOnMissingBean annotations, if has been equipped with a data source, won’t assembly the default data source

@ EnableConfigurationProperties DruidStatProperties and DataSourceProperties the configuration to take effect, point in prefix can find their configuration:

ConfigurationProperties("spring.datasource. Druid ") public class DruidStatProperties {// @ConfigurationProperties(prefix = "spring.datasource") public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {// omit code}Copy the code

ConditionalOnMissingBean shows that the Druid dataSource is injected only if there is no dataSource Bean in the container. That is, if we inject a dataSource manually, we will no longer create Druid’s dataSource. @import injects four beans for monitoring, statistics, and other functions of the Druid data source. ConditionalOnMissingBean the @conditionAlonmissingBean annotation indicates that the Druid data source will not be assembled if it has already been assembled. Finally, a DruidDataSourceWrapper is returned, injecting the Durid data source into the container via the @bean annotation.

2. A related trivia

If a datasource is configured in application.yaml and then manually injected, where does the datasource configuration apply?

The dataSource Bean is injected manually. If the dataSource Bean is injected manually, only the unconfigured properties of the application.yaml Bean will take effect.

Why is that?

We know that creating a Bean has two phases: instantiation and initialization. When the dataSource Bean is instantiated (the createBeanInstance method in the doCreateBean method), the dataSource properties are set once, which were set during manual injection.

And when initializing the dataSource (namely doCreateBean method of initializeBean), will call applyBeanPostProcessorsBeforeInitialization method first, Traverse postProcessBeforeInitialization method before processing.

ConfigurationPropertiesBindingPostProcessor approach to binding properties, the application in yaml set of properties assigned to the dataSource.

So, the resulting dataSource property values are those configured in application.yaml and those not overridden in the @bean when manually injected.

Write a simple code to verify this: configure the properties in application.yaml, then manually inject the dataSource and modify the properties

spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/test? serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8 username: root password: rootCopy the code
@Configuration public class DruidStarterConfig { @Bean @ConfigurationProperties("spring.datasource") public DataSource druidDataSource() { DruidDataSource druidDataSource = new DruidDataSource(); DruidDataSource. SetName ("aaa"); druidDataSource.setUsername("bbb"); druidDataSource.setPassword("ccc"); druidDataSource.setUrl("ddd"); druidDataSource.setDriverClassName("eee"); return druidDataSource; }}Copy the code

Prints the properties of the data source

@SpringBootTest @RunWith(SpringRunner.class) public class DruidStarterConfigTest { @Resource private DataSource dataSource; @Test public void test() throws SQLException { DruidDataSource druidDataSource = (DruidDataSource) dataSource; System.out.println(druidDataSource.getDriverClassName()); System.out.println(druidDataSource.getUrl()); System.out.println(druidDataSource.getUsername()); System.out.println(druidDataSource.getPassword()); System.out.println(druidDataSource.getName()); }}Copy the code

Running results:



You can see that all attributes except the last name attribute are the same values configured in application.yaml.

Refer to the reference

Built-in database and pooled data source: developer.51cto.com/a…