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 3450320d861d..be8221d62d5b 100644 --- a/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java +++ b/spring-beans/src/main/java/org/springframework/beans/BeanUtils.java @@ -23,6 +23,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.lang.reflect.RecordComponent; import java.lang.reflect.Type; import java.util.Arrays; import java.util.Collections; @@ -252,6 +253,19 @@ else if (ctors.length == 0) { return (Constructor) ctors[0]; } } + else if (clazz.isRecord()) { + try { + // if record -> use canonical constructor, which is always presented + Class[] paramTypes + = Arrays.stream(clazz.getRecordComponents()) + .map(RecordComponent::getType) + .toArray(Class[]::new); + return clazz.getDeclaredConstructor(paramTypes); + } + catch (NoSuchMethodException ex) { + // Giving up with record... + } + } // Several constructors -> let's try to take the default constructor try { diff --git a/spring-beans/src/test/java/org/springframework/beans/BeanUtilsTests.java b/spring-beans/src/test/java/org/springframework/beans/BeanUtilsTests.java index 6041c102b772..0ee33280a351 100644 --- a/spring-beans/src/test/java/org/springframework/beans/BeanUtilsTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/BeanUtilsTests.java @@ -521,10 +521,26 @@ void isNotSimpleProperty(Class type) { assertThat(BeanUtils.isSimpleProperty(type)).as("Type [" + type.getName() + "] should not be a simple property").isFalse(); } + @Test + void resolveRecordConstructor() throws NoSuchMethodException { + assertThat(BeanUtils.getResolvableConstructor(RecordWithMultiplePublicConstructors.class)) + .isEqualTo(getRecordWithMultipleVariationsConstructor()); + } + private void assertSignatureEquals(Method desiredMethod, String signature) { assertThat(BeanUtils.resolveSignature(signature, MethodSignatureBean.class)).isEqualTo(desiredMethod); } + public record RecordWithMultiplePublicConstructors(String value, String name) { + public RecordWithMultiplePublicConstructors(String value) { + this(value, "default value"); + } + } + + private Constructor getRecordWithMultipleVariationsConstructor() throws NoSuchMethodException { + return RecordWithMultiplePublicConstructors.class.getConstructor(String.class, String.class); + } + @SuppressWarnings("unused") private static class NumberHolder {