Allow BFPP to modify database initialization ordering

Fixes gh-26899
pull/26981/head
Andy Wilkinson 3 years ago
parent a2772e288a
commit 5d20edc666

@ -34,6 +34,7 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.util.Instantiator;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.Ordered;
import org.springframework.core.env.Environment;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.type.AnnotationMetadata;
@ -84,7 +85,7 @@ public class DatabaseInitializationDependencyConfigurer implements ImportBeanDef
* {@link BeanFactoryPostProcessor} used to configure database initialization
* dependency relationships.
*/
static class DependsOnDatabaseInitializationPostProcessor implements BeanFactoryPostProcessor {
static class DependsOnDatabaseInitializationPostProcessor implements BeanFactoryPostProcessor, Ordered {
private final Environment environment;
@ -92,6 +93,11 @@ public class DatabaseInitializationDependencyConfigurer implements ImportBeanDef
this.environment = environment;
}
@Override
public int getOrder() {
return 0;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
Set<String> initializerBeanNames = detectInitializerBeanNames(beanFactory);

@ -24,7 +24,10 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.function.Consumer;
@ -35,9 +38,11 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
@ -70,6 +75,30 @@ class DatabaseInitializationDependencyConfigurerTests {
MockedDependsOnDatabaseInitializationDetector.instance);
}
@Test
void beanFactoryPostProcessorHasOrderAllowingSubsequentPostProcessorsToFineTuneDependencies() {
performDetection(Arrays.asList(MockDatabaseInitializerDetector.class,
MockedDependsOnDatabaseInitializationDetector.class), (context) -> {
BeanDefinition alpha = BeanDefinitionBuilder.genericBeanDefinition(String.class)
.getBeanDefinition();
BeanDefinition bravo = BeanDefinitionBuilder.genericBeanDefinition(String.class)
.getBeanDefinition();
context.register(DependsOnCaptor.class);
context.register(DependencyConfigurerConfiguration.class);
context.registerBeanDefinition("alpha", alpha);
context.registerBeanDefinition("bravo", bravo);
given(MockDatabaseInitializerDetector.instance.detect(context.getBeanFactory()))
.willReturn(Collections.singleton("alpha"));
given(MockedDependsOnDatabaseInitializationDetector.instance.detect(context.getBeanFactory()))
.willReturn(Collections.singleton("bravo"));
context.refresh();
assertThat(DependsOnCaptor.dependsOn).hasEntrySatisfying("bravo",
(dependencies) -> assertThat(dependencies).containsExactly("alpha"));
assertThat(DependsOnCaptor.dependsOn).hasEntrySatisfying("alpha",
(dependencies) -> assertThat(dependencies).isEmpty());
});
}
@Test
void whenDetectorsAreCreatedThenTheEnvironmentCanBeInjected() {
performDetection(Arrays.asList(ConstructorInjectionDatabaseInitializerDetector.class,
@ -77,6 +106,7 @@ class DatabaseInitializationDependencyConfigurerTests {
BeanDefinition alpha = BeanDefinitionBuilder.genericBeanDefinition(String.class)
.getBeanDefinition();
context.registerBeanDefinition("alpha", alpha);
context.register(DependencyConfigurerConfiguration.class);
context.refresh();
assertThat(ConstructorInjectionDatabaseInitializerDetector.environment).isEqualTo(this.environment);
assertThat(ConstructorInjectionDependsOnDatabaseInitializationDetector.environment)
@ -96,6 +126,7 @@ class DatabaseInitializationDependencyConfigurerTests {
.willReturn(Collections.singleton("alpha"));
given(MockedDependsOnDatabaseInitializationDetector.instance.detect(context.getBeanFactory()))
.willReturn(Collections.singleton("bravo"));
context.register(DependencyConfigurerConfiguration.class);
context.refresh();
assertThat(alpha.getAttribute(DatabaseInitializerDetector.class.getName()))
.isEqualTo(MockDatabaseInitializerDetector.class.getName());
@ -123,6 +154,7 @@ class DatabaseInitializationDependencyConfigurerTests {
context.registerBeanDefinition("alpha", alpha);
context.registerBeanDefinition("bravo", bravo);
context.registerBeanDefinition("charlie", charlie);
context.register(DependencyConfigurerConfiguration.class);
context.refresh();
assertThat(charlie.getDependsOn()).containsExactly("alpha", "bravo");
assertThat(bravo.getDependsOn()).containsExactly("alpha");
@ -137,7 +169,6 @@ class DatabaseInitializationDependencyConfigurerTests {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
context.setEnvironment(this.environment);
context.setClassLoader(detectorSpringFactories);
context.register(DependencyConfigurerConfiguration.class);
contextCallback.accept(context);
}
}
@ -270,4 +301,28 @@ class DatabaseInitializationDependencyConfigurerTests {
}
@Configuration(proxyBeanMethods = false)
static class DependsOnCaptor {
static final Map<String, List<String>> dependsOn = new HashMap<>();
@Bean
static BeanFactoryPostProcessor dependsOnCapturingPostProcessor() {
return (beanFactory) -> {
dependsOn.clear();
for (String name : beanFactory.getBeanDefinitionNames()) {
storeDependsOn(name, beanFactory);
}
};
}
private static void storeDependsOn(String name, ConfigurableListableBeanFactory beanFactory) {
String[] dependsOn = beanFactory.getBeanDefinition(name).getDependsOn();
if (dependsOn != null) {
DependsOnCaptor.dependsOn.put(name, Arrays.asList(dependsOn));
}
}
}
}

Loading…
Cancel
Save