Skip to content

Commit dafd511

Browse files
committed
Support DatabaseInitializerDetector ordering
Update `DatabaseInitializationDependencyConfigurer` so that depends-on ordering is applied based on the `DatabaseInitializerDetector` order. Prior to this commit, if multiple DatabaseInitializer beans were detected the order in which they were initialized was not defined. See gh-26692
1 parent fba5ffc commit dafd511

File tree

4 files changed

+70
-6
lines changed

4 files changed

+70
-6
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/flyway/FlywayMigrationInitializerDatabaseInitializerDetector.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,9 @@ protected Set<Class<?>> getDatabaseInitializerBeanTypes() {
3434
return Collections.singleton(FlywayMigrationInitializer.class);
3535
}
3636

37+
@Override
38+
public int getOrder() {
39+
return 1;
40+
}
41+
3742
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/sql/init/dependency/DatabaseInitializationDependencyConfigurer.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,22 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
9898
if (initializerBeanNames.isEmpty()) {
9999
return;
100100
}
101+
String previousInitializerBeanName = null;
102+
for (String initializerBeanName : initializerBeanNames) {
103+
BeanDefinition beanDefinition = getBeanDefinition(initializerBeanName, beanFactory);
104+
beanDefinition.setDependsOn(merge(beanDefinition.getDependsOn(), previousInitializerBeanName));
105+
previousInitializerBeanName = initializerBeanName;
106+
}
101107
for (String dependsOnInitializationBeanNames : detectDependsOnInitializationBeanNames(beanFactory)) {
102-
BeanDefinition definition = getBeanDefinition(dependsOnInitializationBeanNames, beanFactory);
103-
definition.setDependsOn(merge(definition.getDependsOn(), initializerBeanNames));
108+
BeanDefinition beanDefinition = getBeanDefinition(dependsOnInitializationBeanNames, beanFactory);
109+
beanDefinition.setDependsOn(merge(beanDefinition.getDependsOn(), initializerBeanNames));
104110
}
105111
}
106112

