Skip to content

Commit 71c6eb2

Browse files
committed
Additional configuration classes get detected when imported through XML or registrars
Issue: SPR-11430 Issue: SPR-11723
1 parent f1f1c4c commit 71c6eb2

File tree

5 files changed

+182
-112
lines changed

5 files changed

+182
-112
lines changed

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ public int compare(DeferredImportSelectorHolder o1, DeferredImportSelectorHolder
113113

114114
private final ComponentScanAnnotationParser componentScanParser;
115115

116+
private final ConditionEvaluator conditionEvaluator;
117+
116118
private final Map<ConfigurationClass, ConfigurationClass> configurationClasses =
117119
new LinkedHashMap<ConfigurationClass, ConfigurationClass>();
118120

@@ -125,8 +127,6 @@ public int compare(DeferredImportSelectorHolder o1, DeferredImportSelectorHolder
125127

126128
private final List<DeferredImportSelectorHolder> deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();
127129

128-
private final ConditionEvaluator conditionEvaluator;
129-
130130

131131
/**
132132
* Create a new {@link ConfigurationClassParser} instance that will be used
@@ -488,6 +488,10 @@ public Set<ConfigurationClass> getConfigurationClasses() {
488488
return this.configurationClasses.keySet();
489489
}
490490

491+
public int getPropertySourceCount() {
492+
return this.propertySources.size();
493+
}
494+
491495
public List<PropertySource<?>> getPropertySources() {
492496
List<PropertySource<?>> propertySources = new LinkedList<PropertySource<?>>();
493497
for (Map.Entry<String, List<ResourcePropertySource>> entry : this.propertySources.entrySet()) {

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

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.context.annotation;
1818

1919
import java.beans.PropertyDescriptor;
20+
import java.util.Arrays;
2021
import java.util.HashSet;
2122
import java.util.LinkedHashMap;
2223
import java.util.LinkedHashSet;
@@ -269,7 +270,9 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
269270
*/
270271
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
271272
Set<BeanDefinitionHolder> configCandidates = new LinkedHashSet<BeanDefinitionHolder>();
272-
for (String beanName : registry.getBeanDefinitionNames()) {
273+
String[] candidateNames = registry.getBeanDefinitionNames();
274+
275+
for (String beanName : candidateNames) {
273276
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
274277
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
275278
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
@@ -302,32 +305,59 @@ else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.
302305
ConfigurationClassParser parser = new ConfigurationClassParser(
303306
this.metadataReaderFactory, this.problemReporter, this.environment,
304307
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
305-
parser.parse(configCandidates);
306-
parser.validate();
307-
308-
// Handle any @PropertySource annotations
309-
List<PropertySource<?>> parsedPropertySources = parser.getPropertySources();
310-
if (!parsedPropertySources.isEmpty()) {
311-
if (!(this.environment instanceof ConfigurableEnvironment)) {
312-
logger.warn("Ignoring @PropertySource annotations. " +
313-
"Reason: Environment must implement ConfigurableEnvironment");
314-
}
315-
else {
316-
MutablePropertySources envPropertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();
317-
for (PropertySource<?> propertySource : parsedPropertySources) {
318-
envPropertySources.addLast(propertySource);
308+
309+
Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
310+
int propertySourceCount = 0;
311+
do {
312+
parser.parse(configCandidates);
313+
parser.validate();
314+
315+
// Handle any @PropertySource annotations
316+
if (parser.getPropertySourceCount() > propertySourceCount) {
317+
List<PropertySource<?>> parsedPropertySources = parser.getPropertySources();
318+
if (!parsedPropertySources.isEmpty()) {
319+
if (!(this.environment instanceof ConfigurableEnvironment)) {
320+
logger.warn("Ignoring @PropertySource annotations. " +
321+
"Reason: Environment must implement ConfigurableEnvironment");
322+
}
323+
else {
324+
MutablePropertySources envPropertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();
325+
for (PropertySource<?> propertySource : parsedPropertySources) {
326+
envPropertySources.addLast(propertySource);
327+
}
328+
}
319329
}
330+
propertySourceCount = parser.getPropertySourceCount();
320331
}
321-
}
322332

323-
// Read the model and create bean definitions based on its content
324-
if (this.reader == null) {
325-
this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor,
326-
this.problemReporter, this.metadataReaderFactory, this.resourceLoader, this.environment,
327-
this.importBeanNameGenerator);
328-
}
333+
Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
334+
configClasses.removeAll(alreadyParsed);
329335

330-
this.reader.loadBeanDefinitions(parser.getConfigurationClasses());
336+
// Read the model and create bean definitions based on its content
337+
if (this.reader == null) {
338+
this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor,
339+
this.problemReporter, this.metadataReaderFactory, this.resourceLoader, this.environment,
340+
this.importBeanNameGenerator);
341+
}
342+
this.reader.loadBeanDefinitions(configClasses);
343+
alreadyParsed.addAll(configClasses);
344+
345+
configCandidates.clear();
346+
if (registry.getBeanDefinitionCount() > candidateNames.length) {
347+
String[] newCandidateNames = registry.getBeanDefinitionNames();
348+
Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));
349+
for (String candidateName : newCandidateNames) {
350+
if (!oldCandidateNames.contains(candidateName)) {
351+
BeanDefinition beanDef = registry.getBeanDefinition(candidateName);
352+
if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
353+
configCandidates.add(new BeanDefinitionHolder(beanDef, candidateName));
354+
}
355+
}
356+
}
357+
candidateNames = newCandidateNames;
358+
}
359+
}
360+
while (!configCandidates.isEmpty());
331361

332362
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
333363
if (singletonRegistry != null) {

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

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 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.
@@ -22,10 +22,9 @@
2222
import java.lang.annotation.Target;
2323

2424
import org.junit.Test;
25-
import org.springframework.beans.BeansException;
25+
2626
import org.springframework.beans.factory.BeanFactory;
2727
import org.springframework.beans.factory.BeanFactoryAware;
28-
import org.springframework.beans.factory.config.BeanDefinition;
2928
import org.springframework.beans.factory.config.BeanPostProcessor;
3029
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
3130
import org.springframework.beans.factory.support.GenericBeanDefinition;
@@ -42,6 +41,7 @@
4241
* annotation metadata of the @Configuration class that imported it.
4342
*
4443
* @author Chris Beams
44+
* @author Juergen Hoeller
4545
* @since 3.1
4646
*/
4747
public class ImportAwareTests {
@@ -51,8 +51,7 @@ public void directlyAnnotatedWithImport() {
5151
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
5252
ctx.register(ImportingConfig.class);
5353
ctx.refresh();
54-
55-
ctx.getBean("importedConfigBean");
54+
assertNotNull(ctx.getBean("importedConfigBean"));
5655

5756
ImportedConfig importAwareConfig = ctx.getBean(ImportedConfig.class);
5857
AnnotationMetadata importMetadata = importAwareConfig.importMetadata;
@@ -68,8 +67,7 @@ public void indirectlyAnnotatedWithImport() {
6867
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
6968
ctx.register(IndirectlyImportingConfig.class);
7069
ctx.refresh();
71-
72-
ctx.getBean("importedConfigBean");
70+
assertNotNull(ctx.getBean("importedConfigBean"));
7371

7472
ImportedConfig importAwareConfig = ctx.getBean(ImportedConfig.class);
7573
AnnotationMetadata importMetadata = importAwareConfig.importMetadata;
@@ -87,6 +85,7 @@ public void importRegistrar() throws Exception {
8785
ctx.register(ImportingRegistrarConfig.class);
8886
ctx.refresh();
8987
assertNotNull(ctx.getBean("registrarImportedBean"));
88+
assertNotNull(ctx.getBean("otherImportedConfigBean"));
9089
}
9190

9291
@Test
@@ -96,9 +95,12 @@ public void importRegistrarWithImport() throws Exception {
9695
ctx.register(ImportingRegistrarConfigWithImport.class);
9796
ctx.refresh();
9897
assertNotNull(ctx.getBean("registrarImportedBean"));
98+
assertNotNull(ctx.getBean("otherImportedConfigBean"));
99+
assertNotNull(ctx.getBean("importedConfigBean"));
99100
assertNotNull(ctx.getBean(ImportedConfig.class));
100101
}
101102

103+
102104
@Configuration
103105
@Import(ImportedConfig.class)
104106
static class ImportingConfig {
@@ -140,28 +142,40 @@ public AsyncAnnotationBeanPostProcessor asyncBPP() {
140142
}
141143

142144

145+
@Configuration
146+
static class OtherImportedConfig {
147+
148+
@Bean
149+
public String otherImportedConfigBean() {
150+
return "";
151+
}
152+
}
153+
154+
143155
static class BPP implements BeanFactoryAware, BeanPostProcessor {
144156

145157
@Override
146-
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
158+
public Object postProcessBeforeInitialization(Object bean, String beanName) {
147159
return bean;
148160
}
149161

150162
@Override
151-
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
163+
public Object postProcessAfterInitialization(Object bean, String beanName) {
152164
return bean;
153165
}
154166

155167
@Override
156-
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
168+
public void setBeanFactory(BeanFactory beanFactory) {
157169
}
158170
}
159171

172+
160173
@Configuration
161174
@EnableImportRegistrar
162175
static class ImportingRegistrarConfig {
163176
}
164177

178+
165179
@Configuration
166180
@EnableImportRegistrar
167181
@Import(ImportedConfig.class)
@@ -174,18 +188,22 @@ static class ImportingRegistrarConfigWithImport {
174188
public @interface EnableImportRegistrar {
175189
}
176190

191+
177192
static class ImportedRegistrar implements ImportBeanDefinitionRegistrar {
178193

179194
static boolean called;
180195

181196
@Override
182-
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
183-
BeanDefinitionRegistry registry) {
184-
BeanDefinition beanDefinition = new GenericBeanDefinition();
197+
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
198+
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
185199
beanDefinition.setBeanClassName(String.class.getName());
186-
registry.registerBeanDefinition("registrarImportedBean", beanDefinition );
187-
Assert.state(called == false, "ImportedRegistrar called twice");
200+
registry.registerBeanDefinition("registrarImportedBean", beanDefinition);
201+
GenericBeanDefinition beanDefinition2 = new GenericBeanDefinition();
202+
beanDefinition2.setBeanClass(OtherImportedConfig.class);
203+
registry.registerBeanDefinition("registrarImportedConfig", beanDefinition2);
204+
Assert.state(!called, "ImportedRegistrar called twice");
188205
called = true;
189206
}
190207
}
208+
191209
}

0 commit comments

Comments
 (0)