diff --git a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java index 6d03c4e95e6..e1683e70866 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java @@ -70,9 +70,19 @@ public abstract class BeanUtils { private static final Set> unknownEditorTypes = Collections.newSetFromMap(new ConcurrentReferenceHashMap<>(64)); - private static final boolean kotlinPresent = - ClassUtils.isPresent("kotlin.Unit", BeanUtils.class.getClassLoader()); - + @Nullable + private static Class kotlinMetadata; + + static { + try { + kotlinMetadata = ClassUtils.forName("kotlin.Metadata", BeanUtils.class.getClassLoader()); + } + catch (ClassNotFoundException ex) { + // Kotlin API not available - no special support for Kotlin class instantiation + kotlinMetadata = null; + } + } + /** * Convenience method to instantiate a class using its no-arg constructor. @@ -115,7 +125,7 @@ public abstract class BeanUtils { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { - Constructor ctor = (kotlinPresent && isKotlinClass(clazz) ? + Constructor ctor = (isKotlinClass(clazz) ? KotlinDelegate.findPrimaryConstructor(clazz) : clazz.getDeclaredConstructor()); if (ctor == null) { throw new BeanInstantiationException(clazz, "No default constructor found"); @@ -152,8 +162,8 @@ public abstract class BeanUtils { * non-accessible (that is, non-public) constructor, and supports Kotlin classes * with optional parameters and default values. * @param ctor the constructor to instantiate - * @param args the constructor arguments to apply (use null for unspecified parameter - * if needed for Kotlin classes with optional parameters and default values) + * @param args the constructor arguments to apply (use {@code null} for an unspecified + * parameter if needed for Kotlin classes with optional parameters and default values) * @return the new instance * @throws BeanInstantiationException if the bean cannot be instantiated * @see Constructor#newInstance @@ -162,7 +172,8 @@ public abstract class BeanUtils { Assert.notNull(ctor, "Constructor must not be null"); try { ReflectionUtils.makeAccessible(ctor); - return (kotlinPresent && isKotlinClass(ctor.getDeclaringClass()) ? KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args)); + return (isKotlinClass(ctor.getDeclaringClass()) ? + KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args)); } catch (InstantiationException ex) { throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex); @@ -329,8 +340,7 @@ public abstract class BeanUtils { @Nullable public static Constructor findPrimaryConstructor(Class clazz) { Assert.notNull(clazz, "Class must not be null"); - Constructor ctor = null; - if (kotlinPresent && isKotlinClass(clazz)) { + if (isKotlinClass(clazz)) { return KotlinDelegate.findPrimaryConstructor(clazz); } else { @@ -699,13 +709,10 @@ public abstract class BeanUtils { /** * Return true if the specified class is a Kotlin one. */ + @SuppressWarnings("unchecked") private static boolean isKotlinClass(Class clazz) { - for (Annotation annotation : clazz.getDeclaredAnnotations()) { - if (annotation.annotationType().getName().equals("kotlin.Metadata")) { - return true; - } - } - return false; + return (kotlinMetadata != null && + clazz.getDeclaredAnnotation((Class) kotlinMetadata) != null); } @@ -717,7 +724,8 @@ public abstract class BeanUtils { /** * Return the Java constructor corresponding to the Kotlin primary constructor if any. * @param clazz the {@link Class} of the Kotlin class - * @see http://kotlinlang.org/docs/reference/classes.html#constructors + * @see + * http://kotlinlang.org/docs/reference/classes.html#constructors */ @Nullable public static Constructor findPrimaryConstructor(Class clazz) { @@ -726,7 +734,8 @@ public abstract class BeanUtils { return null; } Constructor constructor = ReflectJvmMapping.getJavaConstructor(primaryConstructor); - Assert.notNull(constructor, "Can't get the Java constructor corresponding to the Kotlin primary constructor of " + clazz.getName()); + Assert.notNull(constructor, + () -> "Failed to find Java constructor corresponding to Kotlin primary constructor: " + clazz.getName()); return constructor; } @@ -735,14 +744,16 @@ public abstract class BeanUtils { * @param ctor the constructor of the Kotlin class to instantiate * @param args the constructor arguments to apply (use null for unspecified parameter if needed) */ - public static T instantiateClass(Constructor ctor, Object... args) throws IllegalAccessException, InvocationTargetException, InstantiationException { + public static T instantiateClass(Constructor ctor, Object... args) + throws IllegalAccessException, InvocationTargetException, InstantiationException { + KFunction kotlinConstructor = ReflectJvmMapping.getKotlinFunction(ctor); if (kotlinConstructor == null) { return ctor.newInstance(args); } List parameters = kotlinConstructor.getParameters(); Map argParameters = new HashMap<>(parameters.size()); - Assert.isTrue(args.length <= parameters.size(), + Assert.isTrue(args.length <= parameters.size(), "The number of provided arguments should be less of equals than the number of constructor parameters"); for (int i = 0 ; i < args.length ; i++) { if (!(parameters.get(i).isOptional() && (args[i] == null))) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java index 4682f411420..16581caf425 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java @@ -53,7 +53,7 @@ import org.springframework.util.ClassUtils; public class DependencyDescriptor extends InjectionPoint implements Serializable { private static final boolean kotlinPresent = - ClassUtils.isPresent("kotlin.Unit", DependencyDescriptor.class.getClassLoader()); + ClassUtils.isPresent("kotlin.Metadata", DependencyDescriptor.class.getClassLoader()); private final Class declaringClass; diff --git a/spring-core/src/main/java/org/springframework/core/MethodParameter.java b/spring-core/src/main/java/org/springframework/core/MethodParameter.java index ad293a19af6..35fdb07d855 100644 --- a/spring-core/src/main/java/org/springframework/core/MethodParameter.java +++ b/spring-core/src/main/java/org/springframework/core/MethodParameter.java @@ -59,7 +59,7 @@ import org.springframework.util.ClassUtils; public class MethodParameter { private static final boolean kotlinPresent = - ClassUtils.isPresent("kotlin.Unit", MethodParameter.class.getClassLoader()); + ClassUtils.isPresent("kotlin.Metadata", MethodParameter.class.getClassLoader()); private final Executable executable; diff --git a/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java b/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java index 997ff919f7b..d0b2461e7fc 100644 --- a/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java +++ b/spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperBuilder.java @@ -772,7 +772,7 @@ public class Jackson2ObjectMapperBuilder { } // Kotlin present? - if (ClassUtils.isPresent("kotlin.Unit", this.moduleClassLoader)) { + if (ClassUtils.isPresent("kotlin.Metadata", this.moduleClassLoader)) { try { Class kotlinModule = (Class) ClassUtils.forName("com.fasterxml.jackson.module.kotlin.KotlinModule", this.moduleClassLoader);