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 a33ed6589e..cfe37ae988 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 @@ -220,7 +220,8 @@ class ConstructorResolver { args = new ArgumentsHolder(explicitArgs); } - int typeDiffWeight = args.getTypeDifferenceWeight(paramTypes); + int typeDiffWeight = (mbd.isLenientConstructorResolution() ? + args.getTypeDifferenceWeight(paramTypes) : args.getAssignabilityWeight(paramTypes)); // Choose this constructor if it represents the closest match. if (typeDiffWeight < minTypeDiffWeight) { constructorToUse = candidate; @@ -436,8 +437,9 @@ class ConstructorResolver { args = new ArgumentsHolder(explicitArgs); } - int typeDiffWeight = args.getTypeDifferenceWeight(paramTypes); - // Choose this constructor if it represents the closest match. + int typeDiffWeight = (mbd.isLenientConstructorResolution() ? + args.getTypeDifferenceWeight(paramTypes) : args.getAssignabilityWeight(paramTypes)); + // Choose this factory method if it represents the closest match. if (typeDiffWeight < minTypeDiffWeight) { factoryMethodToUse = candidate; argsToUse = args.arguments; @@ -744,6 +746,20 @@ class ConstructorResolver { int rawTypeDiffWeight = MethodInvoker.getTypeDifferenceWeight(paramTypes, this.rawArguments) - 1024; return (rawTypeDiffWeight < typeDiffWeight ? rawTypeDiffWeight : typeDiffWeight); } + + public int getAssignabilityWeight(Class[] paramTypes) { + for (int i = 0; i < paramTypes.length; i++) { + if (!ClassUtils.isAssignableValue(paramTypes[i], this.arguments[i])) { + return Integer.MAX_VALUE; + } + } + for (int i = 0; i < paramTypes.length; i++) { + if (!ClassUtils.isAssignableValue(paramTypes[i], this.rawArguments[i])) { + return Integer.MAX_VALUE - 512; + } + } + return Integer.MAX_VALUE - 1024; + } } 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 1c05a0e6b0..af32261b91 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 @@ -180,6 +180,18 @@ true + + + + + + + + + + + + test 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 5a33d8c8e2..9f8e293bc0 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 @@ -1395,6 +1395,48 @@ public final class XmlBeanFactoryTests { } } + public @Test void testLenientDependencyMatching() { + XmlBeanFactory xbf = new XmlBeanFactory(CONSTRUCTOR_ARG_CONTEXT); + LenientDependencyTestBean bean = (LenientDependencyTestBean) xbf.getBean("lenientDependencyTestBean"); + assertTrue(bean.tb instanceof DerivedTestBean); + } + + public @Test void testLenientDependencyMatchingFactoryMethod() { + XmlBeanFactory xbf = new XmlBeanFactory(CONSTRUCTOR_ARG_CONTEXT); + LenientDependencyTestBean bean = (LenientDependencyTestBean) xbf.getBean("lenientDependencyTestBeanFactoryMethod"); + assertTrue(bean.tb instanceof DerivedTestBean); + } + + public @Test void testNonLenientDependencyMatching() { + XmlBeanFactory xbf = new XmlBeanFactory(CONSTRUCTOR_ARG_CONTEXT); + AbstractBeanDefinition bd = (AbstractBeanDefinition) xbf.getBeanDefinition("lenientDependencyTestBean"); + bd.setLenientConstructorResolution(false); + try { + xbf.getBean("lenientDependencyTestBean"); + fail("Should have thrown BeanCreationException"); + } + catch (BeanCreationException ex) { + // expected + ex.printStackTrace(); + assertTrue(ex.getMostSpecificCause().getMessage().contains("Ambiguous")); + } + } + + public @Test void testNonLenientDependencyMatchingFactoryMethod() { + XmlBeanFactory xbf = new XmlBeanFactory(CONSTRUCTOR_ARG_CONTEXT); + AbstractBeanDefinition bd = (AbstractBeanDefinition) xbf.getBeanDefinition("lenientDependencyTestBeanFactoryMethod"); + bd.setLenientConstructorResolution(false); + try { + xbf.getBean("lenientDependencyTestBeanFactoryMethod"); + fail("Should have thrown BeanCreationException"); + } + catch (BeanCreationException ex) { + // expected + ex.printStackTrace(); + assertTrue(ex.getMostSpecificCause().getMessage().contains("Ambiguous")); + } + } + public @Test void testStringConstructor() { XmlBeanFactory xbf = new XmlBeanFactory(CONSTRUCTOR_ARG_CONTEXT); AbstractBeanDefinition bd = (AbstractBeanDefinition) xbf.getBeanDefinition("string"); @@ -1425,7 +1467,7 @@ public final class XmlBeanFactoryTests { fail("Duplicate name not detected"); } catch (BeansException ex) { - assertTrue(ex.getMessage().indexOf("Bean name 'foo'") > -1); + assertTrue(ex.getMessage().contains("Bean name 'foo'")); } } @@ -1435,7 +1477,7 @@ public final class XmlBeanFactoryTests { fail("Duplicate name not detected"); } catch (BeansException e) { - assertTrue(e.getMessage().indexOf("Bean name 'foo'") > -1); + assertTrue(e.getMessage().contains("Bean name 'foo'")); } } @@ -1658,6 +1700,40 @@ public final class XmlBeanFactoryTests { } + public static class LenientDependencyTestBean { + + public final ITestBean tb; + + public LenientDependencyTestBean(ITestBean tb) { + this.tb = tb; + } + + public LenientDependencyTestBean(TestBean tb) { + this.tb = tb; + } + + public LenientDependencyTestBean(DerivedTestBean tb) { + this.tb = tb; + } + + public LenientDependencyTestBean(Map[] m) { + throw new IllegalStateException("Don't pick this constructor"); + } + + public static LenientDependencyTestBean create(ITestBean tb) { + return new LenientDependencyTestBean(tb); + } + + public static LenientDependencyTestBean create(TestBean tb) { + return new LenientDependencyTestBean(tb); + } + + public static LenientDependencyTestBean create(DerivedTestBean tb) { + return new LenientDependencyTestBean(tb); + } + } + + public static class ConstructorArrayTestBean { public final Object array;