Browse Source

Efficient Kotlin metadata detection

Follow-up of 3991ab4a23.

Issue: SPR-15673
pull/1503/head
Sebastien Deleuze 7 years ago
parent
commit
ab6430569d
  1. 18
      spring-beans/src/main/java/org/springframework/beans/BeanUtils.java
  2. 35
      spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java
  3. 82
      spring-core/src/main/java/org/springframework/core/MethodParameter.java

18
spring-beans/src/main/java/org/springframework/beans/BeanUtils.java

@ -71,16 +71,18 @@ public abstract class BeanUtils { @@ -71,16 +71,18 @@ public abstract class BeanUtils {
Collections.newSetFromMap(new ConcurrentReferenceHashMap<>(64));
@Nullable
private static Class<?> kotlinMetadata;
private static final Class<?> kotlinMetadata;
static {
Class<?> metadata;
try {
kotlinMetadata = ClassUtils.forName("kotlin.Metadata", BeanUtils.class.getClassLoader());
metadata = ClassUtils.forName("kotlin.Metadata", BeanUtils.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
// Kotlin API not available - no special support for Kotlin class instantiation
kotlinMetadata = null;
metadata = null;
}
kotlinMetadata = metadata;
}
@ -125,7 +127,7 @@ public abstract class BeanUtils { @@ -125,7 +127,7 @@ public abstract class BeanUtils {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
Constructor<T> ctor = (isKotlinClass(clazz) ?
Constructor<T> ctor = (useKotlinSupport(clazz) ?
KotlinDelegate.findPrimaryConstructor(clazz) : clazz.getDeclaredConstructor());
if (ctor == null) {
throw new BeanInstantiationException(clazz, "No default constructor found");
@ -172,7 +174,7 @@ public abstract class BeanUtils { @@ -172,7 +174,7 @@ public abstract class BeanUtils {
Assert.notNull(ctor, "Constructor must not be null");
try {
ReflectionUtils.makeAccessible(ctor);
return (isKotlinClass(ctor.getDeclaringClass()) ?
return (useKotlinSupport(ctor.getDeclaringClass()) ?
KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
}
catch (InstantiationException ex) {
@ -340,7 +342,7 @@ public abstract class BeanUtils { @@ -340,7 +342,7 @@ public abstract class BeanUtils {
@Nullable
public static <T> Constructor<T> findPrimaryConstructor(Class<T> clazz) {
Assert.notNull(clazz, "Class must not be null");
if (isKotlinClass(clazz)) {
if (useKotlinSupport(clazz)) {
return KotlinDelegate.findPrimaryConstructor(clazz);
}
else {
@ -707,10 +709,10 @@ public abstract class BeanUtils { @@ -707,10 +709,10 @@ public abstract class BeanUtils {
}
/**
* Return true if the specified class is a Kotlin one.
* Return true if Kotlin is present and if the specified class is a Kotlin one.
*/
@SuppressWarnings("unchecked")
private static boolean isKotlinClass(Class<?> clazz) {
private static boolean useKotlinSupport(Class<?> clazz) {
return (kotlinMetadata != null &&
clazz.getDeclaredAnnotation((Class<? extends Annotation>) kotlinMetadata) != null);
}

35
spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java

@ -26,7 +26,6 @@ import java.lang.reflect.Type; @@ -26,7 +26,6 @@ import java.lang.reflect.Type;
import java.util.Map;
import java.util.Optional;
import kotlin.Metadata;
import kotlin.reflect.KProperty;
import kotlin.reflect.jvm.ReflectJvmMapping;
@ -52,8 +51,20 @@ import org.springframework.util.ClassUtils; @@ -52,8 +51,20 @@ import org.springframework.util.ClassUtils;
@SuppressWarnings("serial")
public class DependencyDescriptor extends InjectionPoint implements Serializable {
private static final boolean kotlinPresent =
ClassUtils.isPresent("kotlin.Metadata", DependencyDescriptor.class.getClassLoader());
@Nullable
private static final Class<?> kotlinMetadata;
static {
Class<?> metadata;
try {
metadata = ClassUtils.forName("kotlin.Metadata", DependencyDescriptor.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
// Kotlin API not available - no Kotlin support
metadata = null;
}
kotlinMetadata = metadata;
}
private final Class<?> declaringClass;
@ -172,13 +183,22 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable @@ -172,13 +183,22 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
if (this.field != null) {
return !(this.field.getType() == Optional.class || hasNullableAnnotation() ||
(kotlinPresent && KotlinDelegate.isNullable(this.field)));
(useKotlinSupport(this.field.getDeclaringClass()) && KotlinDelegate.isNullable(this.field)));
}
else {
return !obtainMethodParameter().isOptional();
}
}
/**
* 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);
}
/**
* Check whether the underlying field is annotated with any variant of a
* {@code Nullable} annotation, e.g. {@code javax.annotation.Nullable} or
@ -435,11 +455,8 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable @@ -435,11 +455,8 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
* Check whether the specified {@link Field} represents a nullable Kotlin type or not.
*/
public static boolean isNullable(Field field) {
if (field.getDeclaringClass().isAnnotationPresent(Metadata.class)) {
KProperty<?> property = ReflectJvmMapping.getKotlinProperty(field);
return (property != null && property.getReturnType().isMarkedNullable());
}
return false;
KProperty<?> property = ReflectJvmMapping.getKotlinProperty(field);
return (property != null && property.getReturnType().isMarkedNullable());
}
}

82
spring-core/src/main/java/org/springframework/core/MethodParameter.java

@ -58,8 +58,20 @@ import org.springframework.util.ClassUtils; @@ -58,8 +58,20 @@ import org.springframework.util.ClassUtils;
*/
public class MethodParameter {
private static final boolean kotlinPresent =
ClassUtils.isPresent("kotlin.Metadata", MethodParameter.class.getClassLoader());
@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;
@ -341,7 +353,16 @@ public class MethodParameter { @@ -341,7 +353,16 @@ public class MethodParameter {
*/
public boolean isOptional() {
return (getParameterType() == Optional.class || hasNullableAnnotation() ||
(kotlinPresent && KotlinDelegate.isNullable(this)));
(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);
}
/**
@ -736,41 +757,30 @@ public class MethodParameter { @@ -736,41 +757,30 @@ public class MethodParameter {
* Check whether the specified {@link MethodParameter} represents a nullable Kotlin type or not.
*/
public static boolean isNullable(MethodParameter param) {
if (isKotlinClass(param.getContainingClass())) {
Method method = param.getMethod();
Constructor<?> ctor = param.getConstructor();
int index = param.getParameterIndex();
if (method != null && index == -1) {
KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method);
return (function != null && function.getReturnType().isMarkedNullable());
Method method = param.getMethod();
Constructor<?> ctor = param.getConstructor();
int index = param.getParameterIndex();
if (method != null && index == -1) {
KFunction<?> function = ReflectJvmMapping.getKotlinFunction(method);
return (function != null && function.getReturnType().isMarkedNullable());
}
else {
KFunction<?> function = null;
if (method != null) {
function = ReflectJvmMapping.getKotlinFunction(method);
}
else {
KFunction<?> function = null;
if (method != null) {
function = ReflectJvmMapping.getKotlinFunction(method);
}
else if (ctor != null) {
function = ReflectJvmMapping.getKotlinFunction(ctor);
}
if (function != null) {
List<KParameter> parameters = function.getParameters();
return parameters
.stream()
.filter(p -> KParameter.Kind.VALUE.equals(p.getKind()))
.collect(Collectors.toList())
.get(index)
.getType()
.isMarkedNullable();
}
else if (ctor != null) {
function = ReflectJvmMapping.getKotlinFunction(ctor);
}
}
return false;
}
private static boolean isKotlinClass(Class<?> clazz) {
for (Annotation annotation : clazz.getDeclaredAnnotations()) {
if (annotation.annotationType().getName().equals("kotlin.Metadata")) {
return true;
if (function != null) {
List<KParameter> parameters = function.getParameters();
return parameters
.stream()
.filter(p -> KParameter.Kind.VALUE.equals(p.getKind()))
.collect(Collectors.toList())
.get(index)
.getType()
.isMarkedNullable();
}
}
return false;

Loading…
Cancel
Save