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:
parent
8fbd2141b7
commit
903078a5b2
|
|
@ -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());
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue