diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java index 2d7c5faab41c..8c378959594b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/ConstructorResolver.java @@ -294,7 +294,9 @@ else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) { } throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Could not resolve matching constructor on bean class [" + mbd.getBeanClassName() + "] " + - "(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)"); + "(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities. " + + "You should also check the consistency of arguments when mixing indexed and named arguments, " + + "especially in case of bean definition inheritance)"); } else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java index e33909e54e08..896bb574e9cc 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/DefaultListableBeanFactoryTests.java @@ -790,6 +790,32 @@ void canReferenceParentBeanFromChildViaAlias() { assertThat(mergedBeanDefinition1).as("Use cached merged bean definition").isSameAs(mergedBeanDefinition2); } + @Test + void hintAtPossibleDuplicateArgumentsInParentAndChildWhenMixingIndexAndNamed() { + final String EXPECTED_NAME = "Juergen"; + final int EXPECTED_AGE = 41; + + RootBeanDefinition parentDefinition = new RootBeanDefinition(TestBean.class); + parentDefinition.setAbstract(true); + parentDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, EXPECTED_NAME); + + ChildBeanDefinition childDefinition = new ChildBeanDefinition("parent"); + childDefinition.getConstructorArgumentValues().addGenericArgumentValue(new ConstructorArgumentValues.ValueHolder(EXPECTED_NAME, null, "name")); + childDefinition.getConstructorArgumentValues().addGenericArgumentValue(new ConstructorArgumentValues.ValueHolder(EXPECTED_AGE, null, "age")); + + DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); + factory.registerBeanDefinition("parent", parentDefinition); + factory.registerBeanDefinition("child", childDefinition); + + assertThatExceptionOfType(BeanCreationException.class) + .isThrownBy(() -> factory.getBean("child", TestBean.class)) + .withMessage("Error creating bean with name 'child': Could not resolve matching constructor on bean class " + + "[org.springframework.beans.testfixture.beans.TestBean] (hint: specify index/type/name arguments " + + "for simple parameters to avoid type ambiguities. " + + "You should also check the consistency of arguments when mixing indexed and named arguments, " + + "especially in case of bean definition inheritance)"); + } + @Test void getTypeWorksAfterParentChildMerging() { RootBeanDefinition parentDefinition = new RootBeanDefinition(TestBean.class);