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:
@@ -119,11 +119,15 @@ public abstract class BeanUtils {
|
||||
throw new BeanInstantiationException(clazz, "Specified class is an interface");
|
||||
}
|
||||
try {
|
||||
Constructor<T> ctor = (KotlinDetector.isKotlinType(clazz) ?
|
||||
KotlinDelegate.getPrimaryConstructor(clazz) : clazz.getDeclaredConstructor());
|
||||
return instantiateClass(ctor);
|
||||
return instantiateClass(clazz.getDeclaredConstructor());
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(clazz)) {
|
||||
Constructor<T> ctor = findPrimaryConstructor(clazz);
|
||||
if (ctor != null) {
|
||||
return instantiateClass(ctor);
|
||||
}
|
||||
}
|
||||
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
|
||||
}
|
||||
catch (LinkageError err) {
|
||||
@@ -166,7 +170,7 @@ public abstract class BeanUtils {
|
||||
Assert.notNull(ctor, "Constructor must not be null");
|
||||
try {
|
||||
ReflectionUtils.makeAccessible(ctor);
|
||||
return (KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
|
||||
return (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
|
||||
KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
|
||||
}
|
||||
catch (InstantiationException ex) {
|
||||
@@ -196,7 +200,7 @@ public abstract class BeanUtils {
|
||||
@Nullable
|
||||
public static <T> Constructor<T> findPrimaryConstructor(Class<T> clazz) {
|
||||
Assert.notNull(clazz, "Class must not be null");
|
||||
if (KotlinDetector.isKotlinType(clazz)) {
|
||||
if (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(clazz)) {
|
||||
Constructor<T> kotlinPrimaryConstructor = KotlinDelegate.findPrimaryConstructor(clazz);
|
||||
if (kotlinPrimaryConstructor != null) {
|
||||
return kotlinPrimaryConstructor;
|
||||
@@ -700,22 +704,6 @@ public abstract class BeanUtils {
|
||||
*/
|
||||
private static class KotlinDelegate {
|
||||
|
||||
/**
|
||||
* Determine the Java constructor corresponding to the Kotlin primary constructor.
|
||||
* @param clazz the {@link Class} of the Kotlin class
|
||||
* @throws NoSuchMethodException if no such constructor found
|
||||
* @since 5.0.3
|
||||
* @see #findPrimaryConstructor
|
||||
* @see Class#getDeclaredConstructor
|
||||
*/
|
||||
public static <T> Constructor<T> getPrimaryConstructor(Class<T> clazz) throws NoSuchMethodException {
|
||||
Constructor<T> ctor = findPrimaryConstructor(clazz);
|
||||
if (ctor == null) {
|
||||
throw new NoSuchMethodException();
|
||||
}
|
||||
return ctor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the Java constructor corresponding to the Kotlin primary constructor, if any.
|
||||
* @param clazz the {@link Class} of the Kotlin class
|
||||
|
||||
@@ -168,7 +168,8 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
|
||||
|
||||
if (this.field != null) {
|
||||
return !(this.field.getType() == Optional.class || hasNullableAnnotation() ||
|
||||
(KotlinDetector.isKotlinType(this.field.getDeclaringClass()) &&
|
||||
(KotlinDetector.isKotlinReflectPresent() &&
|
||||
KotlinDetector.isKotlinType(this.field.getDeclaringClass()) &&
|
||||
KotlinDelegate.isNullable(this.field)));
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user