From 5350aeaad94c32768cb2b0099f4a7b89889abf19 Mon Sep 17 00:00:00 2001 From: EmilSt Date: Wed, 7 May 2025 12:55:31 +0100 Subject: [PATCH] fix: ignore Kotlin DefaultConstructorMarker in BeanUtils#getParametersName (#34760) Mismatch caused by DefaultConstructorMarker, which is now filtered out. Signed-off-by: EmilSt --- .../org/springframework/beans/BeanUtils.java | 9 ++- .../beans/BeanUtilsKotlinTests.kt | 61 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) 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..7cea57c4cdab 100644 --- a/spring-beans/src/test/kotlin/org/springframework/beans/BeanUtilsKotlinTests.kt +++ b/spring-beans/src/test/kotlin/org/springframework/beans/BeanUtilsKotlinTests.kt @@ -157,6 +157,67 @@ 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)!! + assertThat(ctor).isNotNull() + val names = BeanUtils.getParameterNames(ctor) + assertThat(names).containsExactly("value") + } + + @Test + fun `getParameterNames filters out DefaultConstructorMarker with ConstructorWithNullableValueClass`() { + val ctor = BeanUtils.findPrimaryConstructor(ConstructorWithNullableValueClass::class.java)!! + assertThat(ctor).isNotNull() + val names = BeanUtils.getParameterNames(ctor) + assertThat(names).containsExactly("value") + } + + @Test + fun `getParameterNames filters out DefaultConstructorMarker with ConstructorWithPrimitiveValueClass`() { + val ctor = BeanUtils.findPrimaryConstructor(ConstructorWithPrimitiveValueClass::class.java)!! + assertThat(ctor).isNotNull() + val names = BeanUtils.getParameterNames(ctor) + assertThat(names).containsExactly("value") + } + + @Test + fun `getParameterNames filters out DefaultConstructorMarker with ConstructorWithNullablePrimitiveValueClass`() { + val ctor = BeanUtils.findPrimaryConstructor(ConstructorWithNullablePrimitiveValueClass::class.java)!! + assertThat(ctor).isNotNull() + val names = BeanUtils.getParameterNames(ctor) + assertThat(names).containsExactly("value") + } + class Foo(val param1: String, val param2: Int)