Merge branch '2.5.x'

Closes gh-27408
pull/27409/head
Andy Wilkinson 3 years ago
commit b5ef5a2d90

@ -20,8 +20,10 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactory;
@ -38,6 +40,7 @@ import org.springframework.core.Ordered;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
@ -100,48 +103,49 @@ public class DatabaseInitializationDependencyConfigurer implements ImportBeanDef
@Override @Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
Set<String> initializerBeanNames = detectInitializerBeanNames(beanFactory); InitializerBeanNames initializerBeanNames = detectInitializerBeanNames(beanFactory);
if (initializerBeanNames.isEmpty()) { if (initializerBeanNames.isEmpty()) {
return; return;
} }
String previousInitializerBeanName = null; Set<String> previousInitializerBeanNamesBatch = null;
for (String initializerBeanName : initializerBeanNames) { for (Set<String> initializerBeanNamesBatch : initializerBeanNames.batchedBeanNames()) {
BeanDefinition beanDefinition = getBeanDefinition(initializerBeanName, beanFactory); for (String initializerBeanName : initializerBeanNamesBatch) {
beanDefinition.setDependsOn(merge(beanDefinition.getDependsOn(), previousInitializerBeanName)); BeanDefinition beanDefinition = getBeanDefinition(initializerBeanName, beanFactory);
previousInitializerBeanName = initializerBeanName; beanDefinition
.setDependsOn(merge(beanDefinition.getDependsOn(), previousInitializerBeanNamesBatch));
}
previousInitializerBeanNamesBatch = initializerBeanNamesBatch;
} }
for (String dependsOnInitializationBeanNames : detectDependsOnInitializationBeanNames(beanFactory)) { for (String dependsOnInitializationBeanNames : detectDependsOnInitializationBeanNames(beanFactory)) {
BeanDefinition beanDefinition = getBeanDefinition(dependsOnInitializationBeanNames, beanFactory); BeanDefinition beanDefinition = getBeanDefinition(dependsOnInitializationBeanNames, beanFactory);
beanDefinition.setDependsOn(merge(beanDefinition.getDependsOn(), initializerBeanNames)); beanDefinition.setDependsOn(merge(beanDefinition.getDependsOn(), initializerBeanNames.beanNames()));
} }
} }
private String[] merge(String[] source, String additional) {
return merge(source, (additional != null) ? Collections.singleton(additional) : Collections.emptySet());
}
private String[] merge(String[] source, Set<String> additional) { private String[] merge(String[] source, Set<String> additional) {
if (CollectionUtils.isEmpty(additional)) {
return source;
}
Set<String> result = new LinkedHashSet<>((source != null) ? Arrays.asList(source) : Collections.emptySet()); Set<String> result = new LinkedHashSet<>((source != null) ? Arrays.asList(source) : Collections.emptySet());
result.addAll(additional); result.addAll(additional);
return StringUtils.toStringArray(result); return StringUtils.toStringArray(result);
} }
private Set<String> detectInitializerBeanNames(ConfigurableListableBeanFactory beanFactory) { private InitializerBeanNames detectInitializerBeanNames(ConfigurableListableBeanFactory beanFactory) {
List<DatabaseInitializerDetector> detectors = getDetectors(beanFactory, DatabaseInitializerDetector.class); List<DatabaseInitializerDetector> detectors = getDetectors(beanFactory, DatabaseInitializerDetector.class);
Set<String> beanNames = new LinkedHashSet<>(); InitializerBeanNames initializerBeanNames = new InitializerBeanNames();
for (DatabaseInitializerDetector detector : detectors) { for (DatabaseInitializerDetector detector : detectors) {
for (String beanName : detector.detect(beanFactory)) { for (String beanName : detector.detect(beanFactory)) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
beanDefinition.setAttribute(DatabaseInitializerDetector.class.getName(), beanDefinition.setAttribute(DatabaseInitializerDetector.class.getName(),
detector.getClass().getName()); detector.getClass().getName());
beanNames.add(beanName); initializerBeanNames.detected(detector, beanName);
} }
} }
beanNames = Collections.unmodifiableSet(beanNames);
for (DatabaseInitializerDetector detector : detectors) { for (DatabaseInitializerDetector detector : detectors) {
detector.detectionComplete(beanFactory, beanNames); detector.detectionComplete(beanFactory, initializerBeanNames.beanNames());
} }
return beanNames; return initializerBeanNames;
} }
private Collection<String> detectDependsOnInitializationBeanNames(ConfigurableListableBeanFactory beanFactory) { private Collection<String> detectDependsOnInitializationBeanNames(ConfigurableListableBeanFactory beanFactory) {
@ -174,6 +178,31 @@ public class DatabaseInitializationDependencyConfigurer implements ImportBeanDef
} }
} }
static class InitializerBeanNames {
private final Map<DatabaseInitializerDetector, Set<String>> byDetectorBeanNames = new LinkedHashMap<>();
private final Set<String> beanNames = new LinkedHashSet<>();
private void detected(DatabaseInitializerDetector detector, String beanName) {
this.byDetectorBeanNames.computeIfAbsent(detector, (key) -> new LinkedHashSet<>()).add(beanName);
this.beanNames.add(beanName);
}
private boolean isEmpty() {
return this.beanNames.isEmpty();
}
private Iterable<Set<String>> batchedBeanNames() {
return this.byDetectorBeanNames.values();
}
private Set<String> beanNames() {
return Collections.unmodifiableSet(this.beanNames);
}
}
} }
} }

