Resolved SPR-6618. Restrictions were too tight on overloaded bean methods and were preventing it altogether. Overloading is now allowed, as long as there is no ambiguity at runtime which bean method should be invoked.

This commit is contained in:
Chris Beams
2009-12-30 19:42:12 +00:00
parent 75d0f9b95c
commit d1b3f57320
9 changed files with 146 additions and 47 deletions

View File

@@ -16,6 +16,7 @@
package org.springframework.context.annotation;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
@@ -48,12 +49,10 @@ final class ConfigurationClass {
private final Resource resource;
private final Map<String, Class> importedResources = new LinkedHashMap<String, Class>();
private final Map<String, Class<?>> importedResources = new LinkedHashMap<String, Class<?>>();
private final Set<ConfigurationClassMethod> methods = new LinkedHashSet<ConfigurationClassMethod>();
private final Map<String, Integer> overloadedMethodMap = new LinkedHashMap<String, Integer>();
private String beanName;
@@ -90,42 +89,46 @@ final class ConfigurationClass {
return this.beanName;
}
public ConfigurationClass addMethod(ConfigurationClassMethod method) {
public void addMethod(ConfigurationClassMethod method) {
this.methods.add(method);
String name = method.getMetadata().getMethodName();
Integer count = this.overloadedMethodMap.get(name);
if (count != null) {
this.overloadedMethodMap.put(name, count + 1);
}
else {
this.overloadedMethodMap.put(name, 1);
}
return this;
}
public Set<ConfigurationClassMethod> getMethods() {
return this.methods;
}
public void addImportedResource(String importedResource, Class readerClass) {
public void addImportedResource(String importedResource, Class<?> readerClass) {
this.importedResources.put(importedResource, readerClass);
}
public Map<String, Class> getImportedResources() {
public Map<String, Class<?>> getImportedResources() {
return this.importedResources;
}
public void validate(ProblemReporter problemReporter) {
// No overloading of factory methods allowed
for (Map.Entry<String, Integer> entry : this.overloadedMethodMap.entrySet()) {
String methodName = entry.getKey();
int count = entry.getValue();
// a @Bean method may only be overloaded through inheritance. No single
// @Configuration class may declare two @Bean methods with the same name.
final char hashDelim = '#';
Map<String, Integer> methodNameCounts = new HashMap<String, Integer>();
for (ConfigurationClassMethod method : methods) {
String dClassName = method.getMetadata().getDeclaringClassName();
String methodName = method.getMetadata().getMethodName();
String fqMethodName = dClassName + hashDelim + methodName;
Integer currentCount = methodNameCounts.get(fqMethodName);
int newCount = currentCount != null ? currentCount + 1 : 1;
methodNameCounts.put(fqMethodName, newCount);
}
for (String methodName : methodNameCounts.keySet()) {
int count = methodNameCounts.get(methodName);
if (count > 1) {
problemReporter.error(new OverloadedMethodProblem(methodName, count));
String shortMethodName = methodName.substring(methodName.indexOf(hashDelim)+1);
problemReporter.error(new BeanMethodOverloadingProblem(shortMethodName, count));
}
}
// A configuration class may not be final (CGLIB limitation)
if (getMetadata().isAnnotated(Configuration.class.getName())) {
if (getMetadata().isFinal()) {
@@ -149,7 +152,6 @@ final class ConfigurationClass {
return getMetadata().getClassName().hashCode();
}
/** Configuration classes must be non-final to accommodate CGLIB subclassing. */
private class FinalConfigurationProblem extends Problem {
@@ -159,15 +161,15 @@ final class ConfigurationClass {
}
}
/** Bean methods on configuration classes may only be overloaded through inheritance. */
private class BeanMethodOverloadingProblem extends Problem {
/** Factory methods on configuration classes must not be overloaded. */
private class OverloadedMethodProblem extends Problem {
public OverloadedMethodProblem(String methodName, int count) {
super(String.format("@Configuration class '%s' has %s overloaded factory methods of name '%s'. " +
"Only one factory method of the same name allowed.",
public BeanMethodOverloadingProblem(String methodName, int count) {
super(String.format("@Configuration class '%s' has %s overloaded @Bean methods named '%s'. " +
"Only one @Bean method of a given name is allowed within each @Configuration class.",
getSimpleName(), count, methodName), new Location(getResource(), getMetadata()));
}
}
}

View File

@@ -119,7 +119,7 @@ class ConfigurationClassBeanDefinitionReader {
* the BeanDefinitionRegistry based on its contents.
*/
private void loadBeanDefinitionsForModelMethod(ConfigurationClassMethod method) {
ConfigurationClass configClass = method.getDeclaringClass();
ConfigurationClass configClass = method.getConfigurationClass();
MethodMetadata metadata = method.getMetadata();
RootBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass);
@@ -213,11 +213,11 @@ class ConfigurationClassBeanDefinitionReader {
registry.registerBeanDefinition(beanName, beanDefToRegister);
}
private void loadBeanDefinitionsFromImportedResources(Map<String, Class> importedResources) {
Map<Class, BeanDefinitionReader> readerInstanceCache = new HashMap<Class, BeanDefinitionReader>();
for (Map.Entry<String, Class> entry : importedResources.entrySet()) {
private void loadBeanDefinitionsFromImportedResources(Map<String, Class<?>> importedResources) {
Map<Class<?>, BeanDefinitionReader> readerInstanceCache = new HashMap<Class<?>, BeanDefinitionReader>();
for (Map.Entry<String, Class<?>> entry : importedResources.entrySet()) {
String resource = entry.getKey();
Class readerClass = entry.getValue();
Class<?> readerClass = entry.getValue();
if (!readerInstanceCache.containsKey(readerClass)) {
try {
BeanDefinitionReader readerInstance = (BeanDefinitionReader)

View File

@@ -16,6 +16,8 @@
package org.springframework.context.annotation;
import static java.lang.String.format;
import org.springframework.beans.factory.parsing.Location;
import org.springframework.beans.factory.parsing.Problem;
import org.springframework.beans.factory.parsing.ProblemReporter;
@@ -35,32 +37,37 @@ final class ConfigurationClassMethod {
private final MethodMetadata metadata;
private final ConfigurationClass declaringClass;
private final ConfigurationClass configurationClass;
public ConfigurationClassMethod(MethodMetadata metadata, ConfigurationClass declaringClass) {
public ConfigurationClassMethod(MethodMetadata metadata, ConfigurationClass configurationClass) {
this.metadata = metadata;
this.declaringClass = declaringClass;
this.configurationClass = configurationClass;
}
public MethodMetadata getMetadata() {
return this.metadata;
}
public ConfigurationClass getDeclaringClass() {
return this.declaringClass;
public ConfigurationClass getConfigurationClass() {
return this.configurationClass;
}
public Location getResourceLocation() {
return new Location(this.declaringClass.getResource(), metadata);
return new Location(this.configurationClass.getResource(), metadata);
}
public void validate(ProblemReporter problemReporter) {
if (this.declaringClass.getMetadata().isAnnotated(Configuration.class.getName()) && !getMetadata().isOverridable()) {
if (this.configurationClass.getMetadata().isAnnotated(Configuration.class.getName()) && !getMetadata().isOverridable()) {
problemReporter.error(new NonOverridableMethodError());
}
}
@Override
public String toString() {
return format("[%s:name=%s,declaringClass=%s]",
this.getClass().getSimpleName(), this.getMetadata().getMethodName(), this.getMetadata().getDeclaringClassName());
}
/**
@@ -74,4 +81,5 @@ final class ConfigurationClassMethod {
}
}
}

View File

@@ -131,7 +131,7 @@ class ConfigurationClassParser {
}
if (metadata.isAnnotated(ImportResource.class.getName())) {
String[] resources = (String[]) metadata.getAnnotationAttributes(ImportResource.class.getName()).get("value");
Class readerClass = (Class) metadata.getAnnotationAttributes(ImportResource.class.getName()).get("reader");
Class<?> readerClass = (Class<?>) metadata.getAnnotationAttributes(ImportResource.class.getName()).get("reader");
if (readerClass == null) {
throw new IllegalStateException("No reader class associated with imported resources: " +
StringUtils.arrayToCommaDelimitedString(resources));
@@ -140,8 +140,8 @@ class ConfigurationClassParser {
configClass.addImportedResource(resource, readerClass);
}
}
Set<MethodMetadata> methods = metadata.getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata methodMetadata : methods) {
Set<MethodMetadata> beanMethods = metadata.getAnnotatedMethods(Bean.class.getName());
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addMethod(new ConfigurationClassMethod(methodMetadata, configClass));
}
}