Revised handling of missing data class arguments

Includes unified detection of Kotlin's optional parameters in MethodParameter.isOptional(), reduces BeanUtils.findPrimaryConstructor to Kotlin semantics (for reuse in AutowiredAnnotationBeanPostProcessor), and finally introduces a common KotlinDetector delegate with an isKotlinType(Class) check.

Issue: SPR-15877
Issue: SPR-16020
This commit is contained in:
Juergen Hoeller
2017-09-28 00:31:12 +02:00
parent d3129a8bd7
commit ec345bf162
14 changed files with 204 additions and 344 deletions

View File

@@ -57,6 +57,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.FatalBeanException;
import org.springframework.context.ApplicationContext;
import org.springframework.core.KotlinDetector;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@@ -762,7 +763,7 @@ public class Jackson2ObjectMapperBuilder {
}
// Kotlin present?
if (ClassUtils.isPresent("kotlin.Metadata", this.moduleClassLoader)) {
if (KotlinDetector.isKotlinPresent()) {
try {
Class<? extends Module> kotlinModule = (Class<? extends Module>)
ClassUtils.forName("com.fasterxml.jackson.module.kotlin.KotlinModule", this.moduleClassLoader);

View File

@@ -198,11 +198,22 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
WebDataBinderFactory binderFactory, NativeWebRequest webRequest) throws Exception {
MethodParameter nestedParameter = parameter.nestedIfOptional();
Class<?> type = nestedParameter.getNestedParameterType();
Class<?> clazz = nestedParameter.getNestedParameterType();
Constructor<?> ctor = BeanUtils.findPrimaryConstructor(type);
Constructor<?> ctor = BeanUtils.findPrimaryConstructor(clazz);
if (ctor == null) {
throw new IllegalStateException("No primary constructor found for " + type.getName());
Constructor<?>[] ctors = clazz.getConstructors();
if (ctors.length == 1) {
ctor = ctors[0];
}
else {
try {
ctor = clazz.getDeclaredConstructor();
}
catch (NoSuchMethodException ex) {
throw new IllegalStateException("No primary or default constructor found for " + clazz, ex);
}
}
}
Object attribute = constructAttribute(ctor, attributeName, binderFactory, webRequest);
@@ -263,8 +274,13 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol
}
}
try {
args[i] = (value != null ?
binder.convertIfNecessary(value, paramType, new MethodParameter(ctor, i)) : null);
MethodParameter methodParam = new MethodParameter(ctor, i);
if (value == null && methodParam.isOptional()) {
args[i] = (methodParam.getParameterType() == Optional.class ? Optional.empty() : null);
}
else {
args[i] = binder.convertIfNecessary(value, paramType, methodParam);
}
}
catch (TypeMismatchException ex) {
bindingFailure = true;