Skip to content

Commit 9e9049c

Browse files
committed
Add relative ordering to @autoConfiguration
The relative ordering is implemented with @AliasFor annotations on the @autoConfiguration annotation. The production code already works without changes, only the test code had to be modified. It now uses AnnotationMetadata which already knows how to deal with @AliasFor instead of using the reflection API directly. See gh-29907
1 parent 57cd34b commit 9e9049c

File tree

4 files changed

+133
-23
lines changed

4 files changed

+133
-23
lines changed

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

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@
2525
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2626
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2727
import org.springframework.boot.context.annotation.ImportCandidates;
28+
import org.springframework.context.annotation.AnnotationBeanNameGenerator;
29+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
2830
import org.springframework.context.annotation.Conditional;
2931
import org.springframework.context.annotation.Configuration;
32+
import org.springframework.core.annotation.AliasFor;
3033
import org.springframework.core.io.support.SpringFactoriesLoader;
3134

3235
/**
@@ -55,6 +58,52 @@
5558
@Retention(RetentionPolicy.RUNTIME)
5659
@Documented
5760
@Configuration(proxyBeanMethods = false)
61+
@AutoConfigureBefore
62+
@AutoConfigureAfter
5863
public @interface AutoConfiguration {
5964

65+
/**
66+
* Explicitly specify the name of the Spring bean definition associated with the
67+
* {@code @AutoConfiguration} class. If left unspecified (the common case), a bean
68+
* name will be automatically generated.
69+
* <p>
70+
* The custom name applies only if the {@code @AutoConfiguration} class is picked up
71+
* via component scanning or supplied directly to an
72+
* {@link AnnotationConfigApplicationContext}. If the {@code @AutoConfiguration} class
73+
* is registered as a traditional XML bean definition, the name/id of the bean element
74+
* will take precedence.
75+
* @return the explicit component name, if any (or empty String otherwise)
76+
* @see AnnotationBeanNameGenerator
77+
*/
78+
@AliasFor(annotation = Configuration.class)
79+
String value() default "";
80+
81+
/**
82+
* The auto-configure classes that should have not yet been applied.
83+
* @return the classes
84+
*/
85+
@AliasFor(annotation = AutoConfigureBefore.class, attribute = "value")
86+
Class<?>[] before() default {};
87+
88+
/**
89+
* The names of the auto-configure classes that should have not yet been applied.
90+
* @return the class names
91+
*/
92+
@AliasFor(annotation = AutoConfigureBefore.class, attribute = "name")
93+
String[] beforeName() default {};
94+
95+
/**
96+
* The auto-configure classes that should have already been applied.
97+
* @return the classes
98+
*/
99+
@AliasFor(annotation = AutoConfigureAfter.class, attribute = "value")
100+
Class<?>[] after() default {};
101+
102+
/**
103+
* The names of the auto-configure classes that should have already been applied.
104+
* @return the class names
105+
*/
106+
@AliasFor(annotation = AutoConfigureAfter.class, attribute = "name")
107+
String[] afterName() default {};
108+
60109
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/AutoConfigurationSorterTests.java

Lines changed: 82 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,13 +21,15 @@
2121
import java.util.Collections;
2222
import java.util.LinkedHashSet;
2323
import java.util.List;
24+
import java.util.Map;
2425
import java.util.Properties;
2526
import java.util.Set;
2627

2728
import org.junit.jupiter.api.BeforeEach;
2829
import org.junit.jupiter.api.Test;
2930

