Browse Source

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
pull/1889/merge
Sebastien Deleuze 8 years ago
parent
commit
f8f8d28f08
  1. 30
      spring-beans/src/main/java/org/springframework/beans/BeanUtils.java
  2. 3
      spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java
  3. 14
      spring-core/src/main/java/org/springframework/core/DefaultParameterNameDiscoverer.java
  4. 24
      spring-core/src/main/java/org/springframework/core/KotlinDetector.java
  5. 4
      spring-core/src/main/java/org/springframework/core/MethodParameter.java

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

@ -119,11 +119,15 @@ public abstract class BeanUtils { @@ -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 { @@ -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 { @@ -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 { @@ -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

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

@ -168,7 +168,8 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable @@ -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 {

14
spring-core/src/main/java/org/springframework/core/DefaultParameterNameDiscoverer.java

@ -16,17 +16,16 @@ @@ -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; @@ -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());

24
spring-core/src/main/java/org/springframework/core/KotlinDetector.java

@ -1,5 +1,5 @@ @@ -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; @@ -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; @@ -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 { @@ -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).

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

@ -342,7 +342,9 @@ public class MethodParameter { @@ -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)));
}
/**

Loading…
Cancel
Save