Skip to content

Commit fd0a11b

Browse files
committed
Reuse Kotlin parameter names if possible
This commit detects a Kotlin constructor so that it is not required to transmit the parameter names information to the Java side. See gh-8762
1 parent b34b217 commit fd0a11b

File tree

1 file changed

+59
-29
lines changed

1 file changed

+59
-29
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/ConstructorParametersBinder.java

Lines changed: 59 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,16 @@
1919
import java.lang.reflect.Constructor;
2020
import java.lang.reflect.Modifier;
2121
import java.lang.reflect.Parameter;
22+
import java.lang.reflect.Type;
2223
import java.util.ArrayList;
23-
import java.util.Collections;
24-
import java.util.HashMap;
2524
import java.util.LinkedHashMap;
2625
import java.util.List;
2726
import java.util.Map;
2827

28+
import kotlin.reflect.KFunction;
29+
import kotlin.reflect.KParameter;
30+
import kotlin.reflect.jvm.ReflectJvmMapping;
31+
2932
import org.springframework.beans.BeanUtils;
3033
import org.springframework.boot.context.properties.ConfigurationPropertyDefaultValue;
3134
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
@@ -42,18 +45,6 @@ class ConstructorParametersBinder implements BeanBinder {
4245

4346
private static boolean KOTLIN_PRESENT = KotlinDetector.isKotlinPresent();
4447

45-
private static final Map<Class<?>, Object> DEFAULT_TYPE_VALUES;
46-
47-
static {
48-
Map<Class<?>, Object> values = new HashMap<>();
49-
values.put(boolean.class, false);
50-
values.put(byte.class, (byte) 0);
51-
values.put(short.class, (short) 0);
52-
values.put(int.class, 0);
53-
values.put(long.class, (long) 0);
54-
DEFAULT_TYPE_VALUES = Collections.unmodifiableMap(values);
55-
}
56-
5748
@Override
5849
@SuppressWarnings("unchecked")
5950
public <T> T bind(ConfigurationPropertyName name, Bindable<T> target,
@@ -72,14 +63,14 @@ private List<Object> bind(BeanPropertyBinder propertyBinder, Bean bean,
7263
for (ConstructorParameter parameter : bean.getParameters().values()) {
7364
Object bound = bind(parameter, propertyBinder);
7465
if (bound == null) {
75-
bound = getDefaultValue(parameter, bean, converter);
66+
bound = getDefaultValue(parameter, converter);
7667
}
7768
boundParams.add(bound);
7869
}
7970
return boundParams;
8071
}
8172

82-
private Object getDefaultValue(ConstructorParameter parameter, Bean bean,
73+
private Object getDefaultValue(ConstructorParameter parameter,
8374
BindConverter converter) {
8475
if (parameter.getDefaultValue() != null) {
8576
return converter.convert(parameter.getDefaultValue(), parameter.getType(),
@@ -88,7 +79,7 @@ private Object getDefaultValue(ConstructorParameter parameter, Bean bean,
8879
else {
8980
Class<?> resolve = parameter.getType().resolve();
9081
if (resolve != null && resolve.isPrimitive()) {
91-
return (bean.kotlinType) ? null : DEFAULT_TYPE_VALUES.get(resolve);
82+
return null;
9283
}
9384
}
9485
return null;
@@ -103,15 +94,12 @@ private Object bind(ConstructorParameter parameter,
10394

10495
private static final class Bean {
10596

106-
private final boolean kotlinType;
107-
10897
private final Constructor<?> constructor;
10998

11099
private final Map<String, ConstructorParameter> parameters;
111100

112-
private Bean(boolean kotlinType, Constructor<?> constructor,
101+
private Bean(Constructor<?> constructor,
113102
Map<String, ConstructorParameter> parameters) {
114-
this.kotlinType = kotlinType;
115103
this.constructor = constructor;
116104
this.parameters = parameters;
117105
}
@@ -129,20 +117,37 @@ public static Bean get(Bindable<?> bindable) {
129117
.findPrimaryConstructor(type);
130118
if (primaryConstructor != null
131119
&& primaryConstructor.getParameterCount() > 0) {
132-
return new Bean(true, primaryConstructor,
133-
parseParameters(primaryConstructor));
120+
return KotlinBeanProvider.get(primaryConstructor);
134121
}
135122
}
136123
else {
137124
Constructor<?>[] constructors = type.getDeclaredConstructors();
138125
if (constructors.length == 1 && constructors[0].getParameterCount() > 0) {
139-
Constructor<?> constructor = constructors[0];
140-
return new Bean(false, constructor, parseParameters(constructor));
126+
return SimpleBeanProvider.get(constructors[0]);
141127
}
142128
}
143129
return null;
144130
}
145131

132+
public Map<String, ConstructorParameter> getParameters() {
133+
return this.parameters;
134+
}
135+
136+
public Constructor<?> getConstructor() {
137+
return this.constructor;
138+
}
139+
140+
}
141+
142+
/**
143+
* A simple bean provider that uses `-parameters` to extract the parameter names.
144+
*/
145+
private static class SimpleBeanProvider {
146+
147+
public static Bean get(Constructor<?> constructor) {
148+
return new Bean(constructor, parseParameters(constructor));
149+
}
150+
146151
private static Map<String, ConstructorParameter> parseParameters(
147152
Constructor<?> constructor) {
148153
Map<String, ConstructorParameter> parameters = new LinkedHashMap<>();
@@ -160,12 +165,37 @@ private static Map<String, ConstructorParameter> parseParameters(
160165
return parameters;
161166
}
162167

163-
public Map<String, ConstructorParameter> getParameters() {
164-
return this.parameters;
168+
}
169+
170+
/**
171+
* A bean provider for a Kotlin class. Uses the Kotlin constructor to extract the
172+
* parameter names.
173+
*/
174+
private static class KotlinBeanProvider {
175+
176+
public static Bean get(Constructor<?> constructor) {
177+
KFunction<?> kotlinConstructor = ReflectJvmMapping
178+
.getKotlinFunction(constructor);
179+
if (kotlinConstructor != null) {
180+
return new Bean(constructor, parseParameters(kotlinConstructor));
181+
}
182+
else {
183+
return SimpleBeanProvider.get(constructor);
184+
}
165185
}
166186

167-
public Constructor<?> getConstructor() {
168-
return this.constructor;
187+
private static Map<String, ConstructorParameter> parseParameters(
188+
KFunction<?> constructor) {
189+
Map<String, ConstructorParameter> parameters = new LinkedHashMap<>();
190+
for (KParameter parameter : constructor.getParameters()) {
191+
String name = parameter.getName();
192+
Type type = ReflectJvmMapping.getJavaType(parameter.getType());
193+
Annotation[] annotations = parameter.getAnnotations()
194+
.toArray(new Annotation[0]);
195+
parameters.computeIfAbsent(name, (s) -> new ConstructorParameter(name,
196+
ResolvableType.forType(type), annotations, null));
197+
}
198+
return parameters;
169199
}
170200

171201
}

0 commit comments

Comments
 (0)