incorrectly invoked factory methods now result in exceptions with more descriptive messages (SPR-5475)
This commit is contained in:
parent
cbed1c1b4b
commit
351e72b6e2
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package org.springframework.beans.factory.support;
|
package org.springframework.beans.factory.support;
|
||||||
|
|
||||||
|
import static org.springframework.util.StringUtils.collectionToCommaDelimitedString;
|
||||||
|
|
||||||
import java.beans.ConstructorProperties;
|
import java.beans.ConstructorProperties;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Member;
|
import java.lang.reflect.Member;
|
||||||
|
|
@ -44,6 +46,7 @@ import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
||||||
import org.springframework.beans.factory.config.ConstructorArgumentValues;
|
import org.springframework.beans.factory.config.ConstructorArgumentValues;
|
||||||
import org.springframework.beans.factory.config.DependencyDescriptor;
|
import org.springframework.beans.factory.config.DependencyDescriptor;
|
||||||
import org.springframework.beans.factory.config.TypedStringValue;
|
import org.springframework.beans.factory.config.TypedStringValue;
|
||||||
|
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
|
||||||
import org.springframework.core.GenericTypeResolver;
|
import org.springframework.core.GenericTypeResolver;
|
||||||
import org.springframework.core.MethodParameter;
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.ParameterNameDiscoverer;
|
import org.springframework.core.ParameterNameDiscoverer;
|
||||||
|
|
@ -51,6 +54,7 @@ import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.MethodInvoker;
|
import org.springframework.util.MethodInvoker;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class for resolving constructors and factory methods.
|
* Helper class for resolving constructors and factory methods.
|
||||||
|
|
@ -497,12 +501,25 @@ class ConstructorResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (factoryMethodToUse == null) {
|
if (factoryMethodToUse == null) {
|
||||||
|
boolean hasArgs = resolvedValues.getArgumentCount() > 0;
|
||||||
|
String argDesc = "";
|
||||||
|
if (hasArgs) {
|
||||||
|
List<String> argTypes = new ArrayList<String>();
|
||||||
|
for (ValueHolder value : resolvedValues.getIndexedArgumentValues().values()) {
|
||||||
|
String argType = value.getType() != null ?
|
||||||
|
ClassUtils.getShortName(value.getType()) : value.getValue().getClass().getSimpleName();
|
||||||
|
argTypes.add(argType);
|
||||||
|
}
|
||||||
|
argDesc = StringUtils.collectionToCommaDelimitedString(argTypes);
|
||||||
|
}
|
||||||
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
|
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
|
||||||
"No matching factory method found: " +
|
"No matching factory method found: " +
|
||||||
(mbd.getFactoryBeanName() != null ?
|
(mbd.getFactoryBeanName() != null ?
|
||||||
"factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +
|
"factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +
|
||||||
"factory method '" + mbd.getFactoryMethodName() + "'. " +
|
"factory method '" + mbd.getFactoryMethodName() + "(" + argDesc + ")'. " +
|
||||||
"Check that a method of the specified name exists and that it is " +
|
"Check that a method with the specified name " +
|
||||||
|
(hasArgs ? "and arguments " : "") +
|
||||||
|
"exists and that it is " +
|
||||||
(isStatic ? "static" : "non-static") + ".");
|
(isStatic ? "static" : "non-static") + ".");
|
||||||
}
|
}
|
||||||
else if (void.class.equals(factoryMethodToUse.getReturnType())) {
|
else if (void.class.equals(factoryMethodToUse.getReturnType())) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
package org.springframework.beans.factory;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
|
import org.springframework.beans.factory.config.ConstructorArgumentValues;
|
||||||
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
|
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SPR-5475 exposed the fact that the error message displayed when incorrectly
|
||||||
|
* invoking a factory method is not instructive to the user and rather misleading.
|
||||||
|
*
|
||||||
|
* @author Chris Beams
|
||||||
|
*/
|
||||||
|
public class Spr5475Tests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void noArgFactoryMethodInvokedWithOneArg() {
|
||||||
|
assertExceptionMessageForMisconfiguredFactoryMethod(
|
||||||
|
rootBeanDefinition(Foo.class)
|
||||||
|
.setFactoryMethod("noArgFactory")
|
||||||
|
.addConstructorArgValue("bogusArg").getBeanDefinition(),
|
||||||
|
"Error creating bean with name 'foo': No matching factory method found: factory method 'noArgFactory(String)'. " +
|
||||||
|
"Check that a method with the specified name and arguments exists and that it is static.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void noArgFactoryMethodInvokedWithTwoArgs() {
|
||||||
|
assertExceptionMessageForMisconfiguredFactoryMethod(
|
||||||
|
rootBeanDefinition(Foo.class)
|
||||||
|
.setFactoryMethod("noArgFactory")
|
||||||
|
.addConstructorArgValue("bogusArg1")
|
||||||
|
.addConstructorArgValue("bogusArg2".getBytes()).getBeanDefinition(),
|
||||||
|
"Error creating bean with name 'foo': No matching factory method found: factory method 'noArgFactory(String,byte[])'. " +
|
||||||
|
"Check that a method with the specified name and arguments exists and that it is static.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void noArgFactoryMethodInvokedWithTwoArgsAndTypesSpecified() {
|
||||||
|
RootBeanDefinition def = new RootBeanDefinition(Foo.class);
|
||||||
|
def.setFactoryMethodName("noArgFactory");
|
||||||
|
ConstructorArgumentValues cav = new ConstructorArgumentValues();
|
||||||
|
cav.addIndexedArgumentValue(0, "bogusArg1", CharSequence.class.getName());
|
||||||
|
cav.addIndexedArgumentValue(1, "bogusArg2".getBytes());
|
||||||
|
def.setConstructorArgumentValues(cav);
|
||||||
|
|
||||||
|
assertExceptionMessageForMisconfiguredFactoryMethod(
|
||||||
|
def,
|
||||||
|
"Error creating bean with name 'foo': No matching factory method found: factory method 'noArgFactory(CharSequence,byte[])'. " +
|
||||||
|
"Check that a method with the specified name and arguments exists and that it is static.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertExceptionMessageForMisconfiguredFactoryMethod(BeanDefinition bd, String expectedMessage) {
|
||||||
|
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
|
||||||
|
factory.registerBeanDefinition("foo", bd);
|
||||||
|
|
||||||
|
try {
|
||||||
|
factory.preInstantiateSingletons();
|
||||||
|
fail("should have failed with BeanCreationException due to incorrectly invoked factory method");
|
||||||
|
} catch (BeanCreationException ex) {
|
||||||
|
assertThat(ex.getMessage(), equalTo(expectedMessage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void singleArgFactoryMethodInvokedWithNoArgs() {
|
||||||
|
// calling a factory method that accepts arguments without any arguments emits an exception unlike cases
|
||||||
|
// where a no-arg factory method is called with arguments. Adding this test just to document the difference
|
||||||
|
assertExceptionMessageForMisconfiguredFactoryMethod(
|
||||||
|
rootBeanDefinition(Foo.class)
|
||||||
|
.setFactoryMethod("singleArgFactory").getBeanDefinition(),
|
||||||
|
"Error creating bean with name 'foo': " +
|
||||||
|
"Unsatisfied dependency expressed through constructor argument with index 0 of type [java.lang.String]: " +
|
||||||
|
"Ambiguous factory method argument types - did you specify the correct bean references as factory method arguments?");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static class Foo {
|
||||||
|
static Foo noArgFactory() {
|
||||||
|
return new Foo();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Foo singleArgFactory(String arg) {
|
||||||
|
return new Foo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue