Do not silently return null when no constructor candidate is found

This commit updates ConstructorOrFactoryMethodResolver to throw an
exception if no constructor or factory method can be found for a given
bean definition.

This prevents code generation to happen on an incomplete view of the
bean to instantiate.

Closes gh-29052
This commit is contained in:
Stephane Nicoll 2022-09-06 15:22:57 +02:00
parent 8fbd2141b7
commit 903078a5b2
2 changed files with 15 additions and 16 deletions

View File

@ -18,7 +18,6 @@ package org.springframework.beans.factory.aot;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
@ -74,7 +73,6 @@ class ConstructorOrFactoryMethodResolver {
}
@Nullable
Executable resolve(BeanDefinition beanDefinition) {
Supplier<ResolvableType> beanType = () -> getBeanType(beanDefinition);
List<ResolvableType> valueTypes = (beanDefinition.hasConstructorArgumentValues() ?
@ -93,7 +91,13 @@ class ConstructorOrFactoryMethodResolver {
Assert.state(isCompatible, () -> String.format(
"Incompatible target type '%s' for factory bean '%s'",
resolvableType.toClass().getName(), factoryBeanClass.getName()));
return resolveConstructor(() -> ResolvableType.forClass(factoryBeanClass), valueTypes);
Executable executable = resolveConstructor(() -> ResolvableType.forClass(factoryBeanClass), valueTypes);
if (executable != null) {
return executable;
}
throw new IllegalStateException("No suitable FactoryBean constructor found for "
+ beanDefinition + " and argument types " + valueTypes);
}
Executable resolvedConstructor = resolveConstructor(beanType, valueTypes);
@ -101,13 +105,8 @@ class ConstructorOrFactoryMethodResolver {
return resolvedConstructor;
}
Field field = ReflectionUtils.findField(RootBeanDefinition.class, "resolvedConstructorOrFactoryMethod");
if (field != null) {
ReflectionUtils.makeAccessible(field);
return (Executable) ReflectionUtils.getField(field, beanDefinition);
}
return null;
throw new IllegalStateException("No constructor or factory method candidate found for "
+ beanDefinition + " and argument types " + valueTypes);
}
private List<ResolvableType> determineParameterValueTypes(
@ -390,7 +389,6 @@ class ConstructorOrFactoryMethodResolver {
}
}
@Nullable
static Executable resolve(RegisteredBean registeredBean) {
return new ConstructorOrFactoryMethodResolver(registeredBean.getBeanFactory())
.resolve(registeredBean.getMergedBeanDefinition());

View File

@ -200,8 +200,9 @@ class ConstructorOrFactoryMethodResolverTests {
BeanDefinition beanDefinition = BeanDefinitionBuilder
.rootBeanDefinition(MultiConstructorSample.class)
.addConstructorArgValue(Locale.ENGLISH).getBeanDefinition();
Executable executable = resolve(new DefaultListableBeanFactory(), beanDefinition);
assertThat(executable).isNull();
assertThatIllegalStateException().isThrownBy(() -> resolve(new DefaultListableBeanFactory(), beanDefinition))
.withMessageContaining(MultiConstructorSample.class.getName())
.withMessageContaining("and argument types [java.util.Locale]");
}
@Test
@ -212,8 +213,9 @@ class ConstructorOrFactoryMethodResolverTests {
.rootBeanDefinition(Locale.class, "getDefault")
.getBeanDefinition())
.getBeanDefinition();
Executable executable = resolve(new DefaultListableBeanFactory(), beanDefinition);
assertThat(executable).isNull();
assertThatIllegalStateException().isThrownBy(() -> resolve(new DefaultListableBeanFactory(), beanDefinition))
.withMessageContaining(MultiConstructorSample.class.getName())
.withMessageContaining("and argument types [java.util.Locale]");
}
@Test
@ -338,7 +340,6 @@ class ConstructorOrFactoryMethodResolverTests {
}
@Nullable
private Executable resolve(DefaultListableBeanFactory beanFactory, BeanDefinition beanDefinition) {
return new ConstructorOrFactoryMethodResolver(beanFactory).resolve(beanDefinition);
}