From 6de14f71c6ac27cea043b7a19e26812e5c915d71 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Tue, 28 May 2019 15:47:39 +0100 Subject: [PATCH] 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 --- .../bind/ConstructorParametersBinder.java | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ConstructorParametersBinder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ConstructorParametersBinder.java index 08d0b0eb0b..587fc94a12 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ConstructorParametersBinder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ConstructorParametersBinder.java @@ -32,8 +32,11 @@ import kotlin.reflect.jvm.ReflectJvmMapping; import org.springframework.beans.BeanUtils; import org.springframework.boot.context.properties.source.ConfigurationPropertyName; +import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.KotlinDetector; +import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.ResolvableType; +import org.springframework.util.Assert; /** * {@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 final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer(); + public static Bean get(Class type) { Constructor[] constructors = type.getDeclaredConstructors(); if (constructors.length == 1 && constructors[0].getParameterCount() > 0) { @@ -182,19 +188,25 @@ class ConstructorParametersBinder implements BeanBinder { private static Map parseParameters( Constructor constructor) { - Map parameters = new LinkedHashMap<>(); - for (Parameter parameter : constructor.getParameters()) { - String name = parameter.getName(); + String[] parameterNames = PARAMETER_NAME_DISCOVERER + .getParameterNames(constructor); + Assert.state(parameterNames != null, + () -> "Failed to extract parameter names for " + constructor); + Map 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 .getAnnotationsByType(DefaultValue.class); String[] defaultValue = (annotationsByType.length > 0) ? annotationsByType[0].value() : null; - parameters.computeIfAbsent(name, + parametersByName.computeIfAbsent(name, (key) -> new ConstructorParameter(name, ResolvableType.forClass(parameter.getType()), parameter.getDeclaredAnnotations(), defaultValue)); } - return parameters; + return parametersByName; } }