Clarify use of the `spring.datasource` prefix

Previously, Spring Boot mapped both `DataSourceProperties` and the actual
`DataSource` implementation to the same prefix. This results in a huge
amount of keys in the `spring.datasource` namespace  with no way to
identify those that are valid for the pooled data source in use.

This commit maps the four pooled data sources we support in four isolated
namespace, keeping `spring.datasource` only for the common settings.

These are `spring.datasource.tomcat`, `spring.datasource.hikari`,
`spring.datasource.dbcp` and `spring.datasource.dbcp2` for the Tomcat,
Hikari, Commons DBCP and Commons DBCP2 implementations respectively.

Closes gh-2183
This commit is contained in:
Stephane Nicoll 2016-01-28 16:02:15 +01:00
parent bbc0dc69eb
commit 34d87df425
13 changed files with 115 additions and 115 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -229,7 +229,7 @@ public class HealthIndicatorAutoConfigurationTests {
DataSourcePoolMetadataProvidersConfiguration.class,
HealthIndicatorAutoConfiguration.class);
EnvironmentTestUtils.addEnvironment(this.context,
"spring.datasource.validation-query:SELECT from FOOBAR",
"spring.datasource.test.validation-query:SELECT from FOOBAR",
"management.health.diskspace.enabled:false");
this.context.refresh();
Map<String, HealthIndicator> beans = this.context
@ -442,7 +442,7 @@ public class HealthIndicatorAutoConfigurationTests {
protected static class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = DataSourceProperties.PREFIX)
@ConfigurationProperties(prefix = "spring.datasource.test")
public DataSource dataSource() {
return DataSourceBuilder.create()
.driverClassName("org.hsqldb.jdbc.JDBCDriver")

View File

@ -21,6 +21,7 @@ import java.sql.SQLException;
import javax.sql.DataSource;
import javax.sql.XADataSource;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tomcat.jdbc.pool.DataSourceProxy;
@ -107,26 +108,78 @@ public class DataSourceAutoConfiguration {
}
@Configuration
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
protected static class PooledDataSourceConfiguration {
@Autowired
private DataSourceProperties properties;
@SuppressWarnings("unchecked")
private static <T> T createDataSource(DataSourceProperties properties, Class<? extends DataSource> type) {
return (T) DataSourceBuilder
.create(properties.getClassLoader())
.type(type)
.driverClassName(properties.determineDriverClassName())
.url(properties.determineUrl())
.username(properties.determineUsername())
.password(properties.determinePassword())
.build();
}
@Bean
@ConfigurationProperties(prefix = DataSourceProperties.PREFIX)
public DataSource dataSource() {
DataSourceBuilder factory = DataSourceBuilder
.create(this.properties.getClassLoader())
.driverClassName(this.properties.determineDriverClassName())
.url(this.properties.determineUrl())
.username(this.properties.determineUsername())
.password(this.properties.determinePassword());
if (this.properties.getType() != null) {
factory.type(this.properties.getType());
@Configuration
@Import({TomcatDataSourceConfiguration.class, HikariDataSourceConfiguration.class,
DbcpDataSourceConfiguration.class, Dbcp2DataSourceConfiguration.class})
protected static class AllDataSourceConfiguration {
}
@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type",
havingValue = "org.apache.tomcat.jdbc.pool.DataSource", matchIfMissing = true)
protected static class TomcatDataSourceConfiguration {
@Bean
@ConfigurationProperties("spring.datasource.tomcat")
public org.apache.tomcat.jdbc.pool.DataSource dataSource(DataSourceProperties properties) {
return createDataSource(properties, org.apache.tomcat.jdbc.pool.DataSource.class);
}
}
@ConditionalOnClass(HikariDataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type",
havingValue = "com.zaxxer.hikari.HikariDataSource", matchIfMissing = true)
protected static class HikariDataSourceConfiguration {
@Bean
@ConfigurationProperties("spring.datasource.hikari")
public HikariDataSource dataSource(DataSourceProperties properties) {
return createDataSource(properties, HikariDataSource.class);
}
}
@ConditionalOnClass(org.apache.commons.dbcp.BasicDataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type",
havingValue = "org.apache.commons.dbcp.BasicDataSource", matchIfMissing = true)
protected static class DbcpDataSourceConfiguration {
@Bean
@ConfigurationProperties("spring.datasource.dbcp")
public org.apache.commons.dbcp.BasicDataSource dataSource(DataSourceProperties properties) {
return createDataSource(properties, org.apache.commons.dbcp.BasicDataSource.class);
}
}
@ConditionalOnClass(org.apache.commons.dbcp2.BasicDataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type",
havingValue = "org.apache.commons.dbcp2.BasicDataSource", matchIfMissing = true)
protected static class Dbcp2DataSourceConfiguration {
@Bean
@ConfigurationProperties("spring.datasource.dbcp2")
public org.apache.commons.dbcp2.BasicDataSource dataSource(DataSourceProperties properties) {
return createDataSource(properties, org.apache.commons.dbcp2.BasicDataSource.class);
}
return factory.build();
}
}

View File

@ -1,51 +0,0 @@
/*
* Copyright 2012-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.jdbc;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Expose the metadata of the supported data sources. Only used to harvest the relevant
* properties metadata.
*
* @author Stephane Nicoll
* @since 1.2.0
*/
class DataSourceConfigMetadata {
@ConfigurationProperties(DataSourceProperties.PREFIX)
public DataSource tomcatDataSource() {
return (DataSource) DataSourceBuilder.create().type(DataSource.class).build();
}
@ConfigurationProperties(DataSourceProperties.PREFIX)
public HikariDataSource hikariDataSource() {
return (HikariDataSource) DataSourceBuilder.create().type(HikariDataSource.class)
.build();
}
@ConfigurationProperties(DataSourceProperties.PREFIX)
public BasicDataSource dbcpDataSource() {
return (BasicDataSource) DataSourceBuilder.create().type(BasicDataSource.class)
.build();
}
}

View File

@ -42,12 +42,10 @@ import org.springframework.util.StringUtils;
* @author Benedikt Ritter
* @since 1.1.0
*/
@ConfigurationProperties(prefix = DataSourceProperties.PREFIX)
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties
implements BeanClassLoaderAware, EnvironmentAware, InitializingBean {
public static final String PREFIX = "spring.datasource";
private ClassLoader classLoader;
private Environment environment;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2015 the original author or authors.
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -44,7 +44,7 @@ import org.springframework.jmx.support.JmxUtils;
@AutoConfigureBefore({ XADataSourceAutoConfiguration.class,
DataSourceAutoConfiguration.class })
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnProperty(prefix = DataSourceProperties.PREFIX, name = "jndi-name")
@ConditionalOnProperty(prefix = "spring.datasource", name = "jndi-name")
@EnableConfigurationProperties(DataSourceProperties.class)
public class JndiDataSourceAutoConfiguration {

View File

@ -30,7 +30,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.bind.RelaxedDataBinder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.jta.XADataSourceWrapper;
import org.springframework.context.annotation.Bean;
@ -66,7 +65,6 @@ public class XADataSourceAutoConfiguration implements BeanClassLoaderAware {
private ClassLoader classLoader;
@Bean
@ConfigurationProperties(prefix = DataSourceProperties.PREFIX)
public DataSource dataSource() throws Exception {
XADataSource xaDataSource = this.xaDataSource;
if (xaDataSource == null) {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -34,19 +34,20 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* Tests for {@link CommonsDataSourceConfiguration}.
* Tests for {@link CommonsDbcpDataSourceConfiguration}.
*
* @author Dave Syer
* @author Stephane Nicoll
*/
public class CommonsDataSourceConfigurationTests {
public class CommonsDbcpDataSourceConfigurationTests {
private static final String PREFIX = "spring.datasource.";
private static final String PREFIX = "spring.datasource.dbcp.";
private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
@Test
public void testDataSourceExists() throws Exception {
this.context.register(CommonsDataSourceConfiguration.class);
this.context.register(CommonsDbcpDataSourceConfiguration.class);
this.context.refresh();
assertNotNull(this.context.getBean(DataSource.class));
this.context.close();
@ -54,7 +55,7 @@ public class CommonsDataSourceConfigurationTests {
@Test
public void testDataSourcePropertiesOverridden() throws Exception {
this.context.register(CommonsDataSourceConfiguration.class);
this.context.register(CommonsDbcpDataSourceConfiguration.class);
EnvironmentTestUtils.addEnvironment(this.context,
PREFIX + "url:jdbc:foo//bar/spam");
EnvironmentTestUtils.addEnvironment(this.context, PREFIX + "testWhileIdle:true");
@ -78,7 +79,7 @@ public class CommonsDataSourceConfigurationTests {
@Test
public void testDataSourceDefaultsPreserved() throws Exception {
this.context.register(CommonsDataSourceConfiguration.class);
this.context.register(CommonsDbcpDataSourceConfiguration.class);
this.context.refresh();
BasicDataSource ds = this.context.getBean(BasicDataSource.class);
assertEquals(GenericObjectPool.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS,
@ -90,10 +91,10 @@ public class CommonsDataSourceConfigurationTests {
@Configuration
@EnableConfigurationProperties
protected static class CommonsDataSourceConfiguration {
protected static class CommonsDbcpDataSourceConfiguration {
@Bean
@ConfigurationProperties(prefix = DataSourceProperties.PREFIX)
@ConfigurationProperties(prefix = "spring.datasource.dbcp")
public DataSource dataSource() {
return DataSourceBuilder.create().type(BasicDataSource.class).build();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -42,7 +42,7 @@ import static org.junit.Assert.assertNotNull;
*/
public class HikariDataSourceConfigurationTests {
private static final String PREFIX = "spring.datasource.";
private static final String PREFIX = "spring.datasource.hikari.";
private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
@ -103,7 +103,7 @@ public class HikariDataSourceConfigurationTests {
protected static class HikariDataSourceConfiguration {
@Bean
@ConfigurationProperties(prefix = DataSourceProperties.PREFIX)
@ConfigurationProperties(prefix = "spring.datasource.hikari")
public DataSource dataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}

View File

@ -27,14 +27,13 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableMBeanExport;
import org.springframework.context.annotation.Import;
import org.springframework.util.ReflectionUtils;
import static org.junit.Assert.assertEquals;
@ -46,10 +45,11 @@ import static org.junit.Assert.fail;
* Tests for {@link TomcatDataSourceConfiguration}.
*
* @author Dave Syer
* @author Stephane Nicoll
*/
public class TomcatDataSourceConfigurationTests {
private static final String PREFIX = "spring.datasource.";
private static final String PREFIX = "spring.datasource.tomcat.";
private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
@ -66,6 +66,8 @@ public class TomcatDataSourceConfigurationTests {
@Test
public void testDataSourceExists() throws Exception {
this.context.register(TomcatDataSourceConfiguration.class);
EnvironmentTestUtils.addEnvironment(this.context,
PREFIX + "url:jdbc:h2:mem:testdb");
this.context.refresh();
assertNotNull(this.context.getBean(DataSource.class));
assertNotNull(this.context.getBean(org.apache.tomcat.jdbc.pool.DataSource.class));
@ -75,7 +77,7 @@ public class TomcatDataSourceConfigurationTests {
public void testDataSourcePropertiesOverridden() throws Exception {
this.context.register(TomcatDataSourceConfiguration.class);
EnvironmentTestUtils.addEnvironment(this.context,
PREFIX + "url:jdbc:foo//bar/spam");
PREFIX + "url:jdbc:h2:mem:testdb");
EnvironmentTestUtils.addEnvironment(this.context, PREFIX + "testWhileIdle:true");
EnvironmentTestUtils.addEnvironment(this.context, PREFIX + "testOnBorrow:true");
EnvironmentTestUtils.addEnvironment(this.context, PREFIX + "testOnReturn:true");
@ -91,7 +93,7 @@ public class TomcatDataSourceConfigurationTests {
this.context.refresh();
org.apache.tomcat.jdbc.pool.DataSource ds = this.context
.getBean(org.apache.tomcat.jdbc.pool.DataSource.class);
assertEquals("jdbc:foo//bar/spam", ds.getUrl());
assertEquals("jdbc:h2:mem:testdb", ds.getUrl());
assertTrue(ds.isTestWhileIdle());
assertTrue(ds.isTestOnBorrow());
assertTrue(ds.isTestOnReturn());
@ -117,6 +119,8 @@ public class TomcatDataSourceConfigurationTests {
@Test
public void testDataSourceDefaultsPreserved() throws Exception {
this.context.register(TomcatDataSourceConfiguration.class);
EnvironmentTestUtils.addEnvironment(this.context,
PREFIX + "url:jdbc:h2:mem:testdb");
this.context.refresh();
org.apache.tomcat.jdbc.pool.DataSource ds = this.context
.getBean(org.apache.tomcat.jdbc.pool.DataSource.class);
@ -134,24 +138,15 @@ public class TomcatDataSourceConfigurationTests {
}
@Configuration
@Import(DataSourceAutoConfiguration.class)
@EnableConfigurationProperties
@EnableMBeanExport
protected static class TomcatDataSourceConfiguration {
@Autowired
private DataSourceProperties properties;
@Bean
@ConfigurationProperties(prefix = DataSourceProperties.PREFIX)
@ConfigurationProperties(prefix = "spring.datasource.tomcat")
public DataSource dataSource() {
DataSourceBuilder factory = DataSourceBuilder
.create(this.properties.getClassLoader())
.driverClassName(this.properties.determineDriverClassName())
.url(this.properties.determineUrl())
.username(this.properties.determineUsername())
.password(this.properties.determinePassword())
.type(org.apache.tomcat.jdbc.pool.DataSource.class);
return factory.build();
return DataSourceBuilder.create()
.type(org.apache.tomcat.jdbc.pool.DataSource.class).build();
}
}

View File

@ -525,7 +525,10 @@ content into your application; rather pick only the properties that you need.
# DATASOURCE ({sc-spring-boot-autoconfigure}/jdbc/DataSourceAutoConfiguration.{sc-ext}[DataSourceAutoConfiguration] & {sc-spring-boot-autoconfigure}/jdbc/DataSourceProperties.{sc-ext}[DataSourceProperties])
spring.datasource.continue-on-error=false # Do not stop if an error occurs while initializing the database.
spring.datasource.data= # Data (DML) script resource reference.
spring.datasource.dbcp.*= # Commons DBCP specific settings
spring.datasource.dbcp2.*= # Commons DBCP2 specific settings
spring.datasource.driver-class-name= # Fully qualified name of the JDBC driver. Auto-detected based on the URL by default.
spring.datasource.hikari.*= # Hikari specific settings
spring.datasource.initialize=true # Populate the database using 'data.sql'.
spring.datasource.jmx-enabled=false # Enable JMX support (if provided by the underlying pool).
spring.datasource.jndi-name= # JNDI location of the datasource. Class, url, username & password are ignored when set.
@ -544,6 +547,7 @@ content into your application; rather pick only the properties that you need.
spring.datasource.test-on-return= # For instance `false`
spring.datasource.test-while-idle= #
spring.datasource.time-between-eviction-runs-millis= 1
spring.datasource.tomcat.*= # Tomcat datasource specific settings
spring.datasource.type= # Fully qualified name of the connection pool implementation to use. By default, it is auto-detected from the classpath.
spring.datasource.url= # JDBC url of the database.
spring.datasource.username=

View File

@ -293,10 +293,10 @@ following attributes:
[[configuration-metadata-repeated-items]]
==== Repeated meta-data items
It is perfectly acceptable for "`property`" and "`group`" objects with the same name to
appear multiple times within a meta-data file. For example, Spring Boot binds
`spring.datasource` properties to Hikari, Tomcat and DBCP classes, with each potentially
offering overlap of property names. Consumers of meta-data should take care to ensure
that they support such scenarios.
appear multiple times within a meta-data file. For example, you could bind two separate
classes to the same prefix, with each potentially offering overlap of property names.
While this is not supposed to be a frequent scenario, consumers of meta-data should take
care to ensure that they support such scenarios.

View File

@ -1670,7 +1670,7 @@ to start. The script locations can be changed by setting `spring.datasource.sche
`spring.datasource.data`, and neither location will be processed if
`spring.datasource.initialize=false`.
To disable the fail-fast you can set `spring.datasource.continueOnError=true`. This can be
To disable the fail-fast you can set `spring.datasource.continue-on-error=true`. This can be
useful once an application has matured and been deployed a few times, since the scripts
can act as '`poor man's migrations`' -- inserts that fail mean that the data is already
there, so there would be no need to prevent the application from running, for instance.

View File

@ -2337,8 +2337,10 @@ loadable.
See {sc-spring-boot-autoconfigure}/jdbc/DataSourceProperties.{sc-ext}[`DataSourceProperties`]
for more of the supported options. These are the standard options that work regardless of
the actual implementation. It is also possible to fine tune implementation-specific settings
using the `+spring.datasource.*+` prefix, refer to the documentation of the connection pool
the actual implementation. It is also possible to fine tune implementation-specific
settings using their respective prefix (`+spring.datasource.tomcat*+`,
`+spring.datasource.hikari+`, `+spring.datasource.dbcp*+` and
`+spring.datasource.dbcp2*+`), refer to the documentation of the connection pool
implementation you are using for more details.
For instance, if you are using the
@ -2349,13 +2351,13 @@ you could customize many additional settings:
[source,properties,indent=0]
----
# Number of ms to wait before throwing an exception if no connection is available.
spring.datasource.max-wait=10000
spring.datasource.tomcat.max-wait=10000
# Maximum number of active connections that can be allocated from this pool at the same time.
spring.datasource.max-active=50
spring.datasource.tomcat.max-active=50
# Validate the connection before borrowing it from the pool.
spring.datasource.test-on-borrow=true
spring.datasource.tomcat.test-on-borrow=true
----