From 5aa858315947867f5b5caba34834d4c6c3d0ffa3 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Wed, 24 Aug 2022 15:44:54 +0200 Subject: [PATCH] Add shortcut to register an executable with a mode See gh-29011 --- .../AutowiredAnnotationBeanPostProcessor.java | 6 +---- ...BeanDefinitionPropertiesCodeGenerator.java | 6 +---- .../aot/InstanceSupplierCodeGenerator.java | 8 ++---- .../aot/BindingReflectionHintsRegistrar.java | 9 ++----- .../aot/hint/ReflectionHints.java | 26 +++++++++++++++++-- .../aot/hint/ReflectionHintsTests.java | 22 ++++++++++++++++ .../MessageMappingReflectiveProcessor.java | 4 +-- .../RequestMappingReflectiveProcessor.java | 4 +-- 8 files changed, 56 insertions(+), 29 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 9df071e8097..a1cff64cbb1 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 @@ -46,7 +46,6 @@ import org.springframework.aot.generate.GeneratedClass; import org.springframework.aot.generate.GeneratedMethod; import org.springframework.aot.generate.GenerationContext; import org.springframework.aot.generate.MethodReference; -import org.springframework.aot.hint.ExecutableHint; import org.springframework.aot.hint.ExecutableMode; import org.springframework.aot.hint.FieldHint; import org.springframework.aot.hint.RuntimeHints; @@ -914,9 +913,6 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA private static final String INSTANCE_PARAMETER = "instance"; - private static final Consumer INTROSPECT = builder -> builder - .withMode(ExecutableMode.INTROSPECT); - private static final Consumer ALLOW_WRITE = builder -> builder .allowWrite(true); @@ -1024,7 +1020,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA INSTANCE_PARAMETER); } else { - hints.reflection().registerMethod(method, INTROSPECT); + hints.reflection().registerMethod(method, ExecutableMode.INTROSPECT); CodeBlock arguments = new AutowiredArgumentsCodeGenerator(this.target, method).generateCode(method.getParameterTypes()); CodeBlock injectionCode = CodeBlock.of("args -> $L.$L($L)", 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 a85646d1f9d..39380e784d3 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 @@ -28,12 +28,10 @@ import java.util.Map; import java.util.Objects; import java.util.function.BiFunction; import java.util.function.BiPredicate; -import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import org.springframework.aot.generate.GeneratedMethods; -import org.springframework.aot.hint.ExecutableHint; import org.springframework.aot.hint.ExecutableMode; import org.springframework.aot.hint.RuntimeHints; import org.springframework.beans.BeanInfoFactory; @@ -83,8 +81,6 @@ class BeanDefinitionPropertiesCodeGenerator { private static final String BEAN_DEFINITION_VARIABLE = BeanRegistrationCodeFragments.BEAN_DEFINITION_VARIABLE; - private static final Consumer INVOKE_HINT = hint -> hint.withMode(ExecutableMode.INVOKE); - private static final BeanInfoFactory beanInfoFactory = new ExtendedBeanInfoFactory(); private final RuntimeHints hints; @@ -195,7 +191,7 @@ class BeanDefinitionPropertiesCodeGenerator { for (PropertyValue propertyValue : propertyValues) { Method writeMethod = writeMethods.get(propertyValue.getName()); if (writeMethod != null) { - this.hints.reflection().registerMethod(writeMethod, INVOKE_HINT); + this.hints.reflection().registerMethod(writeMethod, 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 1728e41ee3f..66bba162047 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 @@ -28,7 +28,6 @@ import org.springframework.aot.generate.AccessVisibility; import org.springframework.aot.generate.GeneratedMethod; import org.springframework.aot.generate.GeneratedMethods; import org.springframework.aot.generate.GenerationContext; -import org.springframework.aot.hint.ExecutableHint; import org.springframework.aot.hint.ExecutableMode; import org.springframework.beans.factory.support.InstanceSupplier; import org.springframework.beans.factory.support.RegisteredBean; @@ -69,9 +68,6 @@ class InstanceSupplierCodeGenerator { private static final CodeBlock NO_ARGS = CodeBlock.of(""); - private static final Consumer INTROSPECT = hint -> hint - .withMode(ExecutableMode.INTROSPECT); - private final GenerationContext generationContext; @@ -128,7 +124,7 @@ class InstanceSupplierCodeGenerator { Constructor constructor, boolean dependsOnBean, Class declaringClass) { this.generationContext.getRuntimeHints().reflection() - .registerConstructor(constructor, INTROSPECT); + .registerConstructor(constructor, ExecutableMode.INTROSPECT); if (!dependsOnBean && constructor.getParameterCount() == 0) { if (!this.allowDirectSupplierShortcut) { return CodeBlock.of("$T.using($T::new)", InstanceSupplier.class, @@ -225,7 +221,7 @@ class InstanceSupplierCodeGenerator { Class beanClass, Method factoryMethod, Class declaringClass, boolean dependsOnBean) { this.generationContext.getRuntimeHints().reflection() - .registerMethod(factoryMethod, INTROSPECT); + .registerMethod(factoryMethod, ExecutableMode.INTROSPECT); if (!dependsOnBean && factoryMethod.getParameterCount() == 0) { CodeBlock.Builder code = CodeBlock.builder(); code.add("$T.<$T>forFactoryMethod($T.class, $S)", BeanInstanceSupplier.class, diff --git a/spring-context/src/main/java/org/springframework/context/aot/BindingReflectionHintsRegistrar.java b/spring-context/src/main/java/org/springframework/context/aot/BindingReflectionHintsRegistrar.java index ad0af7827ed..c75612dbecb 100644 --- a/spring-context/src/main/java/org/springframework/context/aot/BindingReflectionHintsRegistrar.java +++ b/spring-context/src/main/java/org/springframework/context/aot/BindingReflectionHintsRegistrar.java @@ -25,12 +25,10 @@ import java.lang.reflect.RecordComponent; import java.lang.reflect.Type; import java.util.LinkedHashSet; import java.util.Set; -import java.util.function.Consumer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.aot.hint.ExecutableHint; import org.springframework.aot.hint.ExecutableMode; import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.ReflectionHints; @@ -54,9 +52,6 @@ public class BindingReflectionHintsRegistrar { private static final Log logger = LogFactory.getLog(BindingReflectionHintsRegistrar.class); - private static final Consumer INVOKE = builder -> builder - .withMode(ExecutableMode.INVOKE); - private static final String KOTLIN_COMPANION_SUFFIX = "$Companion"; /** @@ -127,7 +122,7 @@ public class BindingReflectionHintsRegistrar { } private void registerRecordHints(ReflectionHints hints, Set seen, Method method) { - hints.registerMethod(method, INVOKE); + hints.registerMethod(method, ExecutableMode.INVOKE); MethodParameter methodParameter = MethodParameter.forExecutable(method, -1); Type methodParameterType = methodParameter.getGenericParameterType(); if (!seen.contains(methodParameterType)) { @@ -138,7 +133,7 @@ public class BindingReflectionHintsRegistrar { private void registerPropertyHints(ReflectionHints hints, Set seen, @Nullable Method method, int parameterIndex) { if (method != null && method.getDeclaringClass() != Object.class && method.getDeclaringClass() != Enum.class) { - hints.registerMethod(method, INVOKE); + hints.registerMethod(method, ExecutableMode.INVOKE); MethodParameter methodParameter = MethodParameter.forExecutable(method, parameterIndex); Type methodParameterType = methodParameter.getGenericParameterType(); if (!seen.contains(methodParameterType)) { 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 b9460ed01bf..a30046c2cf8 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 @@ -158,6 +158,17 @@ public class ReflectionHints { typeHint -> typeHint.withConstructor(mapParameters(constructor), constructorHint)); } + /** + * Register the need for reflection on the specified {@link Constructor}, + * using the specified {@link ExecutableMode}. + * @param constructor the constructor that requires reflection + * @param mode the requested mode + * @return {@code this}, to facilitate method chaining + */ + public ReflectionHints registerConstructor(Constructor constructor, ExecutableMode mode) { + return registerConstructor(constructor, constructorHint -> constructorHint.withMode(mode)); + } + /** * Register the need for reflection on the specified {@link Constructor}, * enabling {@link ExecutableMode#INVOKE}. @@ -165,7 +176,7 @@ public class ReflectionHints { * @return {@code this}, to facilitate method chaining */ public ReflectionHints registerConstructor(Constructor constructor) { - return registerConstructor(constructor, constructorHint -> constructorHint.withMode(ExecutableMode.INVOKE)); + return registerConstructor(constructor, ExecutableMode.INVOKE); } /** @@ -179,6 +190,17 @@ public class ReflectionHints { typeHint -> typeHint.withMethod(method.getName(), mapParameters(method), methodHint)); } + /** + * Register the need for reflection on the specified {@link Method}, + * using the specified {@link ExecutableMode}. + * @param method the method that requires reflection + * @param mode the requested mode + * @return {@code this}, to facilitate method chaining + */ + public ReflectionHints registerMethod(Method method, ExecutableMode mode) { + return registerMethod(method, methodHint -> methodHint.withMode(mode)); + } + /** * Register the need for reflection on the specified {@link Method}, * enabling {@link ExecutableMode#INVOKE}. @@ -186,7 +208,7 @@ public class ReflectionHints { * @return {@code this}, to facilitate method chaining */ public ReflectionHints registerMethod(Method method) { - return registerMethod(method, methodHint -> methodHint.withMode(ExecutableMode.INVOKE)); + return registerMethod(method, ExecutableMode.INVOKE); } private List mapParameters(Executable executable) { 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 24799c99da8..8ab8d7180d1 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 @@ -176,6 +176,16 @@ class ReflectionHintsTests { }); } + @Test + void registerConstructorWithMode() { + this.reflectionHints.registerConstructor( + TestType.class.getDeclaredConstructors()[0], ExecutableMode.INTROSPECT); + assertTestTypeConstructorHint(constructorHint -> { + assertThat(constructorHint.getParameterTypes()).isEmpty(); + assertThat(constructorHint.getMode()).isEqualTo(ExecutableMode.INTROSPECT); + }); + } + @Test void registerConstructorWithEmptyCustomizerAppliesConsistentDefault() { this.reflectionHints.registerConstructor(TestType.class.getDeclaredConstructors()[0], @@ -219,6 +229,18 @@ class ReflectionHintsTests { }); } + @Test + void registerMethodWithMode() { + Method method = ReflectionUtils.findMethod(TestType.class, "setName", String.class); + assertThat(method).isNotNull(); + this.reflectionHints.registerMethod(method, ExecutableMode.INTROSPECT); + assertTestTypeMethodHints(methodHint -> { + assertThat(methodHint.getName()).isEqualTo("setName"); + assertThat(methodHint.getParameterTypes()).containsOnly(TypeReference.of(String.class)); + assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INTROSPECT); + }); + } + @Test void registerMethodWithEmptyCustomizerAppliesConsistentDefault() { Method method = ReflectionUtils.findMethod(TestType.class, "setName", String.class); diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/MessageMappingReflectiveProcessor.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/MessageMappingReflectiveProcessor.java index 4f7ac3942f3..8fa0be9f606 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/MessageMappingReflectiveProcessor.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/MessageMappingReflectiveProcessor.java @@ -64,13 +64,13 @@ class MessageMappingReflectiveProcessor implements ReflectiveProcessor { } protected void registerMethodHints(ReflectionHints hints, Method method) { - hints.registerMethod(method, hint -> hint.withMode(ExecutableMode.INVOKE)); + hints.registerMethod(method, ExecutableMode.INVOKE); registerParameterHints(hints, method); registerReturnValueHints(hints, method); } protected void registerParameterHints(ReflectionHints hints, Method method) { - hints.registerMethod(method, hint -> hint.withMode(ExecutableMode.INVOKE)); + hints.registerMethod(method, ExecutableMode.INVOKE); for (Parameter parameter : method.getParameters()) { MethodParameter methodParameter = MethodParameter.forParameter(parameter); if (Message.class.isAssignableFrom(methodParameter.getParameterType())) { diff --git a/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMappingReflectiveProcessor.java b/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMappingReflectiveProcessor.java index d61868aeb0e..ac45533aee3 100644 --- a/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMappingReflectiveProcessor.java +++ b/spring-web/src/main/java/org/springframework/web/bind/annotation/RequestMappingReflectiveProcessor.java @@ -63,13 +63,13 @@ class RequestMappingReflectiveProcessor implements ReflectiveProcessor { } protected void registerMethodHints(ReflectionHints hints, Method method) { - hints.registerMethod(method, hint -> hint.withMode(ExecutableMode.INVOKE)); + hints.registerMethod(method, ExecutableMode.INVOKE); registerParameterHints(hints, method); registerReturnValueHints(hints, method); } protected void registerParameterHints(ReflectionHints hints, Method method) { - hints.registerMethod(method, hint -> hint.withMode(ExecutableMode.INVOKE)); + hints.registerMethod(method, ExecutableMode.INVOKE); for (Parameter parameter : method.getParameters()) { MethodParameter methodParameter = MethodParameter.forParameter(parameter); if (methodParameter.hasParameterAnnotation(RequestBody.class) ||