Skip to content

Commit 940011e

Browse files
rwinchunknown
authored and
unknown
committed
Ensure Parent ConfigurationClass loaded on overrides
Previously ConfigurationClassParser could override a nested @configuration without consideration of @bean's defined in parent classes. This commit ensures that if the original ConfigurationClass contains additional bean definitions it is processed again. Issue: SPR-10546
1 parent 92bbd81 commit 940011e

File tree

10 files changed

+447
-7
lines changed

10 files changed

+447
-7
lines changed

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

+66-7
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,12 @@
2323
import java.util.Collections;
2424
import java.util.Comparator;
2525
import java.util.HashMap;
26+
import java.util.HashSet;
2627
import java.util.Iterator;
28+
import java.util.LinkedHashMap;
2729
import java.util.LinkedHashSet;
2830
import java.util.Map;
31+
import java.util.Map.Entry;
2932
import java.util.Set;
3033
import java.util.Stack;
3134

@@ -76,6 +79,7 @@
7679
*
7780
* @author Chris Beams
7881
* @author Juergen Hoeller
82+
* @author Rob Winch
7983
* @since 3.0
8084
* @see ConfigurationClassBeanDefinitionReader
8185
*/
@@ -89,8 +93,8 @@ class ConfigurationClassParser {
8993

9094
private final Set<String> knownSuperclasses = new LinkedHashSet<String>();
9195

92-
private final Set<ConfigurationClass> configurationClasses =
93-
new LinkedHashSet<ConfigurationClass>();
96+
private final Map<ConfigurationClass,ConfigurationClass> configurationClasses =
97+
new LinkedHashMap<ConfigurationClass,ConfigurationClass>();
9498

9599
private final Stack<PropertySource<?>> propertySources =
96100
new Stack<PropertySource<?>>();
@@ -157,13 +161,60 @@ protected void processConfigurationClass(ConfigurationClass configClass) throws
157161
}
158162
while (metadata != null);
159163

160-
if (this.configurationClasses.contains(configClass) && configClass.getBeanName() != null) {
164+
if (getConfigurationClasses().contains(configClass) && configClass.getBeanName() != null) {
161165
// Explicit bean definition found, probably replacing an import.
162166
// Let's remove the old one and go with the new one.
163-
this.configurationClasses.remove(configClass);
167+
ConfigurationClass originalConfigClass = removeConfigurationClass(configClass);
168+
169+
mergeFromOriginalConfig(originalConfigClass,configClass);
164170
}
165171

166-
this.configurationClasses.add(configClass);
172+
addConfigurationClass(configClass);
173+
}
174+
175+
176+
/**
177+
* Merges from the original {@link ConfigurationClass} to the new
178+
* {@link ConfigurationClass}. This is necessary if parent classes have already been
179+
* processed.
180+
*
181+
* @param originalConfigClass the original {@link ConfigurationClass} that may have
182+
* additional metadata
183+
* @param configClass the new {@link ConfigurationClass} that will have metadata added
184+
* to it if necessary
185+
*/
186+
private void mergeFromOriginalConfig(ConfigurationClass originalConfigClass,
187+
ConfigurationClass configClass) {
188+
189+
Set<String> beanMethodNames = new HashSet<String>();
190+
for(BeanMethod beanMethod : configClass.getBeanMethods()) {
191+
beanMethodNames.add(createBeanMethodName(beanMethod));
192+
}
193+
194+
for(BeanMethod originalBeanMethod : originalConfigClass.getBeanMethods()) {
195+
String originalBeanMethodName = createBeanMethodName(originalBeanMethod);
196+
if(!beanMethodNames.contains(originalBeanMethodName)) {
197+
configClass.addBeanMethod(new BeanMethod(originalBeanMethod.getMetadata(), configClass));
198+
}
199+
}
200+
for(Entry<String, Class<? extends BeanDefinitionReader>> originalImportedEntry : originalConfigClass.getImportedResources().entrySet()) {
201+
if(!configClass.getImportedResources().containsKey(originalImportedEntry.getKey())) {
202+
configClass.addImportedResource(originalImportedEntry.getKey(), originalImportedEntry.getValue());
203+
}
204+
}
205+
}
206+
207+
/**
208+
* Converts a {@link BeanMethod} into the fully qualified name of the Method
209+
*
210+
* @param beanMethod
211+
* @return fully qualified name of the {@link BeanMethod}
212+
*/
213+
private String createBeanMethodName(BeanMethod beanMethod) {
214+
String hashDelim = "#";
215+
String dClassName = beanMethod.getMetadata().getDeclaringClassName();
216+
String methodName = beanMethod.getMetadata().getMethodName();
217+
return dClassName + hashDelim + methodName;
167218
}
168219

