Skip to content

Commit 917207b

Browse files
committed
Support for @order on nested configuration classes
Issue: SPR-15384
1 parent e9627a1 commit 917207b

File tree

3 files changed

+79
-9
lines changed

3 files changed

+79
-9
lines changed

spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050
import org.springframework.beans.factory.support.BeanNameGenerator;
5151
import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase;
5252
import org.springframework.core.NestedIOException;
53+
import org.springframework.core.OrderComparator;
54+
import org.springframework.core.Ordered;
5355
import org.springframework.core.annotation.AnnotationAttributes;
5456
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
5557
import org.springframework.core.env.CompositePropertySource;
@@ -330,16 +332,24 @@ protected final SourceClass doProcessConfigurationClass(ConfigurationClass confi
330332
* Register member (nested) classes that happen to be configuration classes themselves.
331333
*/
332334
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
333-
for (SourceClass memberClass : sourceClass.getMemberClasses()) {
334-
if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
335-
!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
335+
Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
336+
if (!memberClasses.isEmpty()) {
337+
List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
338+
for (SourceClass memberClass : memberClasses) {
339+
if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
340+
!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
341+
candidates.add(memberClass);
342+
}
343+
}
344+
OrderComparator.sort(candidates);
345+
for (SourceClass candidate : candidates) {
336346
if (this.importStack.contains(configClass)) {
337347
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
338348
}
339349
else {
340350
this.importStack.push(configClass);
341351
try {
342-
processConfigurationClass(memberClass.asConfigClass(configClass));
352+
processConfigurationClass(candidate.asConfigClass(configClass));
343353
}
344354
finally {
345355
this.importStack.pop();
@@ -747,7 +757,7 @@ public DeferredImportSelector getImportSelector() {
747757
* Simple wrapper that allows annotated source classes to be dealt with
748758
* in a uniform manner, regardless of how they are loaded.
749759
*/
750-
private class SourceClass {
760+
private class SourceClass implements Ordered {
751761

752762
private final Object source; // Class or MetadataReader
753763

@@ -767,6 +777,12 @@ public final AnnotationMetadata getMetadata() {
767777
return this.metadata;
768778
}
769779

780+
@Override
781+
public int getOrder() {
782+
Integer order = ConfigurationClassUtils.getOrder(this.metadata);
783+
return (order != null ? order : Ordered.LOWEST_PRECEDENCE);
784+
}
785+
770786
public Class<?> loadClass() throws ClassNotFoundException {
771787
if (this.source instanceof Class) {
772788
return (Class<?>) this.source;

spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassUtils.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2017 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.
@@ -119,9 +119,9 @@ else if (isLiteConfigurationCandidate(metadata)) {
119119
}
120120

121121
// It's a full or lite configuration candidate... Let's determine the order value, if any.
122-
Map<String, Object> orderAttributes = metadata.getAnnotationAttributes(Order.class.getName());
123-
if (orderAttributes != null) {
124-
beanDef.setAttribute(ORDER_ATTRIBUTE, orderAttributes.get(AnnotationUtils.VALUE));
122+
Integer order = getOrder(metadata);
123+
if (order != null) {
124+
beanDef.setAttribute(ORDER_ATTRIBUTE, order);
125125
}
126126

127127
return true;
@@ -198,6 +198,18 @@ public static boolean isLiteConfigurationClass(BeanDefinition beanDef) {
198198
return CONFIGURATION_CLASS_LITE.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
199199
}
200200

201+
/**
202+
* Determine the order for the given configuration class metadata.
203+
* @param metadata the metadata of the annotated class
204+
* @return the {@link @Order} annotation value on the configuration class,
205+
* or {@link Ordered#LOWEST_PRECEDENCE} if none declared
206+
* @since 5.0
207+
*/
208+
public static Integer getOrder(AnnotationMetadata metadata) {
209+
Map<String, Object> orderAttributes = metadata.getAnnotationAttributes(Order.class.getName());
210+
return (orderAttributes != null ? ((Integer) orderAttributes.get(AnnotationUtils.VALUE)) : null);
211+
}
212+
201213
/**
202214
* Determine the order for the given configuration class bean definition,
203215
* as set by {@link #checkConfigurationClassCandidate}.

spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,18 @@ public void configurationClassesWithInvalidOverridingForProgrammaticCall() {
348348
}
349349
}
350350

351+
@Test
352+
public void nestedConfigurationClassesProcessedInCorrectOrder() {
353+
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(ConfigWithOrderedNestedClasses.class));
354+
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
355+
pp.postProcessBeanFactory(beanFactory);
356+
357+
Foo foo = beanFactory.getBean(Foo.class);
358+
assertTrue(foo instanceof ExtendedFoo);
359+
Bar bar = beanFactory.getBean(Bar.class);
360+
assertSame(foo, bar.foo);
361+
}
362+
351363
@Test
352364
public void scopedProxyTargetMarkedAsNonAutowireCandidate() {
353365
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
@@ -833,6 +845,36 @@ static class InvalidOverridingSingletonBeanConfig {
833845
}
834846
}
835847

848+
@Configuration
849+
static class ConfigWithOrderedNestedClasses {
850+
851+
@Configuration
852+
@Order(1)
853+
static class SingletonBeanConfig {
854+
855+
public @Bean Foo foo() {
856+
return new Foo();
857+
}
858+
859+
public @Bean Bar bar() {
860+
return new Bar(foo());
861+
}
862+
}
863+
864+
@Configuration
865+
@Order(2)
866+
static class OverridingSingletonBeanConfig {
867+
868+
public @Bean ExtendedFoo foo() {
869+
return new ExtendedFoo();
870+
}
871+
872+
public @Bean Bar bar() {
873+
return new Bar(foo());
874+
}
875+
}
876+
}
877+
836878
static class Foo {
837879
}
838880

0 commit comments

Comments
 (0)