Support running Kotlin apps without kotlin-reflect

This commit includes an optimization of BeansUtils#instantiateClass
that favors Java reflection for default constructors before leveraging
Kotlin one for finding primary constructors and avoids Kotlin related
conditions when running in Java.

Issue: SPR-17069
This commit is contained in:
Sebastien Deleuze
2018-07-20 14:24:04 +02:00
parent bccff73e2b
commit f8f8d28f08
5 changed files with 41 additions and 34 deletions

View File

@@ -16,17 +16,16 @@
package org.springframework.core;
import org.springframework.util.ClassUtils;
/**
* Default implementation of the {@link ParameterNameDiscoverer} strategy interface,
* using the Java 8 standard reflection mechanism (if available), and falling back
* to the ASM-based {@link LocalVariableTableParameterNameDiscoverer} for checking
* debug information in the class file.
*
* <p>If Kotlin is present, {@link KotlinReflectionParameterNameDiscoverer} is added first
* in the list and used for Kotlin classes and interfaces. When compiling or running as
* a Graal native image, no {@link ParameterNameDiscoverer} is used.
* <p>If a Kotlin reflection implementation is present,
* {@link KotlinReflectionParameterNameDiscoverer} is added first in the list and used
* for Kotlin classes and interfaces. When compiling or running as a Graal native image,
* no {@link ParameterNameDiscoverer} is used.
*
* <p>Further discoverers may be added through {@link #addDiscoverer(ParameterNameDiscoverer)}.
*
@@ -39,16 +38,13 @@ import org.springframework.util.ClassUtils;
*/
public class DefaultParameterNameDiscoverer extends PrioritizedParameterNameDiscoverer {
private static final boolean kotlinPresent =
ClassUtils.isPresent("kotlin.Unit", DefaultParameterNameDiscoverer.class.getClassLoader());
// See https://github.com/oracle/graal/blob/master/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/ImageInfo.java
private static final boolean inImageCode = (System.getProperty("org.graalvm.nativeimage.imagecode") != null);
public DefaultParameterNameDiscoverer() {
if (!inImageCode) {
if (kotlinPresent) {
if (KotlinDetector.isKotlinReflectPresent()) {
addDiscoverer(new KotlinReflectionParameterNameDiscoverer());
}
addDiscoverer(new StandardReflectionParameterNameDiscoverer());

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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.
@@ -18,6 +18,9 @@ package org.springframework.core;
import java.lang.annotation.Annotation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
@@ -31,19 +34,28 @@ import org.springframework.util.ClassUtils;
@SuppressWarnings("unchecked")
public abstract class KotlinDetector {
private static final Log logger = LogFactory.getLog(KotlinDetector.class);
@Nullable
private static final Class<? extends Annotation> kotlinMetadata;
private static final boolean kotlinReflectPresent;
static {
Class<?> metadata;
ClassLoader classLoader = KotlinDetector.class.getClassLoader();
try {
metadata = ClassUtils.forName("kotlin.Metadata", KotlinDetector.class.getClassLoader());
metadata = ClassUtils.forName("kotlin.Metadata", classLoader);
}
catch (ClassNotFoundException ex) {
// Kotlin API not available - no Kotlin support
metadata = null;
}
kotlinMetadata = (Class<? extends Annotation>) metadata;
kotlinReflectPresent = ClassUtils.isPresent("kotlin.reflect.full.KClasses", classLoader);
if (kotlinMetadata != null && !kotlinReflectPresent) {
logger.info("Kotlin reflection implementation not found at runtime, related features won't be available.");
}
}
@@ -54,6 +66,14 @@ public abstract class KotlinDetector {
return (kotlinMetadata != null);
}
/**
* Determine whether Kotlin reflection is present.
* @since 5.1
*/
public static boolean isKotlinReflectPresent() {
return kotlinReflectPresent;
}
/**
* Determine whether the given {@code Class} is a Kotlin type
* (with Kotlin metadata present on it).

View File

@@ -342,7 +342,9 @@ public class MethodParameter {
*/
public boolean isOptional() {
return (getParameterType() == Optional.class || hasNullableAnnotation() ||
(KotlinDetector.isKotlinType(getContainingClass()) && KotlinDelegate.isOptional(this)));
(KotlinDetector.isKotlinReflectPresent() &&
KotlinDetector.isKotlinType(getContainingClass()) &&
KotlinDelegate.isOptional(this)));
}
/**