169220
/**
@@ -251,6 +302,14 @@ else if (superclass.startsWith("java")) {
251302
return null;
252303
}
253304

305+
private void addConfigurationClass(ConfigurationClass configClass) {
306+
this.configurationClasses.put(configClass,configClass);
307+
}
308+
309+
private ConfigurationClass removeConfigurationClass(ConfigurationClass configClass) {
310+
return this.configurationClasses.remove(configClass);
311+
}
312+
254313
/**
255314
* Register member (nested) classes that happen to be configuration classes themselves.
256315
* @param metadata the metadata representation of the containing class
@@ -441,13 +500,13 @@ private void invokeAwareMethods(ImportBeanDefinitionRegistrar registrar) {
441500
* @see ConfigurationClass#validate
442501
*/
443502
public void validate() {
444-
for (ConfigurationClass configClass : this.configurationClasses) {
503+
for (ConfigurationClass configClass : getConfigurationClasses()) {
445504
configClass.validate(this.problemReporter);
446505
}
447506
}
448507

449508
public Set<ConfigurationClass> getConfigurationClasses() {
450-
return this.configurationClasses;
509+
return this.configurationClasses.keySet();
451510
}
452511

453512
public Stack<PropertySource<?>> getPropertySources() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright 2002-2013 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.spr10546;
18+
19+
import org.springframework.context.annotation.Bean;
20+
import org.springframework.context.annotation.Configuration;
21+
22+
23+
/**
24+
*
25+
* @author Rob Winch
26+
*/
27+
@Configuration
28+
public class ImportedConfig {
29+
@Bean
30+
public String myBean() {
31+
return "myBean";
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2002-2013 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.spr10546;
18+
19+
import org.springframework.context.annotation.Bean;
20+
import org.springframework.context.annotation.Configuration;
21+
22+
23+
/**
24+
*
25+
* @author Rob Winch
26+
*/
27+
@Configuration
28+
public class ParentConfig {
29+
@Bean
30+
public String myBean() {
31+
return "myBean";
32+
}
33+
}
34+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2002-2013 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.spr10546;
18+
19+
import org.springframework.context.annotation.ComponentScan;
20+
import org.springframework.context.annotation.Configuration;
21+
import org.springframework.context.annotation.spr10546.scanpackage.AEnclosingConfig;
22+
23+
24+
/**
25+
*
26+
* @author Rob Winch
27+
*/
28+
@Configuration
29+
@ComponentScan(basePackageClasses=AEnclosingConfig.class)
30+
public class ParentWithComponentScanConfig {
31+
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2002-2013 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.spr10546;
18+
19+
import org.springframework.context.annotation.Configuration;
20+
import org.springframework.context.annotation.Import;
21+
22+
23+
/**
24+
*
25+
* @author Rob Winch
26+
*/
27+
@Configuration
28+
@Import(ImportedConfig.class)
29+
public class ParentWithImportConfig {
30+
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2002-2013 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.spr10546;
18+
19+
import org.springframework.context.annotation.Configuration;
20+
import org.springframework.context.annotation.ImportResource;
21+
22+
23+
/**
24+
*
25+
* @author Rob Winch
26+
*/
27+
@Configuration
28+
@ImportResource("classpath:org/springframework/context/annotation/spr10546/importedResource.xml")
29+
public class ParentWithImportResourceConfig {
30+
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
* Copyright 2002-2013 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.spr10546;
18+
19+
import org.springframework.context.annotation.Configuration;
20+
21+
22+
/**
23+
*
24+
* @author Rob Winch
25+
*/
26+
@Configuration
27+
public class ParentWithParentConfig extends ParentConfig {
28+
29+
}

0 commit comments

Comments
 (0)