Restore support for spring.datasource.type
This commit restores the support of "spring.datasource.type". If this property is set to an unsupported `DataSource` implementation, we now properly create it again, rather than ignoring it because its value did not match any value we support. Since Spring Boot 1.4 now redirects `DataSource` implementation settings to dedicated namespaces, the documentation has been updated to explicitly mention how one can create such arrangement for an unsupported implementation. As a convenience, `DataSourceProperties` can initialize a `DataSourceBuilder` based on its internal state, making it very easy for anyone to create a `DataSource` from a `DataSourceProperties` instance. Closes gh-6695
This commit is contained in:
@@ -30,6 +30,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
@@ -99,7 +100,8 @@ public class DataSourceAutoConfiguration {
|
||||
@Conditional(PooledDataSourceCondition.class)
|
||||
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
|
||||
@Import({ DataSourceConfiguration.Tomcat.class, DataSourceConfiguration.Hikari.class,
|
||||
DataSourceConfiguration.Dbcp.class, DataSourceConfiguration.Dbcp2.class })
|
||||
DataSourceConfiguration.Dbcp.class, DataSourceConfiguration.Dbcp2.class,
|
||||
DataSourceConfiguration.Generic.class})
|
||||
protected static class PooledDataSourceConfiguration {
|
||||
|
||||
}
|
||||
@@ -126,10 +128,28 @@ public class DataSourceAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AnyNestedCondition} that checks that either {@code spring.datasource.type}
|
||||
* is set or {@link PooledDataSourceAvailableCondition} applies.
|
||||
*/
|
||||
static class PooledDataSourceCondition extends AnyNestedCondition {
|
||||
|
||||
PooledDataSourceCondition() {
|
||||
super(ConfigurationPhase.PARSE_CONFIGURATION);
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(prefix = "spring.datasource", name = "type")
|
||||
static class ExplicitType { }
|
||||
|
||||
@Conditional(PooledDataSourceAvailableCondition.class)
|
||||
static class PooledDataSourceAvailable { }
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Condition} to test if a supported connection pool is available.
|
||||
*/
|
||||
static class PooledDataSourceCondition extends SpringBootCondition {
|
||||
static class PooledDataSourceAvailableCondition extends SpringBootCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
|
||||
@@ -21,6 +21,7 @@ import javax.sql.DataSource;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.jdbc.DatabaseDriver;
|
||||
@@ -38,10 +39,7 @@ abstract class DataSourceConfiguration {
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <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();
|
||||
return (T) properties.initializeDataSourceBuilder().type(type).build();
|
||||
}
|
||||
|
||||
@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
|
||||
@@ -111,4 +109,15 @@ abstract class DataSourceConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
@ConditionalOnMissingBean(DataSource.class)
|
||||
@ConditionalOnProperty(name = "spring.datasource.type")
|
||||
static class Generic {
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource(
|
||||
DataSourceProperties properties) {
|
||||
return properties.initializeDataSourceBuilder().build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -164,6 +164,19 @@ public class DataSourceProperties
|
||||
.get(this.classLoader);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a {@link DataSourceBuilder} with the state of this instance.
|
||||
* @return a {@link DataSourceBuilder} initialized with the customizations
|
||||
* defined on this instance
|
||||
*/
|
||||
public DataSourceBuilder initializeDataSourceBuilder() {
|
||||
return DataSourceBuilder.create(getClassLoader())
|
||||
.type(getType())
|
||||
.driverClassName(determineDriverClassName())
|
||||
.url(determineUrl()).username(determineUsername())
|
||||
.password(determinePassword());
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ import org.springframework.boot.test.util.EnvironmentTestUtils;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -184,18 +185,39 @@ public class DataSourceAutoConfigurationTests {
|
||||
assertThat(pool.getUsername()).isEqualTo("sa");
|
||||
}
|
||||
|
||||
/**
|
||||
* This test makes sure that if no supported data source is present, a datasource
|
||||
* is still created if "spring.datasource.type" is present.
|
||||
*/
|
||||
@Test
|
||||
public void explicitType() {
|
||||
public void explicitTypeNoSupportedDataSource() {
|
||||
EnvironmentTestUtils.addEnvironment(this.context,
|
||||
"spring.datasource.driverClassName:org.hsqldb.jdbcDriver",
|
||||
"spring.datasource.url:jdbc:hsqldb:mem:testdb",
|
||||
"spring.datasource.type:" + HikariDataSource.class.getName());
|
||||
"spring.datasource.type:" + SimpleDriverDataSource.class.getName());
|
||||
this.context.setClassLoader(new HidePackagesClassLoader(
|
||||
"org.apache.tomcat", "com.zaxxer.hikari", "org.apache.commons.dbcp",
|
||||
"org.apache.commons.dbcp2"));
|
||||
testExplicitType();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void explicitTypeSupportedDataSource() {
|
||||
EnvironmentTestUtils.addEnvironment(this.context,
|
||||
"spring.datasource.driverClassName:org.hsqldb.jdbcDriver",
|
||||
"spring.datasource.url:jdbc:hsqldb:mem:testdb",
|
||||
"spring.datasource.type:" + SimpleDriverDataSource.class.getName());
|
||||
testExplicitType();
|
||||
}
|
||||
|
||||
private void testExplicitType() {
|
||||
this.context.register(DataSourceAutoConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
assertThat(this.context.getBeansOfType(DataSource.class)).hasSize(1);
|
||||
DataSource bean = this.context.getBean(DataSource.class);
|
||||
assertThat(bean).isNotNull();
|
||||
assertThat(bean.getClass()).isEqualTo(HikariDataSource.class);
|
||||
assertThat(bean.getClass()).isEqualTo(SimpleDriverDataSource.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -232,21 +254,7 @@ public class DataSourceAutoConfigurationTests {
|
||||
EnvironmentTestUtils.addEnvironment(this.context,
|
||||
"spring.datasource.driverClassName:org.hsqldb.jdbcDriver",
|
||||
"spring.datasource.url:jdbc:hsqldb:mem:testdb");
|
||||
this.context.setClassLoader(
|
||||
new URLClassLoader(new URL[0], getClass().getClassLoader()) {
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve)
|
||||
throws ClassNotFoundException {
|
||||
for (String hiddenPackage : hiddenPackages) {
|
||||
if (name.startsWith(hiddenPackage)) {
|
||||
throw new ClassNotFoundException();
|
||||
}
|
||||
}
|
||||
return super.loadClass(name, resolve);
|
||||
}
|
||||
|
||||
});
|
||||
this.context.setClassLoader(new HidePackagesClassLoader(hiddenPackages));
|
||||
this.context.register(DataSourceAutoConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class);
|
||||
this.context.refresh();
|
||||
@@ -312,4 +320,27 @@ public class DataSourceAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
private static final class HidePackagesClassLoader extends URLClassLoader {
|
||||
|
||||
private final String[] hiddenPackages;
|
||||
|
||||
private HidePackagesClassLoader(String... hiddenPackages) {
|
||||
super(new URL[0], DataSourceAutoConfigurationTests.class.getClassLoader());
|
||||
this.hiddenPackages = hiddenPackages;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve)
|
||||
throws ClassNotFoundException {
|
||||
for (String hiddenPackage : this.hiddenPackages) {
|
||||
if (name.startsWith(hiddenPackage)) {
|
||||
throw new ClassNotFoundException();
|
||||
}
|
||||
}
|
||||
return super.loadClass(name, resolve);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1642,15 +1642,14 @@ a| `log4j2.json` +
|
||||
[[howto-configure-a-datasource]]
|
||||
=== Configure a DataSource
|
||||
To override the default settings just define a `@Bean` of your own of type `DataSource`.
|
||||
Spring Boot provides a utility builder class `DataSourceBuilder` that can be used
|
||||
to create one of the standard ones (if it is on the classpath), or you can just create
|
||||
your own, and bind it to a set of `Environment` properties as explained in
|
||||
<<spring-boot-features.adoc#boot-features-external-config-3rd-party-configuration>>, e.g.
|
||||
As explained in
|
||||
<<spring-boot-features.adoc#boot-features-external-config-3rd-party-configuration>> you
|
||||
can easily bind it to a set of `Environment` properties:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix="datasource.mine")
|
||||
@ConfigurationProperties(prefix="datasource.fancy")
|
||||
public DataSource dataSource() {
|
||||
return new FancyDataSource();
|
||||
}
|
||||
@@ -1658,11 +1657,38 @@ your own, and bind it to a set of `Environment` properties as explained in
|
||||
|
||||
[source,properties,indent=0]
|
||||
----
|
||||
datasource.mine.jdbcUrl=jdbc:h2:mem:mydb
|
||||
datasource.mine.user=sa
|
||||
datasource.fancy.jdbcUrl=jdbc:h2:mem:mydb
|
||||
datasource.fancy.username=sa
|
||||
datasource.fancy.poolSize=30
|
||||
----
|
||||
|
||||
Spring Boot also provides a utility builder class `DataSourceBuilder` that can be used
|
||||
to create one of the standard data sources (if it is on the classpath), or you can just
|
||||
create your own. If you want to reuse the customizations of `DataSourceProperties`, you
|
||||
can easily initialize a `DataSourceBuilder` from it:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes,attributes"]
|
||||
----
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix="datasource.mine")
|
||||
public DataSource dataSource(DataSourceProperties properties) {
|
||||
return properties.initializeDataSourceBuilder()
|
||||
// additional customizations
|
||||
.build();
|
||||
}
|
||||
----
|
||||
|
||||
[source,properties,indent=0]
|
||||
----
|
||||
spring.datasource.url=jdbc:h2:mem:mydb
|
||||
spring.datasource.username=sa
|
||||
datasource.mine.poolSize=30
|
||||
----
|
||||
|
||||
In this scenario, you keep the standard properties exposed by Spring Boot with your
|
||||
custom `DataSource` arrangement. By adding `@ConfigurationProperties`, you can also
|
||||
expose additional implementation-specific settings in a dedicated namespace.
|
||||
|
||||
See _<<spring-boot-features.adoc#boot-features-configure-datasource>>_ in the
|
||||
'`Spring Boot features`' section and the
|
||||
{sc-spring-boot-autoconfigure}/jdbc/DataSourceAutoConfiguration.{sc-ext}[`DataSourceAutoConfiguration`]
|
||||
|
||||
Reference in New Issue
Block a user