diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java index 51242ad237..bc90578fdc 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java @@ -291,7 +291,7 @@ class ConfigurationClassParser { } // Process individual @Bean methods - Set beanMethods = sourceClass.getMetadata().getAnnotatedMethods(Bean.class.getName()); + Set beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } @@ -341,7 +341,7 @@ class ConfigurationClassParser { */ private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { for (SourceClass ifc : sourceClass.getInterfaces()) { - Set beanMethods = ifc.getMetadata().getAnnotatedMethods(Bean.class.getName()); + Set beanMethods = retrieveBeanMethodMetadata(ifc); for (MethodMetadata methodMetadata : beanMethods) { if (!methodMetadata.isAbstract()) { // A default method or other concrete method on a Java 8+ interface... @@ -352,6 +352,29 @@ class ConfigurationClassParser { } } + /** + * Retrieve the metadata for all @Bean methods. + */ + private Set retrieveBeanMethodMetadata(SourceClass sourceClass) { + AnnotationMetadata original = sourceClass.getMetadata(); + Set beanMethods = original.getAnnotatedMethods(Bean.class.getName()); + if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) { + // Try reading the class file via ASM for deterministic declaration order... + // Unfortunately, the JVM's standard reflection returns methods in arbitrary + // order, even between different runs of the same application on the same JVM. + try { + AnnotationMetadata asm = + this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata(); + beanMethods = asm.getAnnotatedMethods(Bean.class.getName()); + } + catch (IOException ex) { + logger.debug("Failed to read class file via ASM for determining @Bean method order", ex); + // No worries, let's continue with the reflection metadata we started with... + } + } + return beanMethods; + } + /** * Process the given @PropertySource annotation metadata. * @param propertySource metadata for the @PropertySource annotation found @@ -588,7 +611,7 @@ class ConfigurationClassParser { if (metadata instanceof StandardAnnotationMetadata) { return asSourceClass(((StandardAnnotationMetadata) metadata).getIntrospectedClass()); } - return asSourceClass(configurationClass.getMetadata().getClassName()); + return asSourceClass(metadata.getClassName()); } /** diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java index 6b6d52742e..6af30c6fdd 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassPostProcessorTests.java @@ -1281,15 +1281,15 @@ public class ConfigurationClassPostProcessorTests { @Configuration static class FooBarConfiguration { - @Bean @DependsOn("bar") - public FooImpl foo() { - return new FooImpl(); - } - @Bean public BarInterface bar() { return new BarImpl(); } + + @Bean + public FooImpl foo() { + return new FooImpl(); + } } @Configuration