|
|
|
|
@@ -19,6 +19,7 @@ package org.springframework.boot.context.properties.bind;
|
|
|
|
|
import java.lang.annotation.Annotation;
|
|
|
|
|
import java.lang.reflect.Array;
|
|
|
|
|
import java.lang.reflect.Constructor;
|
|
|
|
|
import java.lang.reflect.Method;
|
|
|
|
|
import java.lang.reflect.Modifier;
|
|
|
|
|
import java.lang.reflect.Parameter;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
@@ -27,6 +28,7 @@ import java.util.Collections;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.Optional;
|
|
|
|
|
import java.util.function.Consumer;
|
|
|
|
|
|
|
|
|
|
import kotlin.reflect.KFunction;
|
|
|
|
|
import kotlin.reflect.KParameter;
|
|
|
|
|
@@ -35,6 +37,7 @@ import org.apache.commons.logging.Log;
|
|
|
|
|
import org.apache.commons.logging.LogFactory;
|
|
|
|
|
|
|
|
|
|
import org.springframework.beans.BeanUtils;
|
|
|
|
|
import org.springframework.boot.context.properties.bind.Binder.Context;
|
|
|
|
|
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
|
|
|
|
|
import org.springframework.core.CollectionFactory;
|
|
|
|
|
import org.springframework.core.DefaultParameterNameDiscoverer;
|
|
|
|
|
@@ -69,7 +72,7 @@ class ValueObjectBinder implements DataObjectBinder {
|
|
|
|
|
@Override
|
|
|
|
|
public <T> T bind(ConfigurationPropertyName name, Bindable<T> target, Binder.Context context,
|
|
|
|
|
DataObjectPropertyBinder propertyBinder) {
|
|
|
|
|
ValueObject<T> valueObject = ValueObject.get(target, this.constructorProvider, context);
|
|
|
|
|
ValueObject<T> valueObject = ValueObject.get(target, this.constructorProvider, context, Discoverer.LENIENT);
|
|
|
|
|
if (valueObject == null) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
@@ -90,7 +93,7 @@ class ValueObjectBinder implements DataObjectBinder {
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public <T> T create(Bindable<T> target, Binder.Context context) {
|
|
|
|
|
ValueObject<T> valueObject = ValueObject.get(target, this.constructorProvider, context);
|
|
|
|
|
ValueObject<T> valueObject = ValueObject.get(target, this.constructorProvider, context, Discoverer.LENIENT);
|
|
|
|
|
if (valueObject == null) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
@@ -102,6 +105,16 @@ class ValueObjectBinder implements DataObjectBinder {
|
|
|
|
|
return valueObject.instantiate(args);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public <T> void onUnableToCreateInstance(Bindable<T> target, Context context, RuntimeException exception) {
|
|
|
|
|
try {
|
|
|
|
|
ValueObject.get(target, this.constructorProvider, context, Discoverer.STRICT);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex) {
|
|
|
|
|
exception.addSuppressed(ex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private <T> T getDefaultValue(Binder.Context context, ConstructorParameter parameter) {
|
|
|
|
|
ResolvableType type = parameter.getType();
|
|
|
|
|
Annotation[] annotations = parameter.getAnnotations();
|
|
|
|
|
@@ -187,7 +200,7 @@ class ValueObjectBinder implements DataObjectBinder {
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
|
static <T> ValueObject<T> get(Bindable<T> bindable, BindConstructorProvider constructorProvider,
|
|
|
|
|
Binder.Context context) {
|
|
|
|
|
Binder.Context context, ParameterNameDiscoverer parameterNameDiscoverer) {
|
|
|
|
|
Class<T> type = (Class<T>) bindable.getType().resolve();
|
|
|
|
|
if (type == null || type.isEnum() || Modifier.isAbstract(type.getModifiers())) {
|
|
|
|
|
return null;
|
|
|
|
|
@@ -198,9 +211,10 @@ class ValueObjectBinder implements DataObjectBinder {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
if (KotlinDetector.isKotlinType(type)) {
|
|
|
|
|
return KotlinValueObject.get((Constructor<T>) bindConstructor, bindable.getType());
|
|
|
|
|
return KotlinValueObject.get((Constructor<T>) bindConstructor, bindable.getType(),
|
|
|
|
|
parameterNameDiscoverer);
|
|
|
|
|
}
|
|
|
|
|
return DefaultValueObject.get(bindConstructor, bindable.getType());
|
|
|
|
|
return DefaultValueObject.get(bindConstructor, bindable.getType(), parameterNameDiscoverer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
@@ -246,12 +260,13 @@ class ValueObjectBinder implements DataObjectBinder {
|
|
|
|
|
return this.constructorParameters;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static <T> ValueObject<T> get(Constructor<T> bindConstructor, ResolvableType type) {
|
|
|
|
|
static <T> ValueObject<T> get(Constructor<T> bindConstructor, ResolvableType type,
|
|
|
|
|
ParameterNameDiscoverer parameterNameDiscoverer) {
|
|
|
|
|
KFunction<T> kotlinConstructor = ReflectJvmMapping.getKotlinFunction(bindConstructor);
|
|
|
|
|
if (kotlinConstructor != null) {
|
|
|
|
|
return new KotlinValueObject<>(bindConstructor, kotlinConstructor, type);
|
|
|
|
|
}
|
|
|
|
|
return DefaultValueObject.get(bindConstructor, type);
|
|
|
|
|
return DefaultValueObject.get(bindConstructor, type, parameterNameDiscoverer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
@@ -262,8 +277,6 @@ class ValueObjectBinder implements DataObjectBinder {
|
|
|
|
|
*/
|
|
|
|
|
private static final class DefaultValueObject<T> extends ValueObject<T> {
|
|
|
|
|
|
|
|
|
|
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
|
|
|
|
|
|
|
|
|
|
private final List<ConstructorParameter> constructorParameters;
|
|
|
|
|
|
|
|
|
|
private DefaultValueObject(Constructor<T> constructor, List<ConstructorParameter> constructorParameters) {
|
|
|
|
|
@@ -277,12 +290,10 @@ class ValueObjectBinder implements DataObjectBinder {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
|
static <T> ValueObject<T> get(Constructor<?> bindConstructor, ResolvableType type) {
|
|
|
|
|
String[] names = PARAMETER_NAME_DISCOVERER.getParameterNames(bindConstructor);
|
|
|
|
|
static <T> ValueObject<T> get(Constructor<?> bindConstructor, ResolvableType type,
|
|
|
|
|
ParameterNameDiscoverer parameterNameDiscoverer) {
|
|
|
|
|
String[] names = parameterNameDiscoverer.getParameterNames(bindConstructor);
|
|
|
|
|
if (names == null) {
|
|
|
|
|
logger.debug(LogMessage.format(
|
|
|
|
|
"Unable to use value object binding with %s as parameter names cannot be discovered",
|
|
|
|
|
bindConstructor));
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
List<ConstructorParameter> constructorParameters = parseConstructorParameters(bindConstructor, type, names);
|
|
|
|
|
@@ -339,4 +350,49 @@ class ValueObjectBinder implements DataObjectBinder {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* {@link ParameterNameDiscoverer} used for value data object binding.
|
|
|
|
|
*/
|
|
|
|
|
static final class Discoverer implements ParameterNameDiscoverer {
|
|
|
|
|
|
|
|
|
|
private static final ParameterNameDiscoverer DEFAULT_DELEGATE = new DefaultParameterNameDiscoverer();
|
|
|
|
|
|
|
|
|
|
private static final ParameterNameDiscoverer LENIENT = new Discoverer(DEFAULT_DELEGATE, (message) -> {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
private static final ParameterNameDiscoverer STRICT = new Discoverer(DEFAULT_DELEGATE, (message) -> {
|
|
|
|
|
throw new IllegalStateException(message.toString());
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
private final ParameterNameDiscoverer delegate;
|
|
|
|
|
|
|
|
|
|
private final Consumer<LogMessage> noParameterNamesHandler;
|
|
|
|
|
|
|
|
|
|
private Discoverer(ParameterNameDiscoverer delegate, Consumer<LogMessage> noParameterNamesHandler) {
|
|
|
|
|
this.delegate = delegate;
|
|
|
|
|
this.noParameterNamesHandler = noParameterNamesHandler;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public String[] getParameterNames(Method method) {
|
|
|
|
|
throw new UnsupportedOperationException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public String[] getParameterNames(Constructor<?> constructor) {
|
|
|
|
|
String[] names = this.delegate.getParameterNames(constructor);
|
|
|
|
|
if (names != null) {
|
|
|
|
|
return names;
|
|
|
|
|
}
|
|
|
|
|
LogMessage message = LogMessage.format(
|
|
|
|
|
"Unable to use value object binding with constructor [%s] as parameter names cannot be discovered. "
|
|
|
|
|
+ "Ensure that the compiler uses the '-parameters' flag",
|
|
|
|
|
constructor);
|
|
|
|
|
this.noParameterNamesHandler.accept(message);
|
|
|
|
|
logger.debug(message);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|