From 58a2b7969901fbd703bc8ffed905b5d8a3c6d8d3 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Wed, 24 Aug 2022 07:21:02 +0200 Subject: [PATCH] Harmonize hint registration Previously, a shortcut method for the default ExecutableMode was provided, but we found out that the shortcut makes it harder to determine the intent. This commit harmonizes hints registration for types, methods, and fields. An ExecutableMode is now mandatory to register a method or constructor. Previous methods that infer a mode or provided a customizer of the builder are deprecated. Closes gh-29135 --- .../AutowiredAnnotationBeanPostProcessor.java | 2 +- ...BeanDefinitionPropertiesCodeGenerator.java | 2 +- .../aot/InstanceSupplierCodeGenerator.java | 4 +- .../aot/agent/InstrumentedMethodTests.java | 8 +- .../hint/BindingReflectionHintsRegistrar.java | 2 +- .../aot/hint/ReflectionHints.java | 62 +++++++++---- .../springframework/aot/hint/TypeHint.java | 17 +++- .../annotation/SimpleReflectiveProcessor.java | 5 +- .../aot/hint/ReflectionHintsTests.java | 59 ++---------- .../aot/hint/TypeHintTests.java | 90 +++---------------- .../ReflectionHintsPredicatesTests.java | 4 +- .../FileNativeConfigurationWriterTests.java | 2 +- .../nativex/ReflectionHintsWriterTests.java | 2 +- .../jpa/support/InjectionCodeGenerator.java | 3 +- 14 files changed, 97 insertions(+), 165 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java index ace0f093ac6..92dec61c232 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -1007,7 +1007,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA AccessVisibility visibility = AccessVisibility.forMember(method); if (visibility == AccessVisibility.PRIVATE || visibility == AccessVisibility.PROTECTED) { - hints.reflection().registerMethod(method); + hints.reflection().registerMethod(method, ExecutableMode.INVOKE); code.add(".resolveAndInvoke($L, $L)", REGISTERED_BEAN_PARAMETER, INSTANCE_PARAMETER); } 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 39380e784d3..cef034888c7 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 @@ -145,7 +145,7 @@ class BeanDefinitionPropertiesCodeGenerator { private void addInitDestroyHint(Class beanUserClass, String methodName) { Method method = ReflectionUtils.findMethod(beanUserClass, methodName); if (method != null) { - this.hints.reflection().registerMethod(method); + this.hints.reflection().registerMethod(method, ExecutableMode.INVOKE); } } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGenerator.java b/spring-beans/src/main/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGenerator.java index e6cb5df84f1..217ac0f1e8f 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGenerator.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGenerator.java @@ -147,7 +147,7 @@ class InstanceSupplierCodeGenerator { Class beanClass, Constructor constructor, boolean dependsOnBean) { this.generationContext.getRuntimeHints().reflection() - .registerConstructor(constructor); + .registerConstructor(constructor, ExecutableMode.INVOKE); GeneratedMethod generatedMethod = generateGetInstanceSupplierMethod(method -> { method.addJavadoc("Get the bean instance supplier for '$L'.", beanName); method.addModifiers(PRIVATE_STATIC); @@ -240,7 +240,7 @@ class InstanceSupplierCodeGenerator { Method factoryMethod, Class declaringClass) { this.generationContext.getRuntimeHints().reflection() - .registerMethod(factoryMethod); + .registerMethod(factoryMethod, ExecutableMode.INVOKE); GeneratedMethod getInstanceMethod = generateGetInstanceSupplierMethod(method -> { method.addJavadoc("Get the bean instance supplier for '$L'.", beanName); method.addModifiers(PRIVATE_STATIC); diff --git a/spring-core-test/src/test/java/org/springframework/aot/agent/InstrumentedMethodTests.java b/spring-core-test/src/test/java/org/springframework/aot/agent/InstrumentedMethodTests.java index 0a3863a08c9..6b989174d3f 100644 --- a/spring-core-test/src/test/java/org/springframework/aot/agent/InstrumentedMethodTests.java +++ b/spring-core-test/src/test/java/org/springframework/aot/agent/InstrumentedMethodTests.java @@ -194,7 +194,7 @@ class InstrumentedMethodTests { @Test void classGetConstructorsShouldMatchConstructorReflectionHint() throws Exception { - hints.reflection().registerConstructor(String.class.getConstructor()); + hints.reflection().registerConstructor(String.class.getConstructor(), ExecutableMode.INVOKE); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCONSTRUCTORS, this.stringGetConstructors); } @@ -250,7 +250,7 @@ class InstrumentedMethodTests { @Test void classGetDeclaredConstructorsShouldMatchConstructorReflectionHint() throws Exception { - hints.reflection().registerConstructor(String.class.getConstructor()); + hints.reflection().registerConstructor(String.class.getConstructor(), ExecutableMode.INVOKE); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDCONSTRUCTORS, this.stringGetDeclaredConstructors); } @@ -350,7 +350,7 @@ class InstrumentedMethodTests { @Test void classGetDeclaredMethodsShouldMatchMethodReflectionHint() throws Exception { - hints.reflection().registerMethod(String.class.getMethod("toString")); + hints.reflection().registerMethod(String.class.getMethod("toString"), ExecutableMode.INVOKE); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDMETHODS, this.stringGetScaleMethod); } @@ -392,7 +392,7 @@ class InstrumentedMethodTests { @Test void classGetMethodsShouldMatchMethodReflectionHint() throws Exception { - hints.reflection().registerMethod(String.class.getMethod("toString")); + hints.reflection().registerMethod(String.class.getMethod("toString"), ExecutableMode.INVOKE); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETMETHODS, this.stringGetMethods); } diff --git a/spring-core/src/main/java/org/springframework/aot/hint/BindingReflectionHintsRegistrar.java b/spring-core/src/main/java/org/springframework/aot/hint/BindingReflectionHintsRegistrar.java index 35b4b5993d9..4c560eb9163 100644 --- a/spring-core/src/main/java/org/springframework/aot/hint/BindingReflectionHintsRegistrar.java +++ b/spring-core/src/main/java/org/springframework/aot/hint/BindingReflectionHintsRegistrar.java @@ -145,7 +145,7 @@ public class BindingReflectionHintsRegistrar { Class companionClass = ClassUtils.resolveClassName(companionClassName, null); Method serializerMethod = ClassUtils.getMethodIfAvailable(companionClass, "serializer"); if (serializerMethod != null) { - hints.registerMethod(serializerMethod); + hints.registerMethod(serializerMethod, ExecutableMode.INVOKE); } } } diff --git a/spring-core/src/main/java/org/springframework/aot/hint/ReflectionHints.java b/spring-core/src/main/java/org/springframework/aot/hint/ReflectionHints.java index e70e9d94cca..7cf337a0c0a 100644 --- a/spring-core/src/main/java/org/springframework/aot/hint/ReflectionHints.java +++ b/spring-core/src/main/java/org/springframework/aot/hint/ReflectionHints.java @@ -36,6 +36,8 @@ import org.springframework.util.ClassUtils; * Gather the need for reflection at runtime. * * @author Stephane Nicoll + * @author Phillip Webb + * @author Andy Wilkinson * @since 6.0 */ public class ReflectionHints { @@ -79,6 +81,7 @@ public class ReflectionHints { * @param type the type to customize * @param typeHint a builder to further customize hints for that type * @return {@code this}, to facilitate method chaining + * @see #registerType(TypeReference, MemberCategory...) */ public ReflectionHints registerType(TypeReference type, Consumer typeHint) { Builder builder = this.types.computeIfAbsent(type, TypeHint.Builder::new); @@ -93,8 +96,19 @@ public class ReflectionHints { * @param memberCategories the member categories to apply * @return {@code this}, to facilitate method chaining */ - public ReflectionHints registerType(Class type, MemberCategory... memberCategories) { - return registerType(TypeReference.of(type), memberCategories); + public ReflectionHints registerType(TypeReference type, MemberCategory... memberCategories) { + return registerType(type, TypeHint.builtWith(memberCategories)); + } + + /** + * Register or customize reflection hints for the specified type. + * @param type the type to customize + * @param typeHint a builder to further customize hints for that type + * @return {@code this}, to facilitate method chaining + * @see #registerType(Class, MemberCategory...) + */ + public ReflectionHints registerType(Class type, Consumer typeHint) { + return registerType(TypeReference.of(type), typeHint); } /** @@ -104,18 +118,8 @@ public class ReflectionHints { * @param memberCategories the member categories to apply * @return {@code this}, to facilitate method chaining */ - public ReflectionHints registerType(TypeReference type , MemberCategory... memberCategories) { - return registerType(type, TypeHint.builtWith(memberCategories)); - } - - /** - * Register or customize reflection hints for the specified type. - * @param type the type to customize - * @param typeHint a builder to further customize hints for that type - * @return {@code this}, to facilitate method chaining - */ - public ReflectionHints registerType(Class type, Consumer typeHint) { - return registerType(TypeReference.of(type), typeHint); + public ReflectionHints registerType(Class type, MemberCategory... memberCategories) { + return registerType(TypeReference.of(type), memberCategories); } /** @@ -125,6 +129,7 @@ public class ReflectionHints { * @param typeName the type to customize * @param typeHint a builder to further customize hints for that type * @return {@code this}, to facilitate method chaining + * @see #registerTypeIfPresent(ClassLoader, String, MemberCategory...) */ public ReflectionHints registerTypeIfPresent(@Nullable ClassLoader classLoader, String typeName, Consumer typeHint) { @@ -134,6 +139,19 @@ public class ReflectionHints { return this; } + /** + * Register or customize reflection hints for the specified type if it + * is available using the specified {@link ClassLoader}. + * @param classLoader the classloader to use to check if the type is present + * @param typeName the type to customize + * @param memberCategories the member categories to apply + * @return {@code this}, to facilitate method chaining + */ + public ReflectionHints registerTypeIfPresent(@Nullable ClassLoader classLoader, + String typeName, MemberCategory... memberCategories) { + return registerTypeIfPresent(classLoader, typeName, TypeHint.builtWith(memberCategories)); + } + /** * Register or customize reflection hints for the types defined by the * specified list of {@link TypeReference type references}. The specified @@ -162,7 +180,9 @@ public class ReflectionHints { * enabling {@link ExecutableMode#INVOKE}. * @param constructor the constructor that requires reflection * @return {@code this}, to facilitate method chaining + * @deprecated in favor of {@link #registerConstructor(Constructor, ExecutableMode)} */ + @Deprecated public ReflectionHints registerConstructor(Constructor constructor) { return registerConstructor(constructor, ExecutableMode.INVOKE); } @@ -175,7 +195,8 @@ public class ReflectionHints { * @return {@code this}, to facilitate method chaining */ public ReflectionHints registerConstructor(Constructor constructor, ExecutableMode mode) { - return registerConstructor(constructor, ExecutableHint.builtWith(mode)); + return registerType(TypeReference.of(constructor.getDeclaringClass()), + typeHint -> typeHint.withConstructor(mapParameters(constructor), mode)); } /** @@ -183,8 +204,10 @@ public class ReflectionHints { * @param constructor the constructor that requires reflection * @param constructorHint a builder to further customize the hints of this * constructor - * @return {@code this}, to facilitate method chaining + * @return {@code this}, to facilitate method chaining` + * @deprecated in favor of {@link #registerConstructor(Constructor, ExecutableMode)} */ + @Deprecated public ReflectionHints registerConstructor(Constructor constructor, Consumer constructorHint) { return registerType(TypeReference.of(constructor.getDeclaringClass()), typeHint -> typeHint.withConstructor(mapParameters(constructor), constructorHint)); @@ -195,7 +218,9 @@ public class ReflectionHints { * enabling {@link ExecutableMode#INVOKE}. * @param method the method that requires reflection * @return {@code this}, to facilitate method chaining + * @deprecated in favor of {@link #registerMethod(Method, ExecutableMode)} */ + @Deprecated public ReflectionHints registerMethod(Method method) { return registerMethod(method, ExecutableMode.INVOKE); } @@ -208,7 +233,8 @@ public class ReflectionHints { * @return {@code this}, to facilitate method chaining */ public ReflectionHints registerMethod(Method method, ExecutableMode mode) { - return registerMethod(method, ExecutableHint.builtWith(mode)); + return registerType(TypeReference.of(method.getDeclaringClass()), + typeHint -> typeHint.withMethod(method.getName(), mapParameters(method), mode)); } /** @@ -216,7 +242,9 @@ public class ReflectionHints { * @param method the method that requires reflection * @param methodHint a builder to further customize the hints of this method * @return {@code this}, to facilitate method chaining + * @deprecated in favor of {@link #registerMethod(Method, ExecutableMode)} */ + @Deprecated public ReflectionHints registerMethod(Method method, Consumer methodHint) { return registerType(TypeReference.of(method.getDeclaringClass()), typeHint -> typeHint.withMethod(method.getName(), mapParameters(method), methodHint)); diff --git a/spring-core/src/main/java/org/springframework/aot/hint/TypeHint.java b/spring-core/src/main/java/org/springframework/aot/hint/TypeHint.java index f660ad3a3c5..191fe5fdefd 100644 --- a/spring-core/src/main/java/org/springframework/aot/hint/TypeHint.java +++ b/spring-core/src/main/java/org/springframework/aot/hint/TypeHint.java @@ -35,6 +35,8 @@ import org.springframework.util.Assert; * A hint that describes the need for reflection on a type. * * @author Stephane Nicoll + * @author Phillip Webb + * @author Andy Wilkinson * @since 6.0 */ public final class TypeHint implements ConditionalHint { @@ -199,7 +201,9 @@ public final class TypeHint implements ConditionalHint { * parameter types, enabling {@link ExecutableMode#INVOKE}. * @param parameterTypes the parameter types of the constructor * @return {@code this}, to facilitate method chaining + * @deprecated in favor of {@link #withConstructor(List, ExecutableMode)} */ + @Deprecated public Builder withConstructor(List parameterTypes) { return withConstructor(parameterTypes, ExecutableMode.INVOKE); } @@ -222,8 +226,11 @@ public final class TypeHint implements ConditionalHint { * @param constructorHint a builder to further customize the hints of this * constructor * @return {@code this}, to facilitate method chaining + * @deprecated in favor of {@link #withConstructor(List, ExecutableMode)} */ - public Builder withConstructor(List parameterTypes, Consumer constructorHint) { + @Deprecated + public Builder withConstructor(List parameterTypes, + Consumer constructorHint) { ExecutableKey key = new ExecutableKey("", parameterTypes); ExecutableHint.Builder builder = this.constructors.computeIfAbsent(key, k -> ExecutableHint.ofConstructor(parameterTypes)); @@ -237,7 +244,9 @@ public final class TypeHint implements ConditionalHint { * @param name the name of the method * @param parameterTypes the parameter types of the constructor * @return {@code this}, to facilitate method chaining + * @deprecated in favor of {@link #withMethod(String, List, ExecutableMode)} */ + @Deprecated public Builder withMethod(String name, List parameterTypes) { return withMethod(name, parameterTypes, ExecutableMode.INVOKE); } @@ -261,8 +270,11 @@ public final class TypeHint implements ConditionalHint { * @param parameterTypes the parameter types of the constructor * @param methodHint a builder to further customize the hints of this method * @return {@code this}, to facilitate method chaining + * @deprecated in favor of {@link #withMethod(String, List, ExecutableMode)} */ - public Builder withMethod(String name, List parameterTypes, Consumer methodHint) { + @Deprecated + public Builder withMethod(String name, List parameterTypes, + Consumer methodHint) { ExecutableKey key = new ExecutableKey(name, parameterTypes); ExecutableHint.Builder builder = this.methods.computeIfAbsent(key, k -> ExecutableHint.ofMethod(name, parameterTypes)); @@ -274,6 +286,7 @@ public final class TypeHint implements ConditionalHint { * Adds the specified {@linkplain MemberCategory member categories}. * @param memberCategories the categories to apply * @return {@code this}, to facilitate method chaining + * @see TypeHint#builtWith(MemberCategory...) */ public Builder withMembers(MemberCategory... memberCategories) { this.memberCategories.addAll(Arrays.asList(memberCategories)); diff --git a/spring-core/src/main/java/org/springframework/aot/hint/annotation/SimpleReflectiveProcessor.java b/spring-core/src/main/java/org/springframework/aot/hint/annotation/SimpleReflectiveProcessor.java index 974981d4106..949f97ca1d9 100644 --- a/spring-core/src/main/java/org/springframework/aot/hint/annotation/SimpleReflectiveProcessor.java +++ b/spring-core/src/main/java/org/springframework/aot/hint/annotation/SimpleReflectiveProcessor.java @@ -21,6 +21,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; +import org.springframework.aot.hint.ExecutableMode; import org.springframework.aot.hint.ReflectionHints; /** @@ -64,7 +65,7 @@ public class SimpleReflectiveProcessor implements ReflectiveProcessor { * @param constructor the constructor to process */ protected void registerConstructorHint(ReflectionHints hints, Constructor constructor) { - hints.registerConstructor(constructor); + hints.registerConstructor(constructor, ExecutableMode.INVOKE); } /** @@ -82,7 +83,7 @@ public class SimpleReflectiveProcessor implements ReflectiveProcessor { * @param method the method to process */ protected void registerMethodHint(ReflectionHints hints, Method method) { - hints.registerMethod(method); + hints.registerMethod(method, ExecutableMode.INVOKE); } } diff --git a/spring-core/src/test/java/org/springframework/aot/hint/ReflectionHintsTests.java b/spring-core/src/test/java/org/springframework/aot/hint/ReflectionHintsTests.java index b585609a06d..7f0749c9d15 100644 --- a/spring-core/src/test/java/org/springframework/aot/hint/ReflectionHintsTests.java +++ b/spring-core/src/test/java/org/springframework/aot/hint/ReflectionHintsTests.java @@ -16,6 +16,7 @@ package org.springframework.aot.hint; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.function.Consumer; @@ -48,8 +49,7 @@ class ReflectionHintsTests { @Test void registerTypeIfPresentRegistersExistingClass() { - this.reflectionHints.registerTypeIfPresent(null, String.class.getName(), - hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS)); + this.reflectionHints.registerTypeIfPresent(null, String.class.getName(), MemberCategory.DECLARED_FIELDS); assertThat(this.reflectionHints.typeHints()).singleElement().satisfies( typeWithMemberCategories(String.class, MemberCategory.DECLARED_FIELDS)); } @@ -144,15 +144,6 @@ class ReflectionHintsTests { @Test void registerConstructor() { - this.reflectionHints.registerConstructor(TestType.class.getDeclaredConstructors()[0]); - assertTestTypeConstructorHint(constructorHint -> { - assertThat(constructorHint.getParameterTypes()).isEmpty(); - assertThat(constructorHint.getMode()).isEqualTo(ExecutableMode.INVOKE); - }); - } - - @Test - void registerConstructorWithMode() { this.reflectionHints.registerConstructor( TestType.class.getDeclaredConstructors()[0], ExecutableMode.INTROSPECT); assertTestTypeConstructorHint(constructorHint -> { @@ -162,25 +153,16 @@ class ReflectionHintsTests { } @Test - void registerConstructorWithEmptyCustomizerAppliesConsistentDefault() { - this.reflectionHints.registerConstructor(TestType.class.getDeclaredConstructors()[0], - constructorHint -> {}); + void registerConstructorTwiceUpdatesExistingEntry() { + Constructor constructor = TestType.class.getDeclaredConstructors()[0]; + this.reflectionHints.registerConstructor(constructor, ExecutableMode.INTROSPECT); + this.reflectionHints.registerConstructor(constructor, ExecutableMode.INVOKE); assertTestTypeConstructorHint(constructorHint -> { assertThat(constructorHint.getParameterTypes()).isEmpty(); assertThat(constructorHint.getMode()).isEqualTo(ExecutableMode.INVOKE); }); } - @Test - void registerConstructorWithCustomizerAppliesCustomization() { - this.reflectionHints.registerConstructor(TestType.class.getDeclaredConstructors()[0], - constructorHint -> constructorHint.withMode(ExecutableMode.INTROSPECT)); - assertTestTypeConstructorHint(constructorHint -> { - assertThat(constructorHint.getParameterTypes()).isEmpty(); - assertThat(constructorHint.getMode()).isEqualTo(ExecutableMode.INTROSPECT); - }); - } - private void assertTestTypeConstructorHint(Consumer constructorHint) { assertThat(this.reflectionHints.typeHints()).singleElement().satisfies(typeHint -> { assertThat(typeHint.getMemberCategories()).isEmpty(); @@ -194,18 +176,6 @@ class ReflectionHintsTests { @Test void registerMethod() { - Method method = ReflectionUtils.findMethod(TestType.class, "setName", String.class); - assertThat(method).isNotNull(); - this.reflectionHints.registerMethod(method); - assertTestTypeMethodHints(methodHint -> { - assertThat(methodHint.getName()).isEqualTo("setName"); - assertThat(methodHint.getParameterTypes()).containsOnly(TypeReference.of(String.class)); - assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE); - }); - } - - @Test - void registerMethodWithMode() { Method method = ReflectionUtils.findMethod(TestType.class, "setName", String.class); assertThat(method).isNotNull(); this.reflectionHints.registerMethod(method, ExecutableMode.INTROSPECT); @@ -217,10 +187,11 @@ class ReflectionHintsTests { } @Test - void registerMethodWithEmptyCustomizerAppliesConsistentDefault() { + void registerMethodTwiceUpdatesExistingEntry() { Method method = ReflectionUtils.findMethod(TestType.class, "setName", String.class); assertThat(method).isNotNull(); - this.reflectionHints.registerMethod(method, methodHint -> {}); + this.reflectionHints.registerMethod(method, ExecutableMode.INTROSPECT); + this.reflectionHints.registerMethod(method, ExecutableMode.INVOKE); assertTestTypeMethodHints(methodHint -> { assertThat(methodHint.getName()).isEqualTo("setName"); assertThat(methodHint.getParameterTypes()).containsOnly(TypeReference.of(String.class)); @@ -228,18 +199,6 @@ class ReflectionHintsTests { }); } - @Test - void registerMethodWithCustomizerAppliesCustomization() { - Method method = ReflectionUtils.findMethod(TestType.class, "setName", String.class); - assertThat(method).isNotNull(); - this.reflectionHints.registerMethod(method, methodHint -> methodHint.withMode(ExecutableMode.INTROSPECT)); - assertTestTypeMethodHints(methodHint -> { - assertThat(methodHint.getName()).isEqualTo("setName"); - assertThat(methodHint.getParameterTypes()).containsOnly(TypeReference.of(String.class)); - assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INTROSPECT); - }); - } - private void assertTestTypeMethodHints(Consumer methodHint) { assertThat(this.reflectionHints.typeHints()).singleElement().satisfies(typeHint -> { assertThat(typeHint.getType().getCanonicalName()).isEqualTo(TestType.class.getCanonicalName()); diff --git a/spring-core/src/test/java/org/springframework/aot/hint/TypeHintTests.java b/spring-core/src/test/java/org/springframework/aot/hint/TypeHintTests.java index c0f57435623..5c84ec93bbc 100644 --- a/spring-core/src/test/java/org/springframework/aot/hint/TypeHintTests.java +++ b/spring-core/src/test/java/org/springframework/aot/hint/TypeHintTests.java @@ -70,16 +70,6 @@ class TypeHintTests { @Test void createWithConstructor() { - List parameterTypes = TypeReference.listOf(byte[].class, int.class); - assertConstructorHint(TypeHint.of(TypeReference.of(String.class)) - .withConstructor(parameterTypes), constructorHint -> { - assertThat(constructorHint.getParameterTypes()).containsOnlyOnceElementsOf(parameterTypes); - assertThat(constructorHint.getMode()).isEqualTo(ExecutableMode.INVOKE); - }); - } - - @Test - void createWithConstructorAndMode() { List parameterTypes = TypeReference.listOf(byte[].class, int.class); assertConstructorHint(TypeHint.of(TypeReference.of(String.class)) .withConstructor(parameterTypes, ExecutableMode.INTROSPECT), constructorHint -> { @@ -89,45 +79,22 @@ class TypeHintTests { } @Test - void createWithConstructorAndEmptyCustomizerAppliesConsistentDefault() { - List parameterTypes = TypeReference.listOf(byte[].class, int.class); - assertConstructorHint(TypeHint.of(TypeReference.of(String.class)) - .withConstructor(parameterTypes, constructorHint -> {}), constructorHint -> { - assertThat(constructorHint.getParameterTypes()).containsOnlyOnceElementsOf(parameterTypes); - assertThat(constructorHint.getMode()).isEqualTo(ExecutableMode.INVOKE); - }); - } - - @Test - void createWithConstructorAndCustomizerAppliesCustomization() { - List parameterTypes = TypeReference.listOf(byte[].class, int.class); - assertConstructorHint(TypeHint.of(TypeReference.of(String.class)) - .withConstructor(parameterTypes, constructorHint -> - constructorHint.withMode(ExecutableMode.INTROSPECT)), constructorHint -> { - assertThat(constructorHint.getParameterTypes()).containsOnlyOnceElementsOf(parameterTypes); - assertThat(constructorHint.getMode()).isEqualTo(ExecutableMode.INTROSPECT); - }); - } - - @Test - void createConstructorReuseBuilder() { + void createWithConstructorWithSameConstructorUpdatesEntry() { List parameterTypes = TypeReference.listOf(byte[].class, int.class); Builder builder = TypeHint.of(TypeReference.of(String.class)) .withConstructor(parameterTypes, ExecutableMode.INTROSPECT); - assertConstructorHint(builder.withConstructor(parameterTypes, constructorHint -> - constructorHint.withMode(ExecutableMode.INVOKE)), constructorHint -> { + assertConstructorHint(builder.withConstructor(parameterTypes, ExecutableMode.INVOKE), constructorHint -> { assertThat(constructorHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes); assertThat(constructorHint.getMode()).isEqualTo(ExecutableMode.INVOKE); }); } @Test - void createConstructorReuseBuilderAndApplyExecutableModePrecedence() { + void createWithConstructorAndSameConstructorAppliesExecutableModePrecedence() { List parameterTypes = TypeReference.listOf(byte[].class, int.class); - Builder builder = TypeHint.of(TypeReference.of(String.class)).withConstructor(parameterTypes, - constructorHint -> constructorHint.withMode(ExecutableMode.INVOKE)); - assertConstructorHint(builder.withConstructor(parameterTypes, constructorHint -> - constructorHint.withMode(ExecutableMode.INTROSPECT)), constructorHint -> { + Builder builder = TypeHint.of(TypeReference.of(String.class)) + .withConstructor(parameterTypes, ExecutableMode.INVOKE); + assertConstructorHint(builder.withConstructor(parameterTypes, ExecutableMode.INTROSPECT), constructorHint -> { assertThat(constructorHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes); assertThat(constructorHint.getMode()).isEqualTo(ExecutableMode.INVOKE); }); @@ -143,17 +110,6 @@ class TypeHintTests { @Test void createWithMethod() { - List parameterTypes = List.of(TypeReference.of(char[].class)); - assertMethodHint(TypeHint.of(TypeReference.of(String.class)) - .withMethod("valueOf", parameterTypes), methodHint -> { - assertThat(methodHint.getName()).isEqualTo("valueOf"); - assertThat(methodHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes); - assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE); - }); - } - - @Test - void createWithMethodAndMode() { List parameterTypes = List.of(TypeReference.of(char[].class)); assertMethodHint(TypeHint.of(TypeReference.of(String.class)) .withMethod("valueOf", parameterTypes, ExecutableMode.INTROSPECT), methodHint -> { @@ -164,36 +120,11 @@ class TypeHintTests { } @Test - void createWithMethodAndEmptyCustomizerAppliesConsistentDefault() { - List parameterTypes = List.of(TypeReference.of(char[].class)); - assertMethodHint(TypeHint.of(TypeReference.of(String.class)) - .withMethod("valueOf", parameterTypes, methodHint -> {}), methodHint -> { - assertThat(methodHint.getName()).isEqualTo("valueOf"); - assertThat(methodHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes); - assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE); - }); - } - - @Test - void createWithMethodAndCustomizerAppliesCustomization() { - List parameterTypes = List.of(TypeReference.of(char[].class)); - assertMethodHint(TypeHint.of(TypeReference.of(String.class)) - .withMethod("valueOf", parameterTypes, methodHint -> - methodHint.withMode(ExecutableMode.INTROSPECT)), methodHint -> { - assertThat(methodHint.getName()).isEqualTo("valueOf"); - assertThat(methodHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes); - assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INTROSPECT); - }); - } - - - @Test - void createWithMethodReuseBuilder() { + void createWithMethodWithSameMethodUpdatesEntry() { List parameterTypes = TypeReference.listOf(char[].class); Builder builder = TypeHint.of(TypeReference.of(String.class)) .withMethod("valueOf", parameterTypes, ExecutableMode.INTROSPECT); - assertMethodHint(builder.withMethod("valueOf", parameterTypes, - methodHint -> methodHint.withMode(ExecutableMode.INVOKE)), methodHint -> { + assertMethodHint(builder.withMethod("valueOf", parameterTypes, ExecutableMode.INVOKE), methodHint -> { assertThat(methodHint.getName()).isEqualTo("valueOf"); assertThat(methodHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes); assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE); @@ -201,12 +132,11 @@ class TypeHintTests { } @Test - void createWithMethodReuseBuilderAndApplyExecutableModePrecedence() { + void createWithMethodAndSameMethodAppliesExecutableModePrecedence() { List parameterTypes = TypeReference.listOf(char[].class); Builder builder = TypeHint.of(TypeReference.of(String.class)) .withMethod("valueOf", parameterTypes, ExecutableMode.INVOKE); - assertMethodHint(builder.withMethod("valueOf", parameterTypes, - methodHint -> methodHint.withMode(ExecutableMode.INTROSPECT)), methodHint -> { + assertMethodHint(builder.withMethod("valueOf", parameterTypes, ExecutableMode.INTROSPECT), methodHint -> { assertThat(methodHint.getName()).isEqualTo("valueOf"); assertThat(methodHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes); assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE); diff --git a/spring-core/src/test/java/org/springframework/aot/hint/predicate/ReflectionHintsPredicatesTests.java b/spring-core/src/test/java/org/springframework/aot/hint/predicate/ReflectionHintsPredicatesTests.java index ade56f2d0d5..2b8ebf338de 100644 --- a/spring-core/src/test/java/org/springframework/aot/hint/predicate/ReflectionHintsPredicatesTests.java +++ b/spring-core/src/test/java/org/springframework/aot/hint/predicate/ReflectionHintsPredicatesTests.java @@ -303,7 +303,7 @@ class ReflectionHintsPredicatesTests { @Test void reflectionOnAnyConstructorMatchesConstructorReflection() { - runtimeHints.reflection().registerConstructor(publicConstructor); + runtimeHints.reflection().registerConstructor(publicConstructor, ExecutableMode.INVOKE); assertPredicateMatches(reflection.onType(SampleClass.class).withAnyConstructor()); } @@ -458,7 +458,7 @@ class ReflectionHintsPredicatesTests { @Test void reflectionOnAnyMethodMatchesMethodReflection() { - runtimeHints.reflection().registerMethod(publicMethod); + runtimeHints.reflection().registerMethod(publicMethod, ExecutableMode.INVOKE); assertPredicateMatches(reflection.onType(SampleClass.class).withAnyMethod()); } diff --git a/spring-core/src/test/java/org/springframework/aot/nativex/FileNativeConfigurationWriterTests.java b/spring-core/src/test/java/org/springframework/aot/nativex/FileNativeConfigurationWriterTests.java index 2668180fa88..238ed1293cf 100644 --- a/spring-core/src/test/java/org/springframework/aot/nativex/FileNativeConfigurationWriterTests.java +++ b/spring-core/src/test/java/org/springframework/aot/nativex/FileNativeConfigurationWriterTests.java @@ -109,7 +109,7 @@ public class FileNativeConfigurationWriterTests { .withField("DEFAULT_CHARSET") .withField("defaultCharset") .withConstructor(TypeReference.listOf(List.class, boolean.class, MimeType.class), ExecutableMode.INTROSPECT) - .withMethod("setDefaultCharset", TypeReference.listOf(Charset.class)) + .withMethod("setDefaultCharset", TypeReference.listOf(Charset.class), ExecutableMode.INVOKE) .withMethod("getDefaultCharset", Collections.emptyList(), ExecutableMode.INTROSPECT)); generator.write(hints); assertEquals(""" diff --git a/spring-core/src/test/java/org/springframework/aot/nativex/ReflectionHintsWriterTests.java b/spring-core/src/test/java/org/springframework/aot/nativex/ReflectionHintsWriterTests.java index 0aa8bce1332..9aff60ece09 100644 --- a/spring-core/src/test/java/org/springframework/aot/nativex/ReflectionHintsWriterTests.java +++ b/spring-core/src/test/java/org/springframework/aot/nativex/ReflectionHintsWriterTests.java @@ -60,7 +60,7 @@ public class ReflectionHintsWriterTests { .withField("DEFAULT_CHARSET") .withField("defaultCharset") .withConstructor(TypeReference.listOf(List.class, boolean.class, MimeType.class), ExecutableMode.INTROSPECT) - .withMethod("setDefaultCharset", List.of(TypeReference.of(Charset.class))) + .withMethod("setDefaultCharset", List.of(TypeReference.of(Charset.class)), ExecutableMode.INVOKE) .withMethod("getDefaultCharset", Collections.emptyList(), ExecutableMode.INTROSPECT)); assertEquals(""" [ diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/support/InjectionCodeGenerator.java b/spring-orm/src/main/java/org/springframework/orm/jpa/support/InjectionCodeGenerator.java index e7e294d3838..3121df3dd18 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/support/InjectionCodeGenerator.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/support/InjectionCodeGenerator.java @@ -21,6 +21,7 @@ import java.lang.reflect.Member; import java.lang.reflect.Method; import org.springframework.aot.generate.AccessVisibility; +import org.springframework.aot.hint.ExecutableMode; import org.springframework.aot.hint.RuntimeHints; import org.springframework.javapoet.CodeBlock; import org.springframework.util.Assert; @@ -97,7 +98,7 @@ class InjectionCodeGenerator { AccessVisibility visibility = AccessVisibility.forMember(method); if (visibility == AccessVisibility.PRIVATE || visibility == AccessVisibility.PROTECTED) { - this.hints.reflection().registerMethod(method); + this.hints.reflection().registerMethod(method, ExecutableMode.INVOKE); code.addStatement("$T method = $T.findMethod($T.class, $S, $T.class)", Method.class, ReflectionUtils.class, method.getDeclaringClass(), method.getName(), method.getParameterTypes()[0]);