Skip to content

Commit 08e2669

Browse files
committed
Fix infinite recursion bug in nested @configuration
Prior to this commit, an infinite recursion would occur if a @configuration class were nested within its superclass, e.g. abstract class Parent { @configuration static class Child extends Parent { ... } } This is because the processing of the nested class automatically checks the superclass hierarchy for certain reasons, and each superclass is in turn checked for nested @configuration classes. The ConfigurationClassParser implementation now prevents this by keeping track of known superclasses, i.e. once a superclass has been processed, it is never again checked for nested classes, etc. Issue: SPR-8955
1 parent f3651c9 commit 08e2669

File tree

3 files changed

+99
-18
lines changed

3 files changed

+99
-18
lines changed

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

+31-18
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ class ConfigurationClassParser {
7777

7878
private final ImportStack importStack = new ImportStack();
7979

80+
private final Set<String> knownSuperclasses = new LinkedHashSet<String>();
81+
8082
private final Set<ConfigurationClass> configurationClasses =
8183
new LinkedHashSet<ConfigurationClass>();
8284

@@ -138,23 +140,12 @@ protected void processConfigurationClass(ConfigurationClass configClass) throws
138140
}
139141
}
140142

141-
while (metadata != null) {
142-
doProcessConfigurationClass(configClass, metadata);
143-
String superClassName = metadata.getSuperClassName();
144-
if (superClassName != null && !Object.class.getName().equals(superClassName)) {
145-
if (metadata instanceof StandardAnnotationMetadata) {
146-
Class<?> clazz = ((StandardAnnotationMetadata) metadata).getIntrospectedClass();
147-
metadata = new StandardAnnotationMetadata(clazz.getSuperclass(), true);
148-
}
149-
else {
150-
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(superClassName);
151-
metadata = reader.getAnnotationMetadata();
152-
}
153-
}
154-
else {
155-
metadata = null;
156-
}
143+
// recursively process the configuration class and its superclass hierarchy
144+
do {
145+
metadata = doProcessConfigurationClass(configClass, metadata);
157146
}
147+
while (metadata != null);
148+
158149
if (this.configurationClasses.contains(configClass) && configClass.getBeanName() != null) {
159150
// Explicit bean definition found, probably replacing an import.
160151
// Let's remove the old one and go with the new one.
@@ -164,7 +155,11 @@ protected void processConfigurationClass(ConfigurationClass configClass) throws
164155
this.configurationClasses.add(configClass);
165156
}
166157

167-
protected void doProcessConfigurationClass(ConfigurationClass configClass, AnnotationMetadata metadata) throws IOException {
158+
/**
159+
* @return annotation metadata of superclass, null if none found or previously processed
160+
*/
161+
protected AnnotationMetadata doProcessConfigurationClass(
162+
ConfigurationClass configClass, AnnotationMetadata metadata) throws IOException {
168163

169164
// recursively process any member (nested) classes first
170165
for (String memberClassName : metadata.getMemberClassNames()) {
@@ -227,8 +222,26 @@ protected void doProcessConfigurationClass(ConfigurationClass configClass, Annot
227222
for (MethodMetadata methodMetadata : beanMethods) {
228223
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
229224
}
230-
}
231225

226+
// process superclass, if any
227+
if (metadata.hasSuperClass()) {
228+
String superclass = metadata.getSuperClassName();
229+
if (this.knownSuperclasses.add(superclass)) {
230+
// superclass found, return its annotation metadata and recurse
231+
if (metadata instanceof StandardAnnotationMetadata) {
232+
Class<?> clazz = ((StandardAnnotationMetadata) metadata).getIntrospectedClass();
233+
return new StandardAnnotationMetadata(clazz.getSuperclass(), true);
234+
}
235+
else {
236+
MetadataReader reader = this.metadataReaderFactory.getMetadataReader(superclass);
237+
return reader.getAnnotationMetadata();
238+
}
239+
}
240+
}
241+
242+
// no superclass, processing is complete
243+
return null;
244+
}
232245

233246
/**
234247
* Return a list of attribute maps for all declarations of the given annotation
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2002-2012 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.context.annotation.configuration.spr8955;
18+
19+
import org.springframework.stereotype.Component;
20+
21+
/**
22+
* @author Chris Beams
23+
* @author Willem Dekker
24+
*/
25+
abstract class Spr8955Parent {
26+
27+
@Component
28+
static class Spr8955Child extends Spr8955Parent {
29+
30+
}
31+
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2002-2012 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.context.annotation.configuration.spr8955;
18+
19+
import org.junit.Test;
20+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
21+
22+
23+
/**
24+
* @author Chris Beams
25+
* @author Willem Dekker
26+
*/
27+
public class Spr8955Tests {
28+
29+
@Test
30+
public void repro() {
31+
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
32+
ctx.scan("org.springframework.context.annotation.configuration.spr8955");
33+
ctx.refresh();
34+
}
35+
36+
}

0 commit comments

Comments
 (0)