diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java index b0fdd5320ad..0dc8f487091 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/AbstractBeanDefinition.java @@ -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"); * you may not use this file except in compliance with the License. @@ -16,7 +16,6 @@ package org.springframework.beans.factory.support; -import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.util.Arrays; import java.util.LinkedHashMap; @@ -144,6 +143,8 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess private ConstructorArgumentValues constructorArgumentValues; + private boolean lenientConstructorResolution = true; + private MutablePropertyValues propertyValues; private MethodOverrides methodOverrides = new MethodOverrides(); @@ -168,7 +169,6 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess private Resource resource; - /** * Create a new AbstractBeanDefinition with default settings. */ @@ -226,6 +226,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess setAutowireCandidate(originalAbd.isAutowireCandidate()); copyQualifiersFrom(originalAbd); setPrimary(originalAbd.isPrimary()); + setLenientConstructorResolution(originalAbd.isLenientConstructorResolution()); setInitMethodName(originalAbd.getInitMethodName()); setEnforceInitMethod(originalAbd.isEnforceInitMethod()); setDestroyMethodName(originalAbd.getDestroyMethodName()); @@ -297,6 +298,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess setPrimary(otherAbd.isPrimary()); setDependencyCheck(otherAbd.getDependencyCheck()); setDependsOn(otherAbd.getDependsOn()); + setLenientConstructorResolution(otherAbd.isLenientConstructorResolution()); if (otherAbd.getInitMethodName() != null) { setInitMethodName(otherAbd.getInitMethodName()); setEnforceInitMethod(otherAbd.isEnforceInitMethod()); @@ -642,7 +644,7 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess * Copy the qualifiers from the supplied AbstractBeanDefinition to this bean definition. * @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"); this.qualifiers.putAll(source.qualifiers); } @@ -670,6 +672,23 @@ public abstract class AbstractBeanDefinition extends BeanMetadataAttributeAccess return !this.constructorArgumentValues.isEmpty(); } + /** + * Specify whether to resolve constructors in lenient mode (true, + * 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. */ diff --git a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java index 86c3bd3f006..6972e847b79 100644 --- a/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java +++ b/org.springframework.beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java @@ -226,6 +226,12 @@ class ConstructorResolver { argsToUse = args.arguments; 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) { @@ -561,21 +567,24 @@ class ConstructorResolver { // We found a potential match - let's give it a try. // Do not consider the same value definition multiple times! 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()) { Object convertedValue = valueHolder.getConvertedValue(); + args.rawArguments[paramIndex] = + (mbd.isLenientConstructorResolution() ? originalValue : convertedValue); args.arguments[paramIndex] = convertedValue; args.preparedArguments[paramIndex] = convertedValue; } else { try { - Object originalValue = valueHolder.getValue(); Object convertedValue = converter.convertIfNecessary(originalValue, paramType, MethodParameter.forMethodOrConstructor(methodOrCtor, paramIndex)); + args.rawArguments[paramIndex] = + (mbd.isLenientConstructorResolution() ? originalValue : convertedValue); args.arguments[paramIndex] = convertedValue; - ConstructorArgumentValues.ValueHolder sourceHolder = - (ConstructorArgumentValues.ValueHolder) valueHolder.getSource(); - Object sourceValue = sourceHolder.getValue(); if (originalValue == sourceValue || sourceValue instanceof TypedStringValue) { // Either a converted value or still the original one: store converted value. sourceHolder.setConvertedValue(convertedValue); diff --git a/org.springframework.context/src/test/java/org/springframework/beans/factory/xml/XmlBeanFactoryTests-constructorArg.xml b/org.springframework.context/src/test/java/org/springframework/beans/factory/xml/XmlBeanFactoryTests-constructorArg.xml index c61eb498015..e12640a6faa 100644 --- a/org.springframework.context/src/test/java/org/springframework/beans/factory/xml/XmlBeanFactoryTests-constructorArg.xml +++ b/org.springframework.context/src/test/java/org/springframework/beans/factory/xml/XmlBeanFactoryTests-constructorArg.xml @@ -170,6 +170,11 @@ true + + false + true + + diff --git a/org.springframework.context/src/test/java/org/springframework/beans/factory/xml/XmlBeanFactoryTests.java b/org.springframework.context/src/test/java/org/springframework/beans/factory/xml/XmlBeanFactoryTests.java index 9162d47c8b9..633be540d6d 100644 --- a/org.springframework.context/src/test/java/org/springframework/beans/factory/xml/XmlBeanFactoryTests.java +++ b/org.springframework.context/src/test/java/org/springframework/beans/factory/xml/XmlBeanFactoryTests.java @@ -56,6 +56,7 @@ import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.MethodReplacer; 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.FileSystemResource; import org.springframework.core.io.UrlResource; @@ -1363,6 +1364,21 @@ public final class XmlBeanFactoryTests { 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() { XmlBeanFactory xbf = new XmlBeanFactory(CONSTRUCTOR_ARG_CONTEXT); ConstructorArrayTestBean bean = (ConstructorArrayTestBean) xbf.getBean("constructorArray"); @@ -1603,6 +1619,10 @@ public final class XmlBeanFactoryTests { this.boolean1 = b1; this.boolean2 = b2; } + + public DoubleBooleanConstructorBean(String s1, String s2) { + throw new IllegalStateException("Don't pick this constructor"); + } }