113+
private String[] merge(String[] source, String additional) {
114+
return merge(source, (additional != null) ? Collections.singleton(additional) : Collections.emptySet());
115+
}
116+
107117
private String[] merge(String[] source, Set<String> additional) {
108118
Set<String> result = new LinkedHashSet<>((source != null) ? Arrays.asList(source) : Collections.emptySet());
109119
result.addAll(additional);
@@ -112,7 +122,7 @@ private String[] merge(String[] source, Set<String> additional) {
112122

113123
private Set<String> detectInitializerBeanNames(ConfigurableListableBeanFactory beanFactory) {
114124
List<DatabaseInitializerDetector> detectors = getDetectors(beanFactory, DatabaseInitializerDetector.class);
115-
Set<String> beanNames = new HashSet<>();
125+
Set<String> beanNames = new LinkedHashSet<>();
116126
for (DatabaseInitializerDetector detector : detectors) {
117127
for (String beanName : detector.detect(beanFactory)) {
118128
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/sql/init/dependency/DatabaseInitializerDetector.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import javax.sql.DataSource;
2222

2323
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
24+
import org.springframework.core.Ordered;
2425

2526
/**
2627
* Detects beans that initialize an SQL database. Implementations should be registered in
@@ -30,7 +31,7 @@
3031
* @author Andy Wilkinson
3132
* @since 2.5.0
3233
*/
33-
public interface DatabaseInitializerDetector {
34+
public interface DatabaseInitializerDetector extends Ordered {
3435

3536
/**
3637
* Detect beans defined in the given {@code beanFactory} that initialize a
@@ -52,4 +53,9 @@ default void detectionComplete(ConfigurableListableBeanFactory beanFactory,
5253
Set<String> dataSourceInitializerNames) {
5354
}
5455

56+
@Override
57+
default int getOrder() {
58+
return 0;
59+
}
60+
5561
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/sql/init/dependency/DatabaseInitializationDependencyConfigurerTests.java

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
4141
import org.springframework.context.annotation.Configuration;
4242
import org.springframework.context.annotation.Import;
43+
import org.springframework.core.Ordered;
4344
import org.springframework.core.env.ConfigurableEnvironment;
4445
import org.springframework.core.env.Environment;
4546
import org.springframework.mock.env.MockEnvironment;
@@ -54,6 +55,7 @@
5455
* Tests for {@link DatabaseInitializationDependencyConfigurer}.
5556
*
5657
* @author Andy Wilkinson
58+
* @author Phillip Webb
5759
*/
5860
class DatabaseInitializationDependencyConfigurerTests {
5961

@@ -64,7 +66,8 @@ class DatabaseInitializationDependencyConfigurerTests {
6466

6567
@BeforeEach
6668
void resetMocks() {
67-
reset(MockDatabaseInitializerDetector.instance, MockedDependsOnDatabaseInitializationDetector.instance);
69+
reset(MockDatabaseInitializerDetector.instance, OrderedMockDatabaseInitializerDetector.instance,
70+
MockedDependsOnDatabaseInitializationDetector.instance);
6871
}
6972

7073
@Test
@@ -103,6 +106,30 @@ void whenDependenciesAreConfiguredThenBeansThatDependUponDatabaseInitializationD
103106
});
104107
}
105108

109+
@Test
110+
void whenDependenciesAreConfiguredDetectedDatabaseInitializersAreInitializedInCorrectOrder() {
111+
BeanDefinition alpha = BeanDefinitionBuilder.genericBeanDefinition(String.class).getBeanDefinition();
112+
BeanDefinition bravo = BeanDefinitionBuilder.genericBeanDefinition(String.class).getBeanDefinition();
113+
BeanDefinition charlie = BeanDefinitionBuilder.genericBeanDefinition(String.class).getBeanDefinition();
114+
performDetection(Arrays.asList(MockDatabaseInitializerDetector.class,
115+
OrderedMockDatabaseInitializerDetector.class, MockedDependsOnDatabaseInitializationDetector.class),
116+
(context) -> {
117+
given(MockDatabaseInitializerDetector.instance.detect(context.getBeanFactory()))
118+
.willReturn(Collections.singleton("alpha"));
119+
given(OrderedMockDatabaseInitializerDetector.instance.detect(context.getBeanFactory()))
120+
.willReturn(Collections.singleton("bravo"));
121+
given(MockedDependsOnDatabaseInitializationDetector.instance.detect(context.getBeanFactory()))
122+
.willReturn(Collections.singleton("charlie"));
123+
context.registerBeanDefinition("alpha", alpha);
124+
context.registerBeanDefinition("bravo", bravo);
125+
context.registerBeanDefinition("charlie", charlie);
126+
context.refresh();
127+
assertThat(charlie.getDependsOn()).containsExactly("alpha", "bravo");
128+
assertThat(bravo.getDependsOn()).containsExactly("alpha");
129+
assertThat(alpha.getDependsOn()).isNullOrEmpty();
130+
});
131+
}
132+
106133
private void performDetection(Collection<Class<?>> detectors,
107134
Consumer<AnnotationConfigApplicationContext> contextCallback) {
108135
DetectorSpringFactoriesClassLoader detectorSpringFactories = new DetectorSpringFactoriesClassLoader(this.temp);
@@ -158,7 +185,7 @@ static class MockDatabaseInitializerDetector implements DatabaseInitializerDetec
158185

159186
@Override
160187
public Set<String> detect(ConfigurableListableBeanFactory beanFactory) {
161-
return MockDatabaseInitializerDetector.instance.detect(beanFactory);
188+
return instance.detect(beanFactory);
162189
}
163190

164191
@Override
@@ -169,6 +196,22 @@ public void detectionComplete(ConfigurableListableBeanFactory beanFactory,
169196

170197
}
171198

199+
static class OrderedMockDatabaseInitializerDetector implements DatabaseInitializerDetector {
200+
201+
private static DatabaseInitializerDetector instance = mock(DatabaseInitializerDetector.class);
202+
203+
@Override
204+
public Set<String> detect(ConfigurableListableBeanFactory beanFactory) {
205+
return instance.detect(beanFactory);
206+
}
207+
208+
@Override
209+
public int getOrder() {
210+
return Ordered.LOWEST_PRECEDENCE;
211+
}
212+
213+
}
214+
172215
static class MockedDependsOnDatabaseInitializationDetector implements DependsOnDatabaseInitializationDetector {
173216

174217
private static DependsOnDatabaseInitializationDetector instance = mock(

0 commit comments

Comments
 (0)