diff --git a/spring-core/src/main/java/org/springframework/aot/hint/support/BindingReflectionHintsRegistrar.java b/spring-core/src/main/java/org/springframework/aot/hint/support/BindingReflectionHintsRegistrar.java index 2c8d89b566..fbf3c9d763 100644 --- a/spring-core/src/main/java/org/springframework/aot/hint/support/BindingReflectionHintsRegistrar.java +++ b/spring-core/src/main/java/org/springframework/aot/hint/support/BindingReflectionHintsRegistrar.java @@ -21,6 +21,7 @@ import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; +import java.lang.reflect.RecordComponent; import java.lang.reflect.Type; import java.util.LinkedHashSet; import java.util.Set; @@ -90,20 +91,28 @@ public class BindingReflectionHintsRegistrar { } seen.add(type); if (shouldRegisterMembers(clazz)) { - builder.withMembers( - MemberCategory.DECLARED_FIELDS, - MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); - try { - BeanInfo beanInfo = Introspector.getBeanInfo(clazz); - PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); - for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { - registerPropertyHints(hints, seen, propertyDescriptor.getWriteMethod(), 0); - registerPropertyHints(hints, seen, propertyDescriptor.getReadMethod(), -1); + if (clazz.isRecord()) { + builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); + for (RecordComponent recordComponent : clazz.getRecordComponents()) { + registerRecordHints(hints, seen, recordComponent.getAccessor()); } } - catch (IntrospectionException ex) { - if (logger.isDebugEnabled()) { - logger.debug("Ignoring referenced type [" + clazz.getName() + "]: " + ex.getMessage()); + else { + builder.withMembers( + MemberCategory.DECLARED_FIELDS, + MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); + try { + BeanInfo beanInfo = Introspector.getBeanInfo(clazz); + PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); + for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { + registerPropertyHints(hints, seen, propertyDescriptor.getWriteMethod(), 0); + registerPropertyHints(hints, seen, propertyDescriptor.getReadMethod(), -1); + } + } + catch (IntrospectionException ex) { + if (logger.isDebugEnabled()) { + logger.debug("Ignoring referenced type [" + clazz.getName() + "]: " + ex.getMessage()); + } } } } @@ -115,6 +124,15 @@ public class BindingReflectionHintsRegistrar { referencedTypes.forEach(referencedType -> registerReflectionHints(hints, seen, referencedType)); } + private void registerRecordHints(ReflectionHints hints, Set seen, Method method) { + hints.registerMethod(method, INVOKE); + MethodParameter methodParameter = MethodParameter.forExecutable(method, -1); + Type methodParameterType = methodParameter.getGenericParameterType(); + if (!seen.contains(methodParameterType)) { + registerReflectionHints(hints, seen, methodParameterType); + } + } + private void registerPropertyHints(ReflectionHints hints, Set seen, @Nullable Method method, int parameterIndex) { if (method != null && method.getDeclaringClass() != Object.class && method.getDeclaringClass() != Enum.class) { diff --git a/spring-core/src/test/java/org/springframework/aot/hint/support/BindingReflectionHintsRegistrarTests.java b/spring-core/src/test/java/org/springframework/aot/hint/support/BindingReflectionHintsRegistrarTests.java index 15acfb4188..e4b0573d48 100644 --- a/spring-core/src/test/java/org/springframework/aot/hint/support/BindingReflectionHintsRegistrarTests.java +++ b/spring-core/src/test/java/org/springframework/aot/hint/support/BindingReflectionHintsRegistrarTests.java @@ -203,6 +203,26 @@ public class BindingReflectionHintsRegistrarTests { .satisfies(typeHint -> assertThat(typeHint.getType()).isEqualTo(TypeReference.of(SampleEnum.class))); } + @Test + void registerTypeForSerializationWithRecord() { + bindingRegistrar.registerReflectionHints(this.hints.reflection(), SampleRecord.class); + assertThat(this.hints.reflection().typeHints()).satisfiesExactlyInAnyOrder( + typeHint -> { + assertThat(typeHint.getType()).isEqualTo(TypeReference.of(String.class)); + assertThat(typeHint.getMemberCategories()).isEmpty(); + assertThat(typeHint.constructors()).isEmpty(); + assertThat(typeHint.fields()).isEmpty(); + assertThat(typeHint.methods()).isEmpty(); + }, + typeHint -> { + assertThat(typeHint.getType()).isEqualTo(TypeReference.of(SampleRecord.class)); + assertThat(typeHint.methods()).singleElement().satisfies(methodHint -> { + assertThat(methodHint.getName()).isEqualTo("name"); + assertThat(methodHint.getModes()).containsOnly(ExecutableMode.INVOKE); + }); + }); + } + static class SampleEmptyClass { } @@ -285,4 +305,6 @@ public class BindingReflectionHintsRegistrarTests { value1, value2 } + record SampleRecord(String name) {} + }