From 80646591367b93d220b9dc0b0a126b203edc140c Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Mon, 9 Oct 2023 20:03:06 +0200 Subject: [PATCH] Register bean reflection hint for property fields Prior to this commit, the bean definition properties code generator would register hints for invoking the setter methods of registered property values defined for the bean definition. The internal algorithm is also reflecting on the Field to discover annotations. Doing so actually calls `getDeclaredFields` to iterate on the available fields. This is done recursively up the type hierarchy until the field is found. This commit registers the required reflection metadata. Closes gh-31390 --- ...BeanDefinitionPropertiesCodeGenerator.java | 8 ++++++ ...efinitionPropertiesCodeGeneratorTests.java | 27 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java index 614cc32abf0..dbd44b1b0ae 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGenerator.java @@ -34,6 +34,7 @@ import java.util.function.Predicate; import org.springframework.aot.generate.GeneratedMethods; import org.springframework.aot.hint.ExecutableMode; +import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.TypeReference; import org.springframework.beans.BeanUtils; @@ -195,6 +196,13 @@ class BeanDefinitionPropertiesCodeGenerator { Method writeMethod = writeMethods.get(propertyValue.getName()); if (writeMethod != null) { this.hints.reflection().registerMethod(writeMethod, ExecutableMode.INVOKE); + // ReflectionUtils#findField searches recursively in the type hierarchy + Class searchType = beanDefinition.getTargetType(); + while (searchType != null && searchType != writeMethod.getDeclaringClass()) { + this.hints.reflection().registerType(searchType, MemberCategory.DECLARED_FIELDS); + searchType = searchType.getSuperclass(); + } + this.hints.reflection().registerType(writeMethod.getDeclaringClass(), MemberCategory.DECLARED_FIELDS); } } } diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGeneratorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGeneratorTests.java index aa1f663b8b2..8cdf923bb69 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGeneratorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/aot/BeanDefinitionPropertiesCodeGeneratorTests.java @@ -34,6 +34,7 @@ import org.junit.jupiter.api.Test; import org.reactivestreams.Publisher; import org.springframework.aot.generate.GeneratedClass; +import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; import org.springframework.aot.test.generate.TestGenerationContext; import org.springframework.beans.factory.FactoryBean; @@ -240,6 +241,21 @@ class BeanDefinitionPropertiesCodeGeneratorTests { assertThat(actual.getPropertyValues().get("spring")).isEqualTo("framework"); }); assertHasMethodInvokeHints(PropertyValuesBean.class, "setTest", "setSpring"); + assertHasDecalredFieldsHint(PropertyValuesBean.class); + } + + @Test + void propertyValuesWhenValuesOnParentClass() { + this.beanDefinition.setTargetType(ExtendedPropertyValuesBean.class); + this.beanDefinition.getPropertyValues().add("test", String.class); + this.beanDefinition.getPropertyValues().add("spring", "framework"); + compile((actual, compiled) -> { + assertThat(actual.getPropertyValues().get("test")).isEqualTo(String.class); + assertThat(actual.getPropertyValues().get("spring")).isEqualTo("framework"); + }); + assertHasMethodInvokeHints(PropertyValuesBean.class, "setTest", "setSpring"); + assertHasDecalredFieldsHint(ExtendedPropertyValuesBean.class); + assertHasDecalredFieldsHint(PropertyValuesBean.class); } @Test @@ -300,6 +316,7 @@ class BeanDefinitionPropertiesCodeGeneratorTests { assertThat(actual.getPropertyValues().get("name")).isEqualTo("World"); }); assertHasMethodInvokeHints(PropertyValuesFactoryBean.class, "setPrefix", "setName" ); + assertHasDecalredFieldsHint(PropertyValuesFactoryBean.class); } @Test @@ -453,6 +470,12 @@ class BeanDefinitionPropertiesCodeGeneratorTests { .test(this.generationContext.getRuntimeHints())); } + private void assertHasDecalredFieldsHint(Class beanType) { + assertThat(RuntimeHintsPredicates.reflection() + .onType(beanType).withMemberCategory(MemberCategory.DECLARED_FIELDS)) + .accepts(this.generationContext.getRuntimeHints()); + } + private void compile(BiConsumer result) { compile(attribute -> true, result); } @@ -524,6 +547,10 @@ class BeanDefinitionPropertiesCodeGeneratorTests { } + static class ExtendedPropertyValuesBean extends PropertyValuesBean { + + } + static class PropertyValuesFactoryBean implements FactoryBean { private String prefix;