@ -26,6 +26,7 @@ import java.util.Collections;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
@ -71,7 +72,8 @@ class DatabaseInitializationDependencyConfigurerTests {
@BeforeEach @BeforeEach
void resetMocks() { void resetMocks() {
reset(MockDatabaseInitializerDetector.instance, OrderedMockDatabaseInitializerDetector.instance, reset(MockDatabaseInitializerDetector.instance, OrderedNearLowestMockDatabaseInitializerDetector.instance,
OrderedLowestMockDatabaseInitializerDetector.instance,
MockedDependsOnDatabaseInitializationDetector.instance); MockedDependsOnDatabaseInitializationDetector.instance);
} }
@ -94,8 +96,7 @@ class DatabaseInitializationDependencyConfigurerTests {
context.refresh(); context.refresh();
assertThat(DependsOnCaptor.dependsOn).hasEntrySatisfying("bravo", assertThat(DependsOnCaptor.dependsOn).hasEntrySatisfying("bravo",
(dependencies) -> assertThat(dependencies).containsExactly("alpha")); (dependencies) -> assertThat(dependencies).containsExactly("alpha"));
assertThat(DependsOnCaptor.dependsOn).hasEntrySatisfying("alpha", assertThat(DependsOnCaptor.dependsOn).doesNotContainKey("alpha");
(dependencies) -> assertThat(dependencies).isEmpty());
}); });
} }
@ -140,24 +141,34 @@ class DatabaseInitializationDependencyConfigurerTests {
@Test @Test
void whenDependenciesAreConfiguredDetectedDatabaseInitializersAreInitializedInCorrectOrder() { void whenDependenciesAreConfiguredDetectedDatabaseInitializersAreInitializedInCorrectOrder() {
BeanDefinition alpha = BeanDefinitionBuilder.genericBeanDefinition(String.class).getBeanDefinition(); BeanDefinition alpha = BeanDefinitionBuilder.genericBeanDefinition(String.class).getBeanDefinition();
BeanDefinition bravo = BeanDefinitionBuilder.genericBeanDefinition(String.class).getBeanDefinition(); BeanDefinition bravo1 = BeanDefinitionBuilder.genericBeanDefinition(String.class).getBeanDefinition();
BeanDefinition bravo2 = BeanDefinitionBuilder.genericBeanDefinition(String.class).getBeanDefinition();
BeanDefinition charlie = BeanDefinitionBuilder.genericBeanDefinition(String.class).getBeanDefinition(); BeanDefinition charlie = BeanDefinitionBuilder.genericBeanDefinition(String.class).getBeanDefinition();
performDetection(Arrays.asList(MockDatabaseInitializerDetector.class, BeanDefinition delta = BeanDefinitionBuilder.genericBeanDefinition(String.class).getBeanDefinition();
OrderedMockDatabaseInitializerDetector.class, MockedDependsOnDatabaseInitializationDetector.class), performDetection(
Arrays.asList(MockDatabaseInitializerDetector.class, OrderedLowestMockDatabaseInitializerDetector.class,
OrderedNearLowestMockDatabaseInitializerDetector.class,
MockedDependsOnDatabaseInitializationDetector.class),
(context) -> { (context) -> {
given(MockDatabaseInitializerDetector.instance.detect(context.getBeanFactory())) given(MockDatabaseInitializerDetector.instance.detect(context.getBeanFactory()))
.willReturn(Collections.singleton("alpha")); .willReturn(Collections.singleton("alpha"));
given(OrderedMockDatabaseInitializerDetector.instance.detect(context.getBeanFactory())) given(OrderedNearLowestMockDatabaseInitializerDetector.instance.detect(context.getBeanFactory()))
.willReturn(Collections.singleton("bravo")); .willReturn(new LinkedHashSet<>(Arrays.asList("bravo1", "bravo2")));
given(OrderedLowestMockDatabaseInitializerDetector.instance.detect(context.getBeanFactory()))
.willReturn(new LinkedHashSet<>(Arrays.asList("charlie")));
given(MockedDependsOnDatabaseInitializationDetector.instance.detect(context.getBeanFactory())) given(MockedDependsOnDatabaseInitializationDetector.instance.detect(context.getBeanFactory()))
.willReturn(Collections.singleton("charlie")); .willReturn(Collections.singleton("delta"));
context.registerBeanDefinition("alpha", alpha); context.registerBeanDefinition("alpha", alpha);
context.registerBeanDefinition("bravo", bravo); context.registerBeanDefinition("bravo1", bravo1);
context.registerBeanDefinition("bravo2", bravo2);
context.registerBeanDefinition("charlie", charlie); context.registerBeanDefinition("charlie", charlie);
context.registerBeanDefinition("delta", delta);
context.register(DependencyConfigurerConfiguration.class); context.register(DependencyConfigurerConfiguration.class);
context.refresh(); context.refresh();
assertThat(charlie.getDependsOn()).containsExactly("alpha", "bravo"); assertThat(delta.getDependsOn()).containsExactlyInAnyOrder("alpha", "bravo1", "bravo2", "charlie");
assertThat(bravo.getDependsOn()).containsExactly("alpha"); assertThat(charlie.getDependsOn()).containsExactly("bravo1", "bravo2");
assertThat(bravo1.getDependsOn()).containsExactly("alpha");
assertThat(bravo2.getDependsOn()).containsExactly("alpha");
assertThat(alpha.getDependsOn()).isNullOrEmpty(); assertThat(alpha.getDependsOn()).isNullOrEmpty();
}); });
} }
@ -227,7 +238,7 @@ class DatabaseInitializationDependencyConfigurerTests {
} }
static class OrderedMockDatabaseInitializerDetector implements DatabaseInitializerDetector { static class OrderedLowestMockDatabaseInitializerDetector implements DatabaseInitializerDetector {
private static DatabaseInitializerDetector instance = mock(DatabaseInitializerDetector.class); private static DatabaseInitializerDetector instance = mock(DatabaseInitializerDetector.class);
@ -243,6 +254,22 @@ class DatabaseInitializationDependencyConfigurerTests {
} }
static class OrderedNearLowestMockDatabaseInitializerDetector implements DatabaseInitializerDetector {
private static DatabaseInitializerDetector instance = mock(DatabaseInitializerDetector.class);
@Override
public Set<String> detect(ConfigurableListableBeanFactory beanFactory) {
return instance.detect(beanFactory);
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE - 100;
}
}
static class MockedDependsOnDatabaseInitializationDetector implements DependsOnDatabaseInitializationDetector { static class MockedDependsOnDatabaseInitializationDetector implements DependsOnDatabaseInitializationDetector {
private static DependsOnDatabaseInitializationDetector instance = mock( private static DependsOnDatabaseInitializationDetector instance = mock(

Loading…
Cancel
Save