Fail fast if config prop constructor binding cannot determine names

Previously, configuration property constructor binding relied on compilation
with -parameters to be able to discover the names of a constructor's
parameters, failing silently if the parameter names were not available. This
commit updates it to fail when the names could not be determined and switches
to using DefaultParameterNamesDiscoverer. This align configuration property
constructor binding with actuator endpoint operation invocation.

Closes gh-16928
pull/16984/head
Andy Wilkinson 6 years ago
parent b603cd5d4b
commit 6de14f71c6

@ -32,8 +32,11 @@ import kotlin.reflect.jvm.ReflectJvmMapping;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName; import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.KotlinDetector; import org.springframework.core.KotlinDetector;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.ResolvableType; import org.springframework.core.ResolvableType;
import org.springframework.util.Assert;
/** /**
* {@link BeanBinder} for constructor based binding. * {@link BeanBinder} for constructor based binding.
@ -164,10 +167,13 @@ class ConstructorParametersBinder implements BeanBinder {
} }
/** /**
* A simple bean provider that uses `-parameters` to extract the parameter names. * A simple bean provider that uses {@link DefaultParameterNameDiscoverer} to extract
* the parameter names.
*/ */
private static class SimpleBeanProvider { private static class SimpleBeanProvider {
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
public static Bean get(Class<?> type) { public static Bean get(Class<?> type) {
Constructor<?>[] constructors = type.getDeclaredConstructors(); Constructor<?>[] constructors = type.getDeclaredConstructors();
if (constructors.length == 1 && constructors[0].getParameterCount() > 0) { if (constructors.length == 1 && constructors[0].getParameterCount() > 0) {
@ -182,19 +188,25 @@ class ConstructorParametersBinder implements BeanBinder {
private static Map<String, ConstructorParameter> parseParameters( private static Map<String, ConstructorParameter> parseParameters(
Constructor<?> constructor) { Constructor<?> constructor) {
Map<String, ConstructorParameter> parameters = new LinkedHashMap<>(); String[] parameterNames = PARAMETER_NAME_DISCOVERER
for (Parameter parameter : constructor.getParameters()) { .getParameterNames(constructor);
String name = parameter.getName(); Assert.state(parameterNames != null,
() -> "Failed to extract parameter names for " + constructor);
Map<String, ConstructorParameter> parametersByName = new LinkedHashMap<>();
Parameter[] parameters = constructor.getParameters();
for (int i = 0; i < parameterNames.length; i++) {
String name = parameterNames[i];
Parameter parameter = parameters[i];
DefaultValue[] annotationsByType = parameter DefaultValue[] annotationsByType = parameter
.getAnnotationsByType(DefaultValue.class); .getAnnotationsByType(DefaultValue.class);
String[] defaultValue = (annotationsByType.length > 0) String[] defaultValue = (annotationsByType.length > 0)
? annotationsByType[0].value() : null; ? annotationsByType[0].value() : null;
parameters.computeIfAbsent(name, parametersByName.computeIfAbsent(name,
(key) -> new ConstructorParameter(name, (key) -> new ConstructorParameter(name,
ResolvableType.forClass(parameter.getType()), ResolvableType.forClass(parameter.getType()),
parameter.getDeclaredAnnotations(), defaultValue)); parameter.getDeclaredAnnotations(), defaultValue));
} }
return parameters; return parametersByName;
} }
} }

Loading…
Cancel
Save