introduced "lenientConstructorResolution" flag (SPR-5816)

This commit is contained in:
Juergen Hoeller 2009-07-16 13:27:47 +00:00
parent f4a83c5c74
commit a9254b34d1
4 changed files with 62 additions and 9 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2008 the original author or authors. * Copyright 2002-2009 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,7 +16,6 @@
package org.springframework.beans.factory.support; package org.springframework.beans.factory.support;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.util.Arrays; import java.util.Arrays;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@ -144,6 +143,8 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
private ConstructorArgumentValues constructorArgumentValues; private ConstructorArgumentValues constructorArgumentValues;
private boolean lenientConstructorResolution = true;
private MutablePropertyValues propertyValues; private MutablePropertyValues propertyValues;
private MethodOverrides methodOverrides = new MethodOverrides(); private MethodOverrides methodOverrides = new MethodOverrides();
@ -168,7 +169,6 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
private Resource resource; private Resource resource;
/** /**
* Create a new AbstractBeanDefinition with default settings. * Create a new AbstractBeanDefinition with default settings.
*/ */
@ -226,6 +226,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
setAutowireCandidate(originalAbd.isAutowireCandidate()); setAutowireCandidate(originalAbd.isAutowireCandidate());
copyQualifiersFrom(originalAbd); copyQualifiersFrom(originalAbd);
setPrimary(originalAbd.isPrimary()); setPrimary(originalAbd.isPrimary());
setLenientConstructorResolution(originalAbd.isLenientConstructorResolution());
setInitMethodName(originalAbd.getInitMethodName()); setInitMethodName(originalAbd.getInitMethodName());
setEnforceInitMethod(originalAbd.isEnforceInitMethod()); setEnforceInitMethod(originalAbd.isEnforceInitMethod());
setDestroyMethodName(originalAbd.getDestroyMethodName()); setDestroyMethodName(originalAbd.getDestroyMethodName());
@ -297,6 +298,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
setPrimary(otherAbd.isPrimary()); setPrimary(otherAbd.isPrimary());
setDependencyCheck(otherAbd.getDependencyCheck()); setDependencyCheck(otherAbd.getDependencyCheck());
setDependsOn(otherAbd.getDependsOn()); setDependsOn(otherAbd.getDependsOn());
setLenientConstructorResolution(otherAbd.isLenientConstructorResolution());
if (otherAbd.getInitMethodName() != null) { if (otherAbd.getInitMethodName() != null) {
setInitMethodName(otherAbd.getInitMethodName()); setInitMethodName(otherAbd.getInitMethodName());
setEnforceInitMethod(otherAbd.isEnforceInitMethod()); setEnforceInitMethod(otherAbd.isEnforceInitMethod());
@ -642,7 +644,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
* Copy the qualifiers from the supplied AbstractBeanDefinition to this bean definition. * Copy the qualifiers from the supplied AbstractBeanDefinition to this bean definition.
* @param source the AbstractBeanDefinition to copy from * @param source the AbstractBeanDefinition to copy from
*/ */
protected void copyQualifiersFrom(AbstractBeanDefinition source) { public void copyQualifiersFrom(AbstractBeanDefinition source) {
Assert.notNull(source, "Source must not be null"); Assert.notNull(source, "Source must not be null");
this.qualifiers.putAll(source.qualifiers); this.qualifiers.putAll(source.qualifiers);
} }
@ -670,6 +672,23 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess
return !this.constructorArgumentValues.isEmpty(); return !this.constructorArgumentValues.isEmpty();
} }
/**
* Specify whether to resolve constructors in lenient mode (<code>true</code>,
* which is the default) or to switch to strict resolution (throwing an exception
* in case of ambigious constructors that all match when converting the arguments,
* whereas lenient mode would use the one with the 'closest' type matches).
*/
public void setLenientConstructorResolution(boolean lenientConstructorResolution) {
this.lenientConstructorResolution = lenientConstructorResolution;
}
/**
* Return whether to resolve constructors in lenient mode or in strict mode.
*/
public boolean isLenientConstructorResolution() {
return this.lenientConstructorResolution;
}
/** /**
* Specify property values for this bean, if any. * Specify property values for this bean, if any.
*/ */

View File

