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 0da9c8fd525f..e6b1daf57a5b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java @@ -33,6 +33,7 @@ import java.util.Set; import kotlin.jvm.JvmClassMappingKt; +import kotlin.jvm.internal.DefaultConstructorMarker; import kotlin.reflect.KClass; import kotlin.reflect.KFunction; import kotlin.reflect.KParameter; @@ -659,7 +660,13 @@ public static MethodParameter getWriteMethodParameter(PropertyDescriptor pd) { ConstructorProperties cp = ctor.getAnnotation(ConstructorProperties.class); @Nullable String[] paramNames = (cp != null ? cp.value() : parameterNameDiscoverer.getParameterNames(ctor)); Assert.state(paramNames != null, () -> "Cannot resolve parameter names for constructor " + ctor); - Assert.state(paramNames.length == ctor.getParameterCount(), + + // The generated param "DefaultConstructorMarker" is used to avoid collision of signatures + // and should be filtered out + long realParamsCount = Arrays.stream(ctor.getParameters()) + .filter(p -> !DefaultConstructorMarker.class.equals(p.getType())) + .count(); + Assert.state(paramNames.length == realParamsCount, () -> "Invalid number of parameter names: " + paramNames.length + " for constructor " + ctor); return paramNames; } 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 5b8378a49533..c75e7cc7fae9 100644 --- a/spring-beans/src/test/kotlin/org/springframework/beans/BeanUtilsKotlinTests.kt +++ b/spring-beans/src/test/kotlin/org/springframework/beans/BeanUtilsKotlinTests.kt @@ -93,7 +93,6 @@ class BeanUtilsKotlinTests { @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)) @@ -102,7 +101,6 @@ class BeanUtilsKotlinTests { @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)) @@ -111,7 +109,6 @@ class BeanUtilsKotlinTests { @Test fun `Instantiate class with value class parameter`() { val constructor = BeanUtils.findPrimaryConstructor(ConstructorWithValueClass::class.java)!! - assertThat(constructor).isNotNull() val value = ValueClass("Hello value class!") val instance = BeanUtils.instantiateClass(constructor, value) assertThat(instance).isEqualTo(ConstructorWithValueClass(value)) @@ -120,7 +117,6 @@ class BeanUtilsKotlinTests { @Test fun `Instantiate class with nullable value class parameter`() { val constructor = BeanUtils.findPrimaryConstructor(ConstructorWithNullableValueClass::class.java)!! - assertThat(constructor).isNotNull() val value = ValueClass("Hello value class!") var instance = BeanUtils.instantiateClass(constructor, value) assertThat(instance).isEqualTo(ConstructorWithNullableValueClass(value)) @@ -131,7 +127,6 @@ class BeanUtilsKotlinTests { @Test fun `Instantiate primitive value class`() { val constructor = BeanUtils.findPrimaryConstructor(PrimitiveValueClass::class.java)!! - assertThat(constructor).isNotNull() val value = 0 val instance = BeanUtils.instantiateClass(constructor, value) assertThat(instance).isEqualTo(PrimitiveValueClass(value)) @@ -140,7 +135,6 @@ class BeanUtilsKotlinTests { @Test fun `Instantiate class with primitive value class parameter`() { val constructor = BeanUtils.findPrimaryConstructor(ConstructorWithPrimitiveValueClass::class.java)!! - assertThat(constructor).isNotNull() val value = PrimitiveValueClass(0) val instance = BeanUtils.instantiateClass(constructor, value) assertThat(instance).isEqualTo(ConstructorWithPrimitiveValueClass(value)) @@ -149,7 +143,6 @@ class BeanUtilsKotlinTests { @Test fun `Instantiate class with nullable primitive value class parameter`() { val constructor = BeanUtils.findPrimaryConstructor(ConstructorWithNullablePrimitiveValueClass::class.java)!! - assertThat(constructor).isNotNull() val value = PrimitiveValueClass(0) var instance = BeanUtils.instantiateClass(constructor, value) assertThat(instance).isEqualTo(ConstructorWithNullablePrimitiveValueClass(value)) @@ -157,6 +150,63 @@ class BeanUtilsKotlinTests { assertThat(instance).isEqualTo(ConstructorWithNullablePrimitiveValueClass(null)) } + @Test + fun `getParameterNames filters out DefaultConstructorMarker with Foo`() { + val ctor = BeanUtils.findPrimaryConstructor(Foo::class.java)!! + val names = BeanUtils.getParameterNames(ctor) + assertThat(names).containsExactly("param1", "param2") + } + + + @Test + fun `getParameterNames filters out DefaultConstructorMarker with Bar`() { + val ctor = BeanUtils.findPrimaryConstructor(Bar::class.java)!! + val names = BeanUtils.getParameterNames(ctor) + assertThat(names).containsExactly("param1", "param2") + } + + @Test + fun `getParameterNames filters out DefaultConstructorMarker with Baz`() { + val ctor = BeanUtils.findPrimaryConstructor(Baz::class.java)!! + val names = BeanUtils.getParameterNames(ctor) + assertThat(names).containsExactly("param1", "param2") + } + + @Test + fun `getParameterNames filters out DefaultConstructorMarker with Qux`() { + val ctor = BeanUtils.findPrimaryConstructor(Qux::class.java)!! + val names = BeanUtils.getParameterNames(ctor) + assertThat(names).containsExactly("param1", "param2") + } + + @Test + fun `getParameterNames filters out DefaultConstructorMarker with ConstructorWithValueClass`() { + val ctor = BeanUtils.findPrimaryConstructor(ConstructorWithValueClass::class.java)!! + val names = BeanUtils.getParameterNames(ctor) + assertThat(names).containsExactly("value") + } + + @Test + fun `getParameterNames filters out DefaultConstructorMarker with ConstructorWithNullableValueClass`() { + val ctor = BeanUtils.findPrimaryConstructor(ConstructorWithNullableValueClass::class.java)!! + val names = BeanUtils.getParameterNames(ctor) + assertThat(names).containsExactly("value") + } + + @Test + fun `getParameterNames filters out DefaultConstructorMarker with ConstructorWithPrimitiveValueClass`() { + val ctor = BeanUtils.findPrimaryConstructor(ConstructorWithPrimitiveValueClass::class.java)!! + val names = BeanUtils.getParameterNames(ctor) + assertThat(names).containsExactly("value") + } + + @Test + fun `getParameterNames filters out DefaultConstructorMarker with ConstructorWithNullablePrimitiveValueClass`() { + val ctor = BeanUtils.findPrimaryConstructor(ConstructorWithNullablePrimitiveValueClass::class.java)!! + val names = BeanUtils.getParameterNames(ctor) + assertThat(names).containsExactly("value") + } + class Foo(val param1: String, val param2: Int)