Browse Source

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
pull/29130/head
Stephane Nicoll 3 years ago
parent
commit
903078a5b2
  1. 20
      spring-beans/src/main/java/org/springframework/beans/factory/aot/ConstructorOrFactoryMethodResolver.java
  2. 11
      spring-beans/src/test/java/org/springframework/beans/factory/aot/ConstructorOrFactoryMethodResolverTests.java

20
spring-beans/src/main/java/org/springframework/beans/factory/aot/ConstructorOrFactoryMethodResolver.java

@ -18,7 +18,6 @@ package org.springframework.beans.factory.aot; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -390,7 +389,6 @@ class ConstructorOrFactoryMethodResolver {
}
}
@Nullable
static Executable resolve(RegisteredBean registeredBean) {
return new ConstructorOrFactoryMethodResolver(registeredBean.getBeanFactory())
.resolve(registeredBean.getMergedBeanDefinition());

11
spring-beans/src/test/java/org/springframework/beans/factory/aot/ConstructorOrFactoryMethodResolverTests.java

@ -200,8 +200,9 @@ class ConstructorOrFactoryMethodResolverTests { @@ -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 { @@ -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 { @@ -338,7 +340,6 @@ class ConstructorOrFactoryMethodResolverTests {
}
@Nullable
private Executable resolve(DefaultListableBeanFactory beanFactory, BeanDefinition beanDefinition) {
return new ConstructorOrFactoryMethodResolver(beanFactory).resolve(beanDefinition);
}

Loading…
Cancel
Save