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

@@ -0,0 +1,65 @@
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core;
import java.lang.annotation.Annotation;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
* A common delegate for detecting Kotlin's presence and for identifying Kotlin types.
*
* @author Juergen Hoeller
* @author Sebastien Deleuze
* @since 5.0
*/
@SuppressWarnings("unchecked")
public abstract class KotlinDetector {
@Nullable
private static final Class<? extends Annotation> kotlinMetadata;
static {
Class<?> metadata;
try {
metadata = ClassUtils.forName("kotlin.Metadata", KotlinDetector.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
// Kotlin API not available - no Kotlin support
metadata = null;
}
kotlinMetadata = (Class<? extends Annotation>) metadata;
}
/**
* Determine whether Kotlin is present in general.
*/
public static boolean isKotlinPresent() {
return (kotlinMetadata != null);
}
/**
* Determine whether the given {@code Class} is a Kotlin type
* (with Kotlin metadata present on it).
*/
public static boolean isKotlinType(Class<?> clazz) {
return (kotlinMetadata != null && clazz.getDeclaredAnnotation(kotlinMetadata) != null);
}
}

View File

@@ -16,7 +16,6 @@
package org.springframework.core;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.List;
@@ -27,7 +26,6 @@ import kotlin.reflect.KParameter;
import kotlin.reflect.jvm.ReflectJvmMapping;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
/**
* {@link ParameterNameDiscoverer} implementation which uses Kotlin's reflection facilities
@@ -41,27 +39,13 @@ import org.springframework.util.ClassUtils;
*/
public class KotlinReflectionParameterNameDiscoverer implements ParameterNameDiscoverer {
@Nullable
private static final Class<?> kotlinMetadata;
static {
Class<?> metadata;
try {
metadata = ClassUtils.forName("kotlin.Metadata", KotlinReflectionParameterNameDiscoverer.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
// Kotlin API not available - no special support for Kotlin class instantiation
metadata = null;
}
kotlinMetadata = metadata;
}
@Override
@Nullable
public String[] getParameterNames(Method method) {
if (!useKotlinSupport(method.getDeclaringClass())) {
if (!KotlinDetector.isKotlinType(method.getDeclaringClass())) {
return null;
}
try {
KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method);
return (function != null ? getParameterNames(function.getParameters()) : null);
@@ -74,9 +58,10 @@ public class KotlinReflectionParameterNameDiscoverer implements ParameterNameDis
@Override
@Nullable
public String[] getParameterNames(Constructor<?> ctor) {
if (!useKotlinSupport(ctor.getDeclaringClass())) {
if (!KotlinDetector.isKotlinType(ctor.getDeclaringClass())) {
return null;
}
try {
KFunction<?> function = ReflectJvmMapping.getKotlinFunction(ctor);
return (function != null ? getParameterNames(function.getParameters()) : null);
@@ -103,13 +88,4 @@ public class KotlinReflectionParameterNameDiscoverer implements ParameterNameDis
return parameterNames;
}
/**
* Return true if Kotlin is present and if the specified class is a Kotlin one.
*/
@SuppressWarnings("unchecked")
private static boolean useKotlinSupport(Class<?> clazz) {
return (kotlinMetadata != null &&
clazz.getDeclaredAnnotation((Class<? extends Annotation>) kotlinMetadata) != null);
}
}

View File

@@ -37,7 +37,6 @@ import kotlin.reflect.jvm.ReflectJvmMapping;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Helper class that encapsulates the specification of a method parameter, i.e. a {@link Method}
@@ -58,22 +57,6 @@ import org.springframework.util.ClassUtils;
*/
public class MethodParameter {
@Nullable
private static final Class<?> kotlinMetadata;
static {
Class<?> metadata;
try {
metadata = ClassUtils.forName("kotlin.Metadata", MethodParameter.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
// Kotlin API not available - no Kotlin support
metadata = null;
}
kotlinMetadata = metadata;
}
private final Executable executable;
private final int parameterIndex;
@@ -353,16 +336,7 @@ public class MethodParameter {
*/
public boolean isOptional() {
return (getParameterType() == Optional.class || hasNullableAnnotation() ||
(useKotlinSupport(this.getContainingClass()) && KotlinDelegate.isNullable(this)));
}
/**
* Return true if Kotlin is present and if the specified class is a Kotlin one.
*/
@SuppressWarnings("unchecked")
private static boolean useKotlinSupport(Class<?> clazz) {
return (kotlinMetadata != null &&
clazz.getDeclaredAnnotation((Class<? extends Annotation>) kotlinMetadata) != null);
(KotlinDetector.isKotlinType(getContainingClass()) && KotlinDelegate.isOptional(this)));
}
/**
@@ -754,9 +728,10 @@ public class MethodParameter {
private static class KotlinDelegate {
/**
* Check whether the specified {@link MethodParameter} represents a nullable Kotlin type or not.
* Check whether the specified {@link MethodParameter} represents a nullable Kotlin type
* or an optional parameter (with a default value in the Kotlin declaration).
*/
public static boolean isNullable(MethodParameter param) {
public static boolean isOptional(MethodParameter param) {
Method method = param.getMethod();
Constructor<?> ctor = param.getConstructor();
int index = param.getParameterIndex();
@@ -774,13 +749,12 @@ public class MethodParameter {
}
if (function != null) {
List<KParameter> parameters = function.getParameters();
return parameters
KParameter parameter = parameters
.stream()
.filter(p -> KParameter.Kind.VALUE.equals(p.getKind()))
.collect(Collectors.toList())
.get(index)
.getType()
.isMarkedNullable();
.get(index);
return (parameter.getType().isMarkedNullable() || parameter.isOptional());
}
}
return false;