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

View File

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