@ -226,6 +226,12 @@ class ConstructorResolver {
argsToUse = args.arguments; argsToUse = args.arguments;
minTypeDiffWeight = typeDiffWeight; minTypeDiffWeight = typeDiffWeight;
} }
else if (typeDiffWeight < Integer.MAX_VALUE && typeDiffWeight == minTypeDiffWeight &&
!mbd.isLenientConstructorResolution()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Ambiguous constructor matches found in bean '" + beanName + "' " +
"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)");
}
} }
if (constructorToUse == null) { if (constructorToUse == null) {
@ -561,21 +567,24 @@ class ConstructorResolver {
// We found a potential match - let's give it a try. // We found a potential match - let's give it a try.
// Do not consider the same value definition multiple times! // Do not consider the same value definition multiple times!
usedValueHolders.add(valueHolder); usedValueHolders.add(valueHolder);
args.rawArguments[paramIndex] = valueHolder.getValue(); ConstructorArgumentValues.ValueHolder sourceHolder =
(ConstructorArgumentValues.ValueHolder) valueHolder.getSource();
Object originalValue = valueHolder.getValue();
Object sourceValue = sourceHolder.getValue();
if (valueHolder.isConverted()) { if (valueHolder.isConverted()) {
Object convertedValue = valueHolder.getConvertedValue(); Object convertedValue = valueHolder.getConvertedValue();
args.rawArguments[paramIndex] =
(mbd.isLenientConstructorResolution() ? originalValue : convertedValue);
args.arguments[paramIndex] = convertedValue; args.arguments[paramIndex] = convertedValue;
args.preparedArguments[paramIndex] = convertedValue; args.preparedArguments[paramIndex] = convertedValue;
} }
else { else {
try { try {
Object originalValue = valueHolder.getValue();
Object convertedValue = converter.convertIfNecessary(originalValue, paramType, Object convertedValue = converter.convertIfNecessary(originalValue, paramType,
MethodParameter.forMethodOrConstructor(methodOrCtor, paramIndex)); MethodParameter.forMethodOrConstructor(methodOrCtor, paramIndex));
args.rawArguments[paramIndex] =
(mbd.isLenientConstructorResolution() ? originalValue : convertedValue);
args.arguments[paramIndex] = convertedValue; args.arguments[paramIndex] = convertedValue;
ConstructorArgumentValues.ValueHolder sourceHolder =
(ConstructorArgumentValues.ValueHolder) valueHolder.getSource();
Object sourceValue = sourceHolder.getValue();
if (originalValue == sourceValue || sourceValue instanceof TypedStringValue) { if (originalValue == sourceValue || sourceValue instanceof TypedStringValue) {
// Either a converted value or still the original one: store converted value. // Either a converted value or still the original one: store converted value.
sourceHolder.setConvertedValue(convertedValue); sourceHolder.setConvertedValue(convertedValue);

View File

@ -170,6 +170,11 @@
<constructor-arg index="1"><value>true</value></constructor-arg> <constructor-arg index="1"><value>true</value></constructor-arg>
</bean> </bean>
<bean id="beanWithDoubleBooleanNoType" class="org.springframework.beans.factory.xml.XmlBeanFactoryTests$DoubleBooleanConstructorBean" scope="prototype">
<constructor-arg index="0"><value>false</value></constructor-arg>
<constructor-arg index="1"><value>true</value></constructor-arg>
</bean>
<bean id="constructorArray" class="org.springframework.beans.factory.xml.XmlBeanFactoryTests$ConstructorArrayTestBean"> <bean id="constructorArray" class="org.springframework.beans.factory.xml.XmlBeanFactoryTests$ConstructorArrayTestBean">
<constructor-arg type="int[]"> <constructor-arg type="int[]">
<array value-type="int"> <array value-type="int">

View File

@ -56,6 +56,7 @@ import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.MethodReplacer; import org.springframework.beans.factory.support.MethodReplacer;
import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.UrlResource; import org.springframework.core.io.UrlResource;
@ -1363,6 +1364,21 @@ public final class XmlBeanFactoryTests {
assertEquals(Boolean.TRUE, bean.boolean2); assertEquals(Boolean.TRUE, bean.boolean2);
} }
public @Test void testDoubleBooleanNoType() {
XmlBeanFactory xbf = new XmlBeanFactory(CONSTRUCTOR_ARG_CONTEXT);
AbstractBeanDefinition bd = (AbstractBeanDefinition) xbf.getBeanDefinition("beanWithDoubleBooleanNoType");
bd.setLenientConstructorResolution(false);
try {
xbf.getBean("beanWithDoubleBooleanNoType");
fail("Should have thrown BeanCreationException");
}
catch (BeanCreationException ex) {
// expected
ex.printStackTrace();
assertTrue(ex.getMostSpecificCause().getMessage().contains("Ambiguous"));
}
}
public @Test void testPrimitiveConstructorArray() { public @Test void testPrimitiveConstructorArray() {
XmlBeanFactory xbf = new XmlBeanFactory(CONSTRUCTOR_ARG_CONTEXT); XmlBeanFactory xbf = new XmlBeanFactory(CONSTRUCTOR_ARG_CONTEXT);
ConstructorArrayTestBean bean = (ConstructorArrayTestBean) xbf.getBean("constructorArray"); ConstructorArrayTestBean bean = (ConstructorArrayTestBean) xbf.getBean("constructorArray");
@ -1603,6 +1619,10 @@ public final class XmlBeanFactoryTests {
this.boolean1 = b1; this.boolean1 = b1;
this.boolean2 = b2; this.boolean2 = b2;
} }
public DoubleBooleanConstructorBean(String s1, String s2) {
throw new IllegalStateException("Don't pick this constructor");
}
} }