Generalize database initialization detection and include R2DBC

Closes gh-25818
This commit is contained in:
Andy Wilkinson
2021-03-29 19:02:40 +01:00
parent 22b02a6c6f
commit 9b65409e23
34 changed files with 297 additions and 236 deletions

View File

@@ -21,18 +21,18 @@ import java.util.Set;
import org.flywaydb.core.Flyway;
import org.springframework.boot.jdbc.init.dependency.AbstractBeansOfTypeDataSourceInitializerDetector;
import org.springframework.boot.jdbc.init.dependency.DataSourceInitializerDetector;
import org.springframework.boot.sql.init.dependency.AbstractBeansOfTypeDatabaseInitializerDetector;
import org.springframework.boot.sql.init.dependency.DatabaseInitializerDetector;
/**
* A {@link DataSourceInitializerDetector} for {@link Flyway}.
* A {@link DatabaseInitializerDetector} for {@link Flyway}.
*
* @author Andy Wilkinson
*/
class FlywayDataSourceInitializerDetector extends AbstractBeansOfTypeDataSourceInitializerDetector {
class FlywayDatabaseInitializerDetector extends AbstractBeansOfTypeDatabaseInitializerDetector {
@Override
protected Set<Class<?>> getDataSourceInitializerBeanTypes() {
protected Set<Class<?>> getDatabaseInitializerBeanTypes() {
return Collections.singleton(Flyway.class);
}

View File

@@ -20,21 +20,21 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.springframework.boot.jdbc.init.dependency.AbstractBeansOfTypeDependsOnDataSourceInitializationDetector;
import org.springframework.boot.jdbc.init.dependency.DependsOnDataSourceInitializationDetector;
import org.springframework.boot.sql.init.dependency.AbstractBeansOfTypeDependsOnDatabaseInitializationDetector;
import org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
/**
* {@link DependsOnDataSourceInitializationDetector} for Spring Framework's JDBC support.
* {@link DependsOnDatabaseInitializationDetector} for Spring Framework's JDBC support.
*
* @author Andy Wilkinson
*/
class SpringJdbcDependsOnDataSourceInitializationDetector
extends AbstractBeansOfTypeDependsOnDataSourceInitializationDetector {
class SpringJdbcDependsOnDatabaseInitializationDetector
extends AbstractBeansOfTypeDependsOnDatabaseInitializationDetector {
@Override
protected Set<Class<?>> getDependsOnDataSourceInitializationBeanTypes() {
protected Set<Class<?>> getDependsOnDatabaseInitializationBeanTypes() {
return new HashSet<>(Arrays.asList(JdbcOperations.class, NamedParameterJdbcOperations.class));
}

View File

@@ -19,19 +19,18 @@ package org.springframework.boot.jdbc.init;
import java.util.Collections;
import java.util.Set;
import org.springframework.boot.jdbc.init.dependency.AbstractBeansOfTypeDataSourceInitializerDetector;
import org.springframework.boot.jdbc.init.dependency.DataSourceInitializerDetector;
import org.springframework.boot.sql.init.dependency.AbstractBeansOfTypeDatabaseInitializerDetector;
import org.springframework.boot.sql.init.dependency.DatabaseInitializerDetector;
/**
* A {@link DataSourceInitializerDetector} for
* {@link DataSourceScriptDatabaseInitializer}.
* A {@link DatabaseInitializerDetector} for {@link DataSourceScriptDatabaseInitializer}.
*
* @author Andy Wilkinson
*/
class DataSourceScriptDatabaseInitializerDetector extends AbstractBeansOfTypeDataSourceInitializerDetector {
class DataSourceScriptDatabaseInitializerDetector extends AbstractBeansOfTypeDatabaseInitializerDetector {
@Override
protected Set<Class<?>> getDataSourceInitializerBeanTypes() {
protected Set<Class<?>> getDatabaseInitializerBeanTypes() {
return Collections.singleton(DataSourceScriptDatabaseInitializer.class);
}

View File

@@ -21,19 +21,18 @@ import java.util.Set;
import org.jooq.DSLContext;
import org.springframework.boot.jdbc.init.dependency.AbstractBeansOfTypeDependsOnDataSourceInitializationDetector;
import org.springframework.boot.jdbc.init.dependency.DependsOnDataSourceInitializationDetector;
import org.springframework.boot.sql.init.dependency.AbstractBeansOfTypeDependsOnDatabaseInitializationDetector;
import org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector;
/**
* {@link DependsOnDataSourceInitializationDetector} for jOOQ.
* {@link DependsOnDatabaseInitializationDetector} for jOOQ.
*
* @author Andy Wilkinson
*/
class JooqDependsOnDataSourceInitializationDetector
extends AbstractBeansOfTypeDependsOnDataSourceInitializationDetector {
class JooqDependsOnDatabaseInitializationDetector extends AbstractBeansOfTypeDependsOnDatabaseInitializationDetector {
@Override
protected Set<Class<?>> getDependsOnDataSourceInitializationBeanTypes() {
protected Set<Class<?>> getDependsOnDatabaseInitializationBeanTypes() {
return Collections.singleton(DSLContext.class);
}

View File

@@ -21,18 +21,18 @@ import java.util.Set;
import liquibase.integration.spring.SpringLiquibase;
import org.springframework.boot.jdbc.init.dependency.AbstractBeansOfTypeDataSourceInitializerDetector;
import org.springframework.boot.jdbc.init.dependency.DataSourceInitializerDetector;
import org.springframework.boot.sql.init.dependency.AbstractBeansOfTypeDatabaseInitializerDetector;
import org.springframework.boot.sql.init.dependency.DatabaseInitializerDetector;
/**
* A {@link DataSourceInitializerDetector} for Liquibase.
* A {@link DatabaseInitializerDetector} for Liquibase.
*
* @author Andy Wilkinson
*/
class LiquibaseDataSourceInitializerDetector extends AbstractBeansOfTypeDataSourceInitializerDetector {
class LiquibaseDatabaseInitializerDetector extends AbstractBeansOfTypeDatabaseInitializerDetector {
@Override
protected Set<Class<?>> getDataSourceInitializerBeanTypes() {
protected Set<Class<?>> getDatabaseInitializerBeanTypes() {
return Collections.singleton(SpringLiquibase.class);
}

View File

@@ -25,26 +25,26 @@ import javax.persistence.EntityManagerFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.jdbc.init.dependency.AbstractBeansOfTypeDataSourceInitializerDetector;
import org.springframework.boot.jdbc.init.dependency.DataSourceInitializerDetector;
import org.springframework.boot.sql.init.dependency.AbstractBeansOfTypeDatabaseInitializerDetector;
import org.springframework.boot.sql.init.dependency.DatabaseInitializerDetector;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
/**
* A {@link DataSourceInitializerDetector} for JPA.
* A {@link DatabaseInitializerDetector} for JPA.
*
* @author Andy Wilkinson
*/
class JpaDataSourceInitializerDetector extends AbstractBeansOfTypeDataSourceInitializerDetector {
class JpaDatabaseInitializerDetector extends AbstractBeansOfTypeDatabaseInitializerDetector {
private final Environment environment;
JpaDataSourceInitializerDetector(Environment environment) {
JpaDatabaseInitializerDetector(Environment environment) {
this.environment = environment;
}
@Override
protected Set<Class<?>> getDataSourceInitializerBeanTypes() {
protected Set<Class<?>> getDatabaseInitializerBeanTypes() {
boolean deferred = this.environment.getProperty("spring.jpa.defer-datasource-initialization", boolean.class,
false);
return deferred ? Collections.singleton(EntityManagerFactory.class) : Collections.emptySet();
@@ -63,8 +63,8 @@ class JpaDataSourceInitializerDetector extends AbstractBeansOfTypeDataSourceInit
while (iterator.hasNext()) {
String initializerName = iterator.next();
BeanDefinition initializerDefinition = beanFactory.getBeanDefinition(initializerName);
if (JpaDataSourceInitializerDetector.class.getName()
.equals(initializerDefinition.getAttribute(DataSourceInitializerDetector.class.getName()))) {
if (JpaDatabaseInitializerDetector.class.getName()
.equals(initializerDefinition.getAttribute(DatabaseInitializerDetector.class.getName()))) {
iterator.remove();
jpaInitializers.add(initializerName);
}

View File

@@ -23,27 +23,26 @@ import java.util.Set;
import javax.persistence.EntityManagerFactory;
import org.springframework.boot.jdbc.init.dependency.AbstractBeansOfTypeDependsOnDataSourceInitializationDetector;
import org.springframework.boot.jdbc.init.dependency.DependsOnDataSourceInitializationDetector;
import org.springframework.boot.sql.init.dependency.AbstractBeansOfTypeDependsOnDatabaseInitializationDetector;
import org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector;
import org.springframework.core.env.Environment;
import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
/**
* {@link DependsOnDataSourceInitializationDetector} for JPA.
* {@link DependsOnDatabaseInitializationDetector} for JPA.
*
* @author Andy Wilkinson
*/
class JpaDependsOnDataSourceInitializationDetector
extends AbstractBeansOfTypeDependsOnDataSourceInitializationDetector {
class JpaDependsOnDatabaseInitializationDetector extends AbstractBeansOfTypeDependsOnDatabaseInitializationDetector {
private final Environment environment;
JpaDependsOnDataSourceInitializationDetector(Environment environment) {
JpaDependsOnDatabaseInitializationDetector(Environment environment) {
this.environment = environment;
}
@Override
protected Set<Class<?>> getDependsOnDataSourceInitializationBeanTypes() {
protected Set<Class<?>> getDependsOnDatabaseInitializationBeanTypes() {
boolean postpone = this.environment.getProperty("spring.jpa.defer-datasource-initialization", boolean.class,
false);
return postpone ? Collections.emptySet()

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2012-2021 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
*
* https://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.r2dbc.init;
import java.util.Collections;
import java.util.Set;
import org.springframework.boot.sql.init.dependency.AbstractBeansOfTypeDatabaseInitializerDetector;
import org.springframework.boot.sql.init.dependency.DatabaseInitializerDetector;
/**
* A {@link DatabaseInitializerDetector} for {@link R2dbcScriptDatabaseInitializer}.
*
* @author Andy Wilkinson
*/
class R2dbcScriptDatabaseInitializerDetector extends AbstractBeansOfTypeDatabaseInitializerDetector {
@Override
protected Set<Class<?>> getDatabaseInitializerBeanTypes() {
return Collections.singleton(R2dbcScriptDatabaseInitializer.class);
}
}

View File

@@ -14,34 +14,32 @@
* limitations under the License.
*/
package org.springframework.boot.jdbc.init.dependency;
package org.springframework.boot.sql.init.dependency;
import java.util.Collections;
import java.util.Set;
import javax.sql.DataSource;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
/**
* Base class for {@link DataSourceInitializerDetector DataSourceInitializerDetectors}
* that detect {@link DataSource} beans by type.
* Base class for {@link DatabaseInitializerDetector DatabaseInitializerDetectors} that
* detect database initializer beans by type.
*
* @author Andy Wilkinson
* @since 2.5.0
*/
public abstract class AbstractBeansOfTypeDataSourceInitializerDetector implements DataSourceInitializerDetector {
public abstract class AbstractBeansOfTypeDatabaseInitializerDetector implements DatabaseInitializerDetector {
/**
* Returns the bean types that should be detected as being data source initializers.
* @return the data source initializer bean types
* Returns the bean types that should be detected as being database initializers.
* @return the database initializer bean types
*/
protected abstract Set<Class<?>> getDataSourceInitializerBeanTypes();
protected abstract Set<Class<?>> getDatabaseInitializerBeanTypes();
@Override
public Set<String> detect(ConfigurableListableBeanFactory beanFactory) {
try {
Set<Class<?>> types = getDataSourceInitializerBeanTypes();
Set<Class<?>> types = getDatabaseInitializerBeanTypes();
return new BeansOfTypeDetector(types).detect(beanFactory);
}
catch (Throwable ex) {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.jdbc.init.dependency;
package org.springframework.boot.sql.init.dependency;
import java.util.Collections;
import java.util.Set;
@@ -22,27 +22,27 @@ import java.util.Set;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
/**
* Base class for {@link DependsOnDataSourceInitializationDetector
* InitializedDataSourceDependentDetector} that detect by type beans that depend upon data
* source initialization.
* Base class for {@link DependsOnDatabaseInitializationDetector
* DependsOnDatabaseInitializationDetectors} that detect by type beans that depend upon
* database initialization.
*
* @author Andy Wilkinson
* @since 2.5.0
*/
public abstract class AbstractBeansOfTypeDependsOnDataSourceInitializationDetector
implements DependsOnDataSourceInitializationDetector {
public abstract class AbstractBeansOfTypeDependsOnDatabaseInitializationDetector
implements DependsOnDatabaseInitializationDetector {
/**
* Returns the bean types that should be detected as depending on data source
* Returns the bean types that should be detected as depending on database
* initialization.
* @return the data source initialization dependent bean types
* @return the database initialization dependent bean types
*/
protected abstract Set<Class<?>> getDependsOnDataSourceInitializationBeanTypes();
protected abstract Set<Class<?>> getDependsOnDatabaseInitializationBeanTypes();
@Override
public Set<String> detect(ConfigurableListableBeanFactory beanFactory) {
try {
Set<Class<?>> types = getDependsOnDataSourceInitializationBeanTypes();
Set<Class<?>> types = getDependsOnDatabaseInitializationBeanTypes();
return new BeansOfTypeDetector(types).detect(beanFactory);
}
catch (Throwable ex) {

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.jdbc.init.dependency;
package org.springframework.boot.sql.init.dependency;
import java.util.HashSet;
import java.util.Set;
@@ -22,18 +22,18 @@ import java.util.Set;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
/**
* {@link DependsOnDataSourceInitializationDetector} that detects beans annotated with
* {@link DependsOnDataSourceInitialization}.
* {@link DependsOnDatabaseInitializationDetector} that detects beans annotated with
* {@link DependsOnDatabaseInitialization}.
*
* @author Andy Wilkinson
*/
class AnnotationDependsOnDataSourceInitializationDetector implements DependsOnDataSourceInitializationDetector {
class AnnotationDependsOnDatabaseInitializationDetector implements DependsOnDatabaseInitializationDetector {
@Override
public Set<String> detect(ConfigurableListableBeanFactory beanFactory) {
Set<String> dependentBeans = new HashSet<>();
for (String beanName : beanFactory.getBeanDefinitionNames()) {
if (beanFactory.findAnnotationOnBean(beanName, DependsOnDataSourceInitialization.class) != null) {
if (beanFactory.findAnnotationOnBean(beanName, DependsOnDatabaseInitialization.class) != null) {
dependentBeans.add(beanName);
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.jdbc.init.dependency;
package org.springframework.boot.sql.init.dependency;
import java.util.Arrays;
import java.util.HashSet;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.jdbc.init.dependency;
package org.springframework.boot.sql.init.dependency;
import java.util.Collection;
import java.util.Collections;
@@ -22,8 +22,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.sql.DataSource;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanDefinition;
@@ -40,93 +38,95 @@ import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.StringUtils;
/**
* Configures beans that depend upon DataSource initialization with
* {@link BeanDefinition#getDependsOn()} dependencies upon beans that perform
* {@link DataSource} initialization. Intended for {@link Import import} in configuration
* classes that define {@code DataSource} initialization beans or that define beans that
* require DataSource initialization to have completed before they are initialized.
* Configures beans that depend upon SQL database initialization with
* {@link BeanDefinition#getDependsOn() dependencies} upon beans that perform database
* initialization. Intended for {@link Import import} in configuration classes that define
* database initialization beans or that define beans that require database initialization
* to have completed before they are initialized.
* <p>
* Beans that initialize a {@link DataSource} are identified by
* {@link DataSourceInitializerDetector DataSourceInitializerDetectors}. Beans that depend
* upon DataSource initialization are identified by
* {@link DependsOnDataSourceInitializationDetector
* DependsOnDataSourceInitializationDetectors}.
* Beans that initialize a database are identified by {@link DatabaseInitializerDetector
* DatabaseInitializerDetectors}. Beans that depend upon database initialization are
* identified by {@link DependsOnDatabaseInitializationDetector
* DependsOnDatabaseInitializationDetectors}.
*
* @author Andy Wilkinson
* @since 2.5.0
* @see DataSourceInitializerDetector
* @see DependsOnDataSourceInitializationDetector
* @see DependsOnDataSourceInitialization
* @see DatabaseInitializerDetector
* @see DependsOnDatabaseInitializationDetector
* @see DependsOnDatabaseInitialization
*/
public class DataSourceInitializationDependencyConfigurer implements ImportBeanDefinitionRegistrar {
public class DatabaseInitializationDependencyConfigurer implements ImportBeanDefinitionRegistrar {
private final Environment environment;
DataSourceInitializationDependencyConfigurer(Environment environment) {
DatabaseInitializationDependencyConfigurer(Environment environment) {
this.environment = environment;
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (registry.containsBeanDefinition(DependsOnDataSourceInitializationPostProcessor.class.getName())) {
if (registry.containsBeanDefinition(DependsOnDatabaseInitializationPostProcessor.class.getName())) {
return;
}
registry.registerBeanDefinition(DependsOnDataSourceInitializationPostProcessor.class.getName(),
registry.registerBeanDefinition(DependsOnDatabaseInitializationPostProcessor.class.getName(),
BeanDefinitionBuilder
.genericBeanDefinition(DependsOnDataSourceInitializationPostProcessor.class,
() -> new DependsOnDataSourceInitializationPostProcessor(this.environment))
.genericBeanDefinition(DependsOnDatabaseInitializationPostProcessor.class,
() -> new DependsOnDatabaseInitializationPostProcessor(this.environment))
.getBeanDefinition());
}
static class DependsOnDataSourceInitializationPostProcessor implements BeanFactoryPostProcessor {
static class DependsOnDatabaseInitializationPostProcessor implements BeanFactoryPostProcessor {
private final Environment environment;
DependsOnDataSourceInitializationPostProcessor(Environment environment) {
DependsOnDatabaseInitializationPostProcessor(Environment environment) {
this.environment = environment;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
Set<String> detectedDataSourceInitializers = detectDataSourceInitializers(beanFactory);
for (String dependentDefinitionName : detectDependsOnDataSourceInitialization(beanFactory,
Set<String> detectedDatabaseInitializers = detectDatabaseInitializers(beanFactory);
if (detectedDatabaseInitializers.isEmpty()) {
return;
}
for (String dependentDefinitionName : detectDependsOnDatabaseInitialization(beanFactory,
this.environment)) {
BeanDefinition definition = getBeanDefinition(dependentDefinitionName, beanFactory);
String[] dependencies = definition.getDependsOn();
for (String dependencyName : detectedDataSourceInitializers) {
for (String dependencyName : detectedDatabaseInitializers) {
dependencies = StringUtils.addStringToArray(dependencies, dependencyName);
}
definition.setDependsOn(dependencies);
}
}
private Set<String> detectDataSourceInitializers(ConfigurableListableBeanFactory beanFactory) {
List<DataSourceInitializerDetector> detectors = instantiateDetectors(beanFactory, this.environment,
DataSourceInitializerDetector.class);
private Set<String> detectDatabaseInitializers(ConfigurableListableBeanFactory beanFactory) {
List<DatabaseInitializerDetector> detectors = instantiateDetectors(beanFactory, this.environment,
DatabaseInitializerDetector.class);
Set<String> detected = new HashSet<>();
for (DataSourceInitializerDetector detector : detectors) {
for (DatabaseInitializerDetector detector : detectors) {
for (String initializerName : detector.detect(beanFactory)) {
detected.add(initializerName);
beanFactory.getBeanDefinition(initializerName)
.setAttribute(DataSourceInitializerDetector.class.getName(), detector.getClass().getName());
.setAttribute(DatabaseInitializerDetector.class.getName(), detector.getClass().getName());
}
}
detected = Collections.unmodifiableSet(detected);
for (DataSourceInitializerDetector detector : detectors) {
for (DatabaseInitializerDetector detector : detectors) {
detector.detectionComplete(beanFactory, detected);
}
return detected;
}
private Collection<String> detectDependsOnDataSourceInitialization(ConfigurableListableBeanFactory beanFactory,
private Collection<String> detectDependsOnDatabaseInitialization(ConfigurableListableBeanFactory beanFactory,
Environment environment) {
List<DependsOnDataSourceInitializationDetector> detectors = instantiateDetectors(beanFactory, environment,
DependsOnDataSourceInitializationDetector.class);
Set<String> dependentUponDataSourceInitialization = new HashSet<>();
for (DependsOnDataSourceInitializationDetector detector : detectors) {
dependentUponDataSourceInitialization.addAll(detector.detect(beanFactory));
List<DependsOnDatabaseInitializationDetector> detectors = instantiateDetectors(beanFactory, environment,
DependsOnDatabaseInitializationDetector.class);
Set<String> dependentUponDatabaseInitialization = new HashSet<>();
for (DependsOnDatabaseInitializationDetector detector : detectors) {
dependentUponDatabaseInitialization.addAll(detector.detect(beanFactory));
}
return dependentUponDataSourceInitialization;
return dependentUponDatabaseInitialization;
}
private <T> List<T> instantiateDetectors(ConfigurableListableBeanFactory beanFactory, Environment environment,

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.jdbc.init.dependency;
package org.springframework.boot.sql.init.dependency;
import java.util.Set;
@@ -23,14 +23,14 @@ import javax.sql.DataSource;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
/**
* Detects beans that initialize a {@link DataSource}. Implementations should be
* registered in {@code META-INF/spring.factories} under the key
* {@code org.springframework.boot.jdbc.init.DependsOnDataSourceInitializationDetector}.
* Detects beans that initialize an SQL database. Implementations should be registered in
* {@code META-INF/spring.factories} under the key
* {@code org.springframework.boot.sql.init.dependency.DatabaseInitializerDetector}.
*
* @author Andy Wilkinson
* @since 2.5.0
*/
public interface DataSourceInitializerDetector {
public interface DatabaseInitializerDetector {
/**
* Detect beans defined in the given {@code beanFactory} that initialize a

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.jdbc.init.dependency;
package org.springframework.boot.sql.init.dependency;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
@@ -25,9 +25,8 @@ import java.lang.annotation.Target;
import org.springframework.context.annotation.Bean;
/**
* Indicate that a bean's creation and initialization depends upon data source
* initialization having completed. May be used on a bean's class or its
* {@link Bean @Bean} definition.
* Indicate that a bean's creation and initialization depends upon database initialization
* having completed. May be used on a bean's class or its {@link Bean @Bean} definition.
*
* @author Andy Wilkinson
* @since 2.5.0
@@ -35,6 +34,6 @@ import org.springframework.context.annotation.Bean;
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DependsOnDataSourceInitialization {
public @interface DependsOnDatabaseInitialization {
}

View File

@@ -14,30 +14,27 @@
* limitations under the License.
*/
package org.springframework.boot.jdbc.init.dependency;
package org.springframework.boot.sql.init.dependency;
import java.util.Set;
import javax.sql.DataSource;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
/**
* Detects beans that depend on {@link DataSource} initialization. Implementations should
* be registered in {@code META-INF/spring.factories} under the key
* {@code org.springframework.boot.jdbc.init.DependsOnDataSourceInitializationDetector}.
* Detects beans that depend on database initialization. Implementations should be
* registered in {@code META-INF/spring.factories} under the key
* {@code org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector}.
*
* @author Andy Wilkinson
* @since 2.5.0
*/
public interface DependsOnDataSourceInitializationDetector {
public interface DependsOnDatabaseInitializationDetector {
/**
* Detect beans defined in the given {@code beanFactory} that depend on
* {@link DataSource} initialization. If no beans are detected, an empty set is
* returned.
* Detect beans defined in the given {@code beanFactory} that depend on database
* initialization. If no beans are detected, an empty set is returned.
* @param beanFactory bean factory to examine
* @return names of any beans that depend upon {@code DataSource} initialization
* @return names of any beans that depend upon database initialization
*/
Set<String> detect(ConfigurableListableBeanFactory beanFactory);

View File

@@ -15,6 +15,6 @@
*/
/**
* Infrastructure for establishing {@code DataSource} initialization bean dependencies.
* Infrastructure for establishing database initialization bean dependencies.
*/
package org.springframework.boot.jdbc.init.dependency;
package org.springframework.boot.sql.init.dependency;

View File

@@ -79,16 +79,17 @@ org.springframework.boot.liquibase.LiquibaseChangelogMissingFailureAnalyzer
org.springframework.boot.diagnostics.FailureAnalysisReporter=\
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter
# DataSource Initializer Detectors
org.springframework.boot.jdbc.init.dependency.DataSourceInitializerDetector=\
org.springframework.boot.flyway.FlywayDataSourceInitializerDetector,\
# Database Initializer Detectors
org.springframework.boot.sql.init.dependency.DatabaseInitializerDetector=\
org.springframework.boot.flyway.FlywayDatabaseInitializerDetector,\
org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializerDetector,\
org.springframework.boot.liquibase.LiquibaseDataSourceInitializerDetector,\
org.springframework.boot.orm.jpa.JpaDataSourceInitializerDetector
org.springframework.boot.liquibase.LiquibaseDatabaseInitializerDetector,\
org.springframework.boot.orm.jpa.JpaDatabaseInitializerDetector,\
org.springframework.boot.r2dbc.init.R2dbcScriptDatabaseInitializerDetector
# Depends On DataSource Initialization Detectors
org.springframework.boot.jdbc.init.dependency.DependsOnDataSourceInitializationDetector=\
org.springframework.boot.jdbc.init.dependency.AnnotationDependsOnDataSourceInitializationDetector,\
org.springframework.boot.jdbc.SpringJdbcDependsOnDataSourceInitializationDetector,\
org.springframework.boot.jooq.JooqDependsOnDataSourceInitializationDetector,\
org.springframework.boot.orm.jpa.JpaDependsOnDataSourceInitializationDetector
# Depends On Database Initialization Detectors
org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector=\
org.springframework.boot.sql.init.dependency.AnnotationDependsOnDatabaseInitializationDetector,\
org.springframework.boot.jdbc.SpringJdbcDependsOnDatabaseInitializationDetector,\
org.springframework.boot.jooq.JooqDependsOnDatabaseInitializationDetector,\
org.springframework.boot.orm.jpa.JpaDependsOnDatabaseInitializationDetector

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.jdbc.init.dependency;
package org.springframework.boot.sql.init.dependency;
import java.io.File;
import java.io.FileWriter;
@@ -51,55 +51,54 @@ import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link DataSourceInitializationDependencyConfigurer}.
* Tests for {@link DatabaseInitializationDependencyConfigurer}.
*
* @author Andy Wilkinson
*/
class DataSourceInitializationDependencyConfigurerTests {
class DatabaseInitializationDependencyConfigurerTests {
private final ConfigurableEnvironment environment = new MockEnvironment();
DataSourceInitializerDetector dataSourceInitializerDetector = MockedDataSourceInitializerDetector.mock;
DatabaseInitializerDetector databaseInitializerDetector = MockedDatabaseInitializerDetector.mock;
DependsOnDataSourceInitializationDetector dependsOnDataSourceInitializationDetector = MockedDependsOnDataSourceInitializationDetector.mock;
DependsOnDatabaseInitializationDetector dependsOnDatabaseInitializationDetector = MockedDependsOnDatabaseInitializationDetector.mock;
@TempDir
File temp;
@BeforeEach
void resetMocks() {
reset(MockedDataSourceInitializerDetector.mock, MockedDependsOnDataSourceInitializationDetector.mock);
reset(MockedDatabaseInitializerDetector.mock, MockedDependsOnDatabaseInitializationDetector.mock);
}
@Test
void whenDetectorsAreCreatedThenTheEnvironmentCanBeInjected() {
performDetection(Arrays.asList(ConstructorInjectionDataSourceInitializerDetector.class,
ConstructorInjectionDependsOnDataSourceInitializationDetector.class), (context) -> {
performDetection(Arrays.asList(ConstructorInjectionDatabaseInitializerDetector.class,
ConstructorInjectionDependsOnDatabaseInitializationDetector.class), (context) -> {
context.refresh();
assertThat(ConstructorInjectionDataSourceInitializerDetector.environment)
.isEqualTo(this.environment);
assertThat(ConstructorInjectionDependsOnDataSourceInitializationDetector.environment)
assertThat(ConstructorInjectionDatabaseInitializerDetector.environment).isEqualTo(this.environment);
assertThat(ConstructorInjectionDependsOnDatabaseInitializationDetector.environment)
.isEqualTo(this.environment);
});
}
@Test
void whenDependenciesAreConfiguredThenBeansThatDependUponDataSourceInitializationDependUponDetectedDataSourceInitializers() {
void whenDependenciesAreConfiguredThenBeansThatDependUponDatabaseInitializationDependUponDetectedDatabaseInitializers() {
BeanDefinition alpha = BeanDefinitionBuilder.genericBeanDefinition(String.class).getBeanDefinition();
BeanDefinition bravo = BeanDefinitionBuilder.genericBeanDefinition(String.class).getBeanDefinition();
performDetection(Arrays.asList(MockedDataSourceInitializerDetector.class,
MockedDependsOnDataSourceInitializationDetector.class), (context) -> {
performDetection(Arrays.asList(MockedDatabaseInitializerDetector.class,
MockedDependsOnDatabaseInitializationDetector.class), (context) -> {
context.registerBeanDefinition("alpha", alpha);
context.registerBeanDefinition("bravo", bravo);
given(this.dataSourceInitializerDetector.detect(context.getBeanFactory()))
given(this.databaseInitializerDetector.detect(context.getBeanFactory()))
.willReturn(Collections.singleton("alpha"));
given(this.dependsOnDataSourceInitializationDetector.detect(context.getBeanFactory()))
given(this.dependsOnDatabaseInitializationDetector.detect(context.getBeanFactory()))
.willReturn(Collections.singleton("bravo"));
context.refresh();
assertThat(alpha.getAttribute(DataSourceInitializerDetector.class.getName()))
.isEqualTo(MockedDataSourceInitializerDetector.class.getName());
assertThat(bravo.getAttribute(DataSourceInitializerDetector.class.getName())).isNull();
verify(this.dataSourceInitializerDetector).detectionComplete(context.getBeanFactory(),
assertThat(alpha.getAttribute(DatabaseInitializerDetector.class.getName()))
.isEqualTo(MockedDatabaseInitializerDetector.class.getName());
assertThat(bravo.getAttribute(DatabaseInitializerDetector.class.getName())).isNull();
verify(this.databaseInitializerDetector).detectionComplete(context.getBeanFactory(),
Collections.singleton("alpha"));
assertThat(bravo.getDependsOn()).containsExactly("alpha");
});
@@ -118,17 +117,17 @@ class DataSourceInitializationDependencyConfigurerTests {
}
@Configuration(proxyBeanMethods = false)
@Import(DataSourceInitializationDependencyConfigurer.class)
@Import(DatabaseInitializationDependencyConfigurer.class)
static class DependencyConfigurerConfiguration {
}
static class ConstructorInjectionDataSourceInitializerDetector implements DataSourceInitializerDetector {
static class ConstructorInjectionDatabaseInitializerDetector implements DatabaseInitializerDetector {
private static Environment environment;
ConstructorInjectionDataSourceInitializerDetector(Environment environment) {
ConstructorInjectionDataSourceInitializerDetector.environment = environment;
ConstructorInjectionDatabaseInitializerDetector(Environment environment) {
ConstructorInjectionDatabaseInitializerDetector.environment = environment;
}
@Override
@@ -138,13 +137,13 @@ class DataSourceInitializationDependencyConfigurerTests {
}
static class ConstructorInjectionDependsOnDataSourceInitializationDetector
implements DependsOnDataSourceInitializationDetector {
static class ConstructorInjectionDependsOnDatabaseInitializationDetector
implements DependsOnDatabaseInitializationDetector {
private static Environment environment;
ConstructorInjectionDependsOnDataSourceInitializationDetector(Environment environment) {
ConstructorInjectionDependsOnDataSourceInitializationDetector.environment = environment;
ConstructorInjectionDependsOnDatabaseInitializationDetector(Environment environment) {
ConstructorInjectionDependsOnDatabaseInitializationDetector.environment = environment;
}
@Override
@@ -154,40 +153,40 @@ class DataSourceInitializationDependencyConfigurerTests {
}
static class MockedDataSourceInitializerDetector implements DataSourceInitializerDetector {
static class MockedDatabaseInitializerDetector implements DatabaseInitializerDetector {
private static DataSourceInitializerDetector mock = Mockito.mock(DataSourceInitializerDetector.class);
private static DatabaseInitializerDetector mock = Mockito.mock(DatabaseInitializerDetector.class);
@Override
public Set<String> detect(ConfigurableListableBeanFactory beanFactory) {
return MockedDataSourceInitializerDetector.mock.detect(beanFactory);
return MockedDatabaseInitializerDetector.mock.detect(beanFactory);
}
@Override
public void detectionComplete(ConfigurableListableBeanFactory beanFactory,
Set<String> dataSourceInitializerNames) {
mock.detectionComplete(beanFactory, dataSourceInitializerNames);
Set<String> databaseInitializerNames) {
mock.detectionComplete(beanFactory, databaseInitializerNames);
}
}
static class MockedDependsOnDataSourceInitializationDetector implements DependsOnDataSourceInitializationDetector {
static class MockedDependsOnDatabaseInitializationDetector implements DependsOnDatabaseInitializationDetector {
private static DependsOnDataSourceInitializationDetector mock = Mockito
.mock(DependsOnDataSourceInitializationDetector.class);
private static DependsOnDatabaseInitializationDetector mock = Mockito
.mock(DependsOnDatabaseInitializationDetector.class);
@Override
public Set<String> detect(ConfigurableListableBeanFactory beanFactory) {
return MockedDependsOnDataSourceInitializationDetector.mock.detect(beanFactory);
return MockedDependsOnDatabaseInitializationDetector.mock.detect(beanFactory);
}
}
static class DetectorSpringFactoriesClassLoader extends ClassLoader {
private final Set<Class<DataSourceInitializerDetector>> dataSourceInitializerDetectors = new HashSet<>();
private final Set<Class<DatabaseInitializerDetector>> databaseInitializerDetectors = new HashSet<>();
private final Set<Class<DependsOnDataSourceInitializationDetector>> dependsOnDataSourceInitializationDetectors = new HashSet<>();
private final Set<Class<DependsOnDatabaseInitializationDetector>> dependsOnDatabaseInitializationDetectors = new HashSet<>();
private final File temp;
@@ -197,12 +196,12 @@ class DataSourceInitializationDependencyConfigurerTests {
@SuppressWarnings("unchecked")
void register(Class<?> detector) {
if (DataSourceInitializerDetector.class.isAssignableFrom(detector)) {
this.dataSourceInitializerDetectors.add((Class<DataSourceInitializerDetector>) detector);
if (DatabaseInitializerDetector.class.isAssignableFrom(detector)) {
this.databaseInitializerDetectors.add((Class<DatabaseInitializerDetector>) detector);
}
else if (DependsOnDataSourceInitializationDetector.class.isAssignableFrom(detector)) {
this.dependsOnDataSourceInitializationDetectors
.add((Class<DependsOnDataSourceInitializationDetector>) detector);
else if (DependsOnDatabaseInitializationDetector.class.isAssignableFrom(detector)) {
this.dependsOnDatabaseInitializationDetectors
.add((Class<DependsOnDatabaseInitializationDetector>) detector);
}
else {
throw new IllegalArgumentException("Unsupported detector type '" + detector.getName() + "'");
@@ -215,10 +214,10 @@ class DataSourceInitializationDependencyConfigurerTests {
return super.findResources(name);
}
Properties properties = new Properties();
properties.put(DataSourceInitializerDetector.class.getName(), String.join(",",
this.dataSourceInitializerDetectors.stream().map(Class::getName).collect(Collectors.toList())));
properties.put(DependsOnDataSourceInitializationDetector.class.getName(),
String.join(",", this.dependsOnDataSourceInitializationDetectors.stream().map(Class::getName)
properties.put(DatabaseInitializerDetector.class.getName(), String.join(",",
this.databaseInitializerDetectors.stream().map(Class::getName).collect(Collectors.toList())));
properties.put(DependsOnDatabaseInitializationDetector.class.getName(),
String.join(",", this.dependsOnDatabaseInitializationDetectors.stream().map(Class::getName)
.collect(Collectors.toList())));
File springFactories = new File(this.temp, "spring.factories");
try (FileWriter writer = new FileWriter(springFactories)) {