3031
import org.springframework.core.Ordered;
32+
import org.springframework.core.type.AnnotationMetadata;
3133
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
3234
import org.springframework.core.type.classreading.MetadataReader;
3335
import org.springframework.core.type.classreading.MetadataReaderFactory;
@@ -43,6 +45,7 @@
4345
*
4446
* @author Phillip Webb
4547
* @author Andy Wilkinson
48+
* @author Moritz Halbritter
4649
*/
4750
class AutoConfigurationSorterTests {
4851

@@ -54,8 +57,14 @@ class AutoConfigurationSorterTests {
5457

5558
private static final String A = AutoConfigureA.class.getName();
5659

60+
private static final String A2 = AutoConfigureA2.class.getName();
61+
62+
private static final String A3 = AutoConfigureA3.class.getName();
63+
5764
private static final String B = AutoConfigureB.class.getName();
5865

66+
private static final String B2 = AutoConfigureB2.class.getName();
67+
5968
private static final String C = AutoConfigureC.class.getName();
6069

6170
private static final String D = AutoConfigureD.class.getName();
@@ -64,15 +73,17 @@ class AutoConfigurationSorterTests {
6473

6574
private static final String W = AutoConfigureW.class.getName();
6675

76+
private static final String W2 = AutoConfigureW2.class.getName();
77+
6778
private static final String X = AutoConfigureX.class.getName();
6879

6980
private static final String Y = AutoConfigureY.class.getName();
7081

82+
private static final String Y2 = AutoConfigureY2.class.getName();
83+
7184
private static final String Z = AutoConfigureZ.class.getName();
7285

73-
private static final String A2 = AutoConfigureA2.class.getName();
74-
75-
private static final String W2 = AutoConfigureW2.class.getName();
86+
private static final String Z2 = AutoConfigureZ2.class.getName();
7687

7788
private AutoConfigurationSorter sorter;
7889

@@ -95,12 +106,24 @@ void byAutoConfigureAfter() {
95106
assertThat(actual).containsExactly(C, B, A);
96107
}
97108

109+
@Test
110+
void byAutoConfigureAfterAliasFor() {
111+
List<String> actual = this.sorter.getInPriorityOrder(Arrays.asList(A3, B2, C));
112+
assertThat(actual).containsExactly(C, B2, A3);
113+
}
114+
98115
@Test
99116
void byAutoConfigureBefore() {
100117
List<String> actual = this.sorter.getInPriorityOrder(Arrays.asList(X, Y, Z));
101118
assertThat(actual).containsExactly(Z, Y, X);
102119
}
103120

121+
@Test
122+
void byAutoConfigureBeforeAliasFor() {
123+
List<String> actual = this.sorter.getInPriorityOrder(Arrays.asList(X, Y2, Z2));
124+
assertThat(actual).containsExactly(Z2, Y2, X);
125+
}
126+
104127
@Test
105128
void byAutoConfigureAfterDoubles() {
106129
List<String> actual = this.sorter.getInPriorityOrder(Arrays.asList(A, B, C, E));
@@ -170,29 +193,47 @@ private AutoConfigurationMetadata getAutoConfigurationMetadata(String... classNa
170193
for (String className : classNames) {
171194
Class<?> type = ClassUtils.forName(className, null);
172195
properties.put(type.getName(), "");
173-
AutoConfigureOrder order = type.getDeclaredAnnotation(AutoConfigureOrder.class);
196+
AnnotationMetadata annotationMetadata = AnnotationMetadata.introspect(type);
197+
addAutoConfigureOrder(properties, className, annotationMetadata);
198+
addAutoConfigureBefore(properties, className, annotationMetadata);
199+
addAutoConfigureAfter(properties, className, annotationMetadata);
200+
}
201+
return AutoConfigurationMetadataLoader.loadMetadata(properties);
202+
}
203+
204+
private void addAutoConfigureAfter(Properties properties, String className, AnnotationMetadata annotationMetadata) {
205+
Map<String, Object> autoConfigureAfter = annotationMetadata
206+
.getAnnotationAttributes(AutoConfigureAfter.class.getName(), true);
207+
if (autoConfigureAfter != null) {
208+
properties.put(className + ".AutoConfigureAfter",
209+
merge((String[]) autoConfigureAfter.get("value"), (String[]) autoConfigureAfter.get("name")));
210+
}
211+
}
212+
213+
private void addAutoConfigureBefore(Properties properties, String className,
214+
AnnotationMetadata annotationMetadata) {
215+
Map<String, Object> autoConfigureBefore = annotationMetadata
216+
.getAnnotationAttributes(AutoConfigureBefore.class.getName(), true);
217+
if (autoConfigureBefore != null) {
218+
properties.put(className + ".AutoConfigureBefore",
219+
merge((String[]) autoConfigureBefore.get("value"), (String[]) autoConfigureBefore.get("name")));
220+
}
221+
}
222+
223+
private void addAutoConfigureOrder(Properties properties, String className, AnnotationMetadata annotationMetadata) {
224+
Map<String, Object> autoConfigureOrder = annotationMetadata
225+
.getAnnotationAttributes(AutoConfigureOrder.class.getName());
226+
if (autoConfigureOrder != null) {
227+
Integer order = (Integer) autoConfigureOrder.get("order");
174228
if (order != null) {
175-
properties.put(className + ".AutoConfigureOrder", String.valueOf(order.value()));
176-
}
177-
AutoConfigureBefore autoConfigureBefore = type.getDeclaredAnnotation(AutoConfigureBefore.class);
178-
if (autoConfigureBefore != null) {
179-
properties.put(className + ".AutoConfigureBefore",
180-
merge(autoConfigureBefore.value(), autoConfigureBefore.name()));
181-
}
182-
AutoConfigureAfter autoConfigureAfter = type.getDeclaredAnnotation(AutoConfigureAfter.class);
183-
if (autoConfigureAfter != null) {
184-
properties.put(className + ".AutoConfigureAfter",
185-
merge(autoConfigureAfter.value(), autoConfigureAfter.name()));
229+
properties.put(className + ".AutoConfigureOrder", String.valueOf(order));
186230
}
187231
}
188-
return AutoConfigurationMetadataLoader.loadMetadata(properties);
189232
}
190233

191-
private String merge(Class<?>[] value, String[] name) {
234+
private String merge(String[] value, String[] name) {
192235
Set<String> items = new LinkedHashSet<>();
193-
for (Class<?> type : value) {
194-
items.add(type.getName());
195-
}
236+
Collections.addAll(items, value);
196237
Collections.addAll(items, name);
197238
return StringUtils.collectionToCommaDelimitedString(items);
198239
}
@@ -222,11 +263,21 @@ static class AutoConfigureA2 {
222263

223264
}
224265

266+
@AutoConfiguration(after = AutoConfigureB2.class)
267+
static class AutoConfigureA3 {
268+
269+
}
270+
225271
@AutoConfigureAfter({ AutoConfigureC.class, AutoConfigureD.class, AutoConfigureE.class })
226272
static class AutoConfigureB {
227273

228274
}
229275

276+
@AutoConfiguration(after = { AutoConfigureC.class, AutoConfigureD.class, AutoConfigureE.class })
277+
static class AutoConfigureB2 {
278+
279+
}
280+
230281
static class AutoConfigureC {
231282

232283
}
@@ -259,11 +310,21 @@ static class AutoConfigureY {
259310

260311
}
261312

313+
@AutoConfiguration(before = AutoConfigureX.class)
314+
static class AutoConfigureY2 {
315+
316+
}
317+
262318
@AutoConfigureBefore(AutoConfigureY.class)
263319
static class AutoConfigureZ {
264320

265321
}
266322

323+
@AutoConfiguration(before = AutoConfigureY2.class)
324+
static class AutoConfigureZ2 {
325+
326+
}
327+
267328
static class SkipCycleMetadataReaderFactory extends CachingMetadataReaderFactory {
268329

269330
@Override

0 commit comments

Comments
 (0)