Support for @Order on nested configuration classes

Issue: SPR-15384
This commit is contained in:
Juergen Hoeller 2017-04-04 17:17:03 +02:00
parent e9627a10c7
commit 917207b7ae
3 changed files with 79 additions and 9 deletions

View File

@ -50,6 +50,8 @@ import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator; import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase; import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase;
import org.springframework.core.NestedIOException; import org.springframework.core.NestedIOException;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.CompositePropertySource;
@ -330,16 +332,24 @@ class ConfigurationClassParser {
* Register member (nested) classes that happen to be configuration classes themselves. * Register member (nested) classes that happen to be configuration classes themselves.
*/ */
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
for (SourceClass memberClass : sourceClass.getMemberClasses()) { Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) && if (!memberClasses.isEmpty()) {
!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) { List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
for (SourceClass memberClass : memberClasses) {
if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
candidates.add(memberClass);
}
}
OrderComparator.sort(candidates);
for (SourceClass candidate : candidates) {
if (this.importStack.contains(configClass)) { if (this.importStack.contains(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack)); this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
} }
else { else {
this.importStack.push(configClass); this.importStack.push(configClass);
try { try {
processConfigurationClass(memberClass.asConfigClass(configClass)); processConfigurationClass(candidate.asConfigClass(configClass));
} }
finally { finally {
this.importStack.pop(); this.importStack.pop();
@ -747,7 +757,7 @@ class ConfigurationClassParser {
* Simple wrapper that allows annotated source classes to be dealt with * Simple wrapper that allows annotated source classes to be dealt with
* in a uniform manner, regardless of how they are loaded. * in a uniform manner, regardless of how they are loaded.
*/ */
private class SourceClass { private class SourceClass implements Ordered {
private final Object source; // Class or MetadataReader private final Object source; // Class or MetadataReader
@ -767,6 +777,12 @@ class ConfigurationClassParser {
return this.metadata; return this.metadata;
} }
@Override
public int getOrder() {
Integer order = ConfigurationClassUtils.getOrder(this.metadata);
return (order != null ? order : Ordered.LOWEST_PRECEDENCE);
}
public Class<?> loadClass() throws ClassNotFoundException { public Class<?> loadClass() throws ClassNotFoundException {
if (this.source instanceof Class) { if (this.source instanceof Class) {
return (Class<?>) this.source; return (Class<?>) this.source;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2016 the original author or authors. * Copyright 2002-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -119,9 +119,9 @@ abstract class ConfigurationClassUtils {
} }
// It's a full or lite configuration candidate... Let's determine the order value, if any. // It's a full or lite configuration candidate... Let's determine the order value, if any.
Map<String, Object> orderAttributes = metadata.getAnnotationAttributes(Order.class.getName()); Integer order = getOrder(metadata);
if (orderAttributes != null) { if (order != null) {
beanDef.setAttribute(ORDER_ATTRIBUTE, orderAttributes.get(AnnotationUtils.VALUE)); beanDef.setAttribute(ORDER_ATTRIBUTE, order);
} }
return true; return true;
@ -198,6 +198,18 @@ abstract class ConfigurationClassUtils {
return CONFIGURATION_CLASS_LITE.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE)); return CONFIGURATION_CLASS_LITE.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
} }
/**
* Determine the order for the given configuration class metadata.
* @param metadata the metadata of the annotated class
* @return the {@link @Order} annotation value on the configuration class,
* or {@link Ordered#LOWEST_PRECEDENCE} if none declared
* @since 5.0
*/
public static Integer getOrder(AnnotationMetadata metadata) {
Map<String, Object> orderAttributes = metadata.getAnnotationAttributes(Order.class.getName());
return (orderAttributes != null ? ((Integer) orderAttributes.get(AnnotationUtils.VALUE)) : null);
}
/** /**
* Determine the order for the given configuration class bean definition, * Determine the order for the given configuration class bean definition,
* as set by {@link #checkConfigurationClassCandidate}. * as set by {@link #checkConfigurationClassCandidate}.

View File

@ -348,6 +348,18 @@ public class ConfigurationClassPostProcessorTests {
} }
} }
@Test
public void nestedConfigurationClassesProcessedInCorrectOrder() {
beanFactory.registerBeanDefinition("config", new RootBeanDefinition(ConfigWithOrderedNestedClasses.class));
ConfigurationClassPostProcessor pp = new ConfigurationClassPostProcessor();
pp.postProcessBeanFactory(beanFactory);
Foo foo = beanFactory.getBean(Foo.class);
assertTrue(foo instanceof ExtendedFoo);
Bar bar = beanFactory.getBean(Bar.class);
assertSame(foo, bar.foo);
}
@Test @Test
public void scopedProxyTargetMarkedAsNonAutowireCandidate() { public void scopedProxyTargetMarkedAsNonAutowireCandidate() {
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
@ -833,6 +845,36 @@ public class ConfigurationClassPostProcessorTests {
} }
} }
@Configuration
static class ConfigWithOrderedNestedClasses {
@Configuration
@Order(1)
static class SingletonBeanConfig {
public @Bean Foo foo() {
return new Foo();
}
public @Bean Bar bar() {
return new Bar(foo());
}
}
@Configuration
@Order(2)
static class OverridingSingletonBeanConfig {
public @Bean ExtendedFoo foo() {
return new ExtendedFoo();
}
public @Bean Bar bar() {
return new Bar(foo());
}
}
}
static class Foo { static class Foo {
} }