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 5fdc92ac39..448b8bc3c3 100644
--- a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java
+++ b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java
@@ -31,8 +31,11 @@ import java.util.Map;
import java.util.Set;
import kotlin.jvm.JvmClassMappingKt;
+import kotlin.jvm.JvmInline;
+import kotlin.reflect.KClass;
import kotlin.reflect.KFunction;
import kotlin.reflect.KParameter;
+import kotlin.reflect.full.KAnnotatedElements;
import kotlin.reflect.full.KClasses;
import kotlin.reflect.jvm.KCallablesJvm;
import kotlin.reflect.jvm.ReflectJvmMapping;
@@ -835,13 +838,22 @@ public abstract class BeanUtils {
* @see
* https://kotlinlang.org/docs/reference/classes.html#constructors
*/
+ @SuppressWarnings("unchecked")
@Nullable
public static Constructor findPrimaryConstructor(Class clazz) {
try {
- KFunction primaryCtor = KClasses.getPrimaryConstructor(JvmClassMappingKt.getKotlinClass(clazz));
+ KClass kClass = JvmClassMappingKt.getKotlinClass(clazz);
+ KFunction primaryCtor = KClasses.getPrimaryConstructor(kClass);
if (primaryCtor == null) {
return null;
}
+ if (kClass.isValue() && !KAnnotatedElements
+ .findAnnotations(kClass, JvmClassMappingKt.getKotlinClass(JvmInline.class)).isEmpty()) {
+ Constructor>[] constructors = clazz.getDeclaredConstructors();
+ Assert.state(constructors.length == 1,
+ "Kotlin value classes annotated with @JvmInline are expected to have a single JVM constructor");
+ return (Constructor) constructors[0];
+ }
Constructor constructor = ReflectJvmMapping.getJavaConstructor(primaryCtor);
if (constructor == null) {
throw new IllegalStateException(
diff --git a/spring-beans/src/test/kotlin/org/springframework/beans/BeanUtilsKotlinTests.kt b/spring-beans/src/test/kotlin/org/springframework/beans/BeanUtilsKotlinTests.kt
index 40ba3cdbf5..ea901077c0 100644
--- a/spring-beans/src/test/kotlin/org/springframework/beans/BeanUtilsKotlinTests.kt
+++ b/spring-beans/src/test/kotlin/org/springframework/beans/BeanUtilsKotlinTests.kt
@@ -90,6 +90,45 @@ class BeanUtilsKotlinTests {
BeanUtils.instantiateClass(PrivateClass::class.java.getDeclaredConstructor())
}
+ @Test
+ fun `Instantiate value class`() {
+ val constructor = BeanUtils.findPrimaryConstructor(ValueClass::class.java)!!
+ assertThat(constructor).isNotNull()
+ val value = "Hello value class!"
+ val instance = BeanUtils.instantiateClass(constructor, value)
+ assertThat(instance).isEqualTo(ValueClass(value))
+ }
+
+ @Test
+ fun `Instantiate value class with multiple constructors`() {
+ val constructor = BeanUtils.findPrimaryConstructor(ValueClassWithMultipleConstructors::class.java)!!
+ assertThat(constructor).isNotNull()
+ val value = "Hello value class!"
+ val instance = BeanUtils.instantiateClass(constructor, value)
+ assertThat(instance).isEqualTo(ValueClassWithMultipleConstructors(value))
+ }
+
+ @Test
+ fun `Instantiate class with value class parameter`() {
+ val constructor = BeanUtils.findPrimaryConstructor(OneConstructorWithValueClass::class.java)!!
+ assertThat(constructor).isNotNull()
+ val value = ValueClass("Hello value class!")
+ val instance = BeanUtils.instantiateClass(constructor, value)
+ assertThat(instance).isEqualTo(OneConstructorWithValueClass(value))
+ }
+
+ @Test
+ fun `Instantiate class with nullable value class parameter`() {
+ val constructor = BeanUtils.findPrimaryConstructor(OneConstructorWithNullableValueClass::class.java)!!
+ assertThat(constructor).isNotNull()
+ val value = ValueClass("Hello value class!")
+ var instance = BeanUtils.instantiateClass(constructor, value)
+ assertThat(instance).isEqualTo(OneConstructorWithNullableValueClass(value))
+ instance = BeanUtils.instantiateClass(constructor, null)
+ assertThat(instance).isEqualTo(OneConstructorWithNullableValueClass(null))
+ }
+
+
class Foo(val param1: String, val param2: Int)
class Bar(val param1: String, val param2: Int = 12)
@@ -128,4 +167,17 @@ class BeanUtilsKotlinTests {
private class PrivateClass
+ @JvmInline
+ value class ValueClass(private val value: String)
+
+ @JvmInline
+ value class ValueClassWithMultipleConstructors(private val value: String) {
+ constructor() : this("Fail")
+ constructor(part1: String, part2: String) : this("Fail")
+ }
+
+ data class OneConstructorWithValueClass(val value: ValueClass)
+
+ data class OneConstructorWithNullableValueClass(val value: ValueClass?)
+
}