diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/Configuration.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/Configuration.java
index a730a96c477..58091101fad 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/Configuration.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/Configuration.java
@@ -232,6 +232,37 @@ import org.springframework.stereotype.Component;
* }
* }
*
+ *
With nested {@code @Configuration} classes
+ * {@code @Configuration} classes may be nested within one another as follows:
+ *
+ * @Configuration
+ * public class AppConfig {
+ * @Inject DataSource dataSource;
+ *
+ * @Bean
+ * public MyBean myBean() {
+ * return new MyBean(dataSource);
+ * }
+ *
+ * @Configuration
+ * static class DatabaseConfig {
+ * @Bean
+ * DataSource dataSource() {
+ * return new EmbeddedDatabaseBuilder().build();
+ * }
+ * }
+ * }
+ *
+ * When bootstrapping such an arrangement, only {@code AppConfig} need be registered
+ * against the application context. By virtue of being a nested {@code @Configuration}
+ * class, {@code DatabaseConfig} will be registered automatically. This avoids
+ * the need to use an {@code @Import} annotation when the relationship between
+ * {@code AppConfig} {@code DatabaseConfig} is already implicitly clear.
+ *
+ * Note also that nested {@code @Configuration} classes can be used to good effect
+ * with the {@code @Profile} annotation to provide two options of the same bean to the
+ * enclosing {@code @Configuration} class.
+ *
*
Configuring lazy initialization
* By default, {@code @Bean} methods will be eagerly instantiated at container
* bootstrap time. To avoid this, {@code @Configuration} may be used in conjunction with
diff --git a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
index 43d567a57bb..e00c6b24859 100644
--- a/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
+++ b/org.springframework.context/src/main/java/org/springframework/context/annotation/ConfigurationClassParser.java
@@ -16,6 +16,8 @@
package org.springframework.context.annotation;
+import static org.springframework.context.annotation.ConfigurationClassUtils.isConfigurationCandidate;
+
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
@@ -157,6 +159,17 @@ class ConfigurationClassParser {
}
protected void doProcessConfigurationClass(ConfigurationClass configClass, AnnotationMetadata metadata) throws IOException {
+
+ // recursively process any member (nested) classes first
+ for (String memberClassName : metadata.getMemberClassNames()) {
+ MetadataReader reader = this.metadataReaderFactory.getMetadataReader(memberClassName);
+ AnnotationMetadata memberClassMetadata = reader.getAnnotationMetadata();
+ if (isConfigurationCandidate(memberClassMetadata)) {
+ processConfigurationClass(new ConfigurationClass(reader, null));
+ }
+ }
+
+ // process any @PropertySource annotations
Map propertySourceAttributes =
metadata.getAnnotationAttributes(org.springframework.context.annotation.PropertySource.class.getName());
if (propertySourceAttributes != null) {
@@ -169,6 +182,7 @@ class ConfigurationClassParser {
this.propertySources.push(ps);
}
+ // process any @ComponentScan annotions
Map componentScanAttributes = metadata.getAnnotationAttributes(ComponentScan.class.getName());
if (componentScanAttributes != null) {
// the config class is annotated with @ComponentScan -> perform the scan immediately
@@ -188,12 +202,14 @@ class ConfigurationClassParser {
}
}
+ // process any @Import annotations
List