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 9df071e809..a1cff64cbb 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 a85646d1f9..39380e784d 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 1728e41ee3..66bba16204 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-beans/src/test/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGeneratorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGeneratorTests.java index e00d3a7792..78378f6b0a 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGeneratorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/aot/InstanceSupplierCodeGeneratorTests.java @@ -283,7 +283,7 @@ class InstanceSupplierCodeGeneratorTests { } private ThrowingConsumer hasMode(ExecutableMode mode) { - return hint -> assertThat(hint.getModes()).containsExactly(mode); + return hint -> assertThat(hint.getMode()).isEqualTo(mode); } @SuppressWarnings("unchecked") 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 ad0af7827e..c75612dbec 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-context/src/test/java/org/springframework/context/aot/BindingReflectionHintsRegistrarTests.java b/spring-context/src/test/java/org/springframework/context/aot/BindingReflectionHintsRegistrarTests.java index 76330a04f5..d3d4b4de38 100644 --- a/spring-context/src/test/java/org/springframework/context/aot/BindingReflectionHintsRegistrarTests.java +++ b/spring-context/src/test/java/org/springframework/context/aot/BindingReflectionHintsRegistrarTests.java @@ -77,7 +77,7 @@ public class BindingReflectionHintsRegistrarTests { assertThat(typeHint.getType()).isEqualTo(TypeReference.of(SampleClassWithGetter.class)); assertThat(typeHint.methods()).singleElement().satisfies(methodHint -> { assertThat(methodHint.getName()).isEqualTo("getName"); - assertThat(methodHint.getModes()).containsOnly(ExecutableMode.INVOKE); + assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE); }); }); } @@ -97,7 +97,7 @@ public class BindingReflectionHintsRegistrarTests { assertThat(typeHint.getType()).isEqualTo(TypeReference.of(SampleClassWithSetter.class)); assertThat(typeHint.methods()).singleElement().satisfies(methodHint -> { assertThat(methodHint.getName()).isEqualTo("setName"); - assertThat(methodHint.getModes()).containsOnly(ExecutableMode.INVOKE); + assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE); }); }); } @@ -125,11 +125,11 @@ public class BindingReflectionHintsRegistrarTests { assertThat(typeHint.methods()).satisfiesExactlyInAnyOrder( methodHint -> { assertThat(methodHint.getName()).isEqualTo("setNames"); - assertThat(methodHint.getModes()).containsOnly(ExecutableMode.INVOKE); + assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE); }, methodHint -> { assertThat(methodHint.getName()).isEqualTo("getNames"); - assertThat(methodHint.getModes()).containsOnly(ExecutableMode.INVOKE); + assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE); }); }); } @@ -180,7 +180,7 @@ public class BindingReflectionHintsRegistrarTests { assertThat(typeHint.methods()).singleElement().satisfies( methodHint -> { assertThat(methodHint.getName()).isEqualTo("getResolvableType"); - assertThat(methodHint.getModes()).containsOnly(ExecutableMode.INVOKE); + assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE); }); }); } @@ -218,7 +218,7 @@ public class BindingReflectionHintsRegistrarTests { 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); + assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE); }); }); } diff --git a/spring-context/src/test/kotlin/org/springframework/context/aot/KotlinBindingReflectionHintsRegistrarTests.kt b/spring-context/src/test/kotlin/org/springframework/context/aot/KotlinBindingReflectionHintsRegistrarTests.kt index be7bc11d58..c59c8fc01d 100644 --- a/spring-context/src/test/kotlin/org/springframework/context/aot/KotlinBindingReflectionHintsRegistrarTests.kt +++ b/spring-context/src/test/kotlin/org/springframework/context/aot/KotlinBindingReflectionHintsRegistrarTests.kt @@ -48,8 +48,7 @@ class KotlinBindingReflectionHintsRegistrarTests { assertThat(typeHint.methods()).singleElement() .satisfies(ThrowingConsumer { methodHint: ExecutableHint -> assertThat(methodHint.name).isEqualTo("getName") - assertThat(methodHint.modes) - .containsOnly(ExecutableMode.INVOKE) + assertThat(methodHint.mode).isEqualTo(ExecutableMode.INVOKE) }) }, ThrowingConsumer { typeHint: TypeHint -> @@ -57,7 +56,7 @@ class KotlinBindingReflectionHintsRegistrarTests { assertThat(typeHint.methods()).singleElement() .satisfies(ThrowingConsumer { methodHint: ExecutableHint -> assertThat(methodHint.name).isEqualTo("serializer") - assertThat(methodHint.modes).containsOnly(ExecutableMode.INVOKE) + assertThat(methodHint.mode).isEqualTo(ExecutableMode.INVOKE) }) }) } 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 483638bf67..d5a063e3b8 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 @@ -151,15 +151,15 @@ class InstrumentedMethodTests { @Test void classGetConstructorShouldMatchInstrospectConstructorHint() { - hints.reflection().registerType(String.class, typeHint -> typeHint.withConstructor(Collections.emptyList(), - constructorHint -> constructorHint.setModes(ExecutableMode.INTROSPECT))); + hints.reflection().registerType(String.class,typeHint -> + typeHint.withConstructor(Collections.emptyList(), ExecutableMode.INTROSPECT)); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCONSTRUCTOR, this.stringGetConstructor); } @Test void classGetConstructorShouldMatchInvokeConstructorHint() { - hints.reflection().registerType(String.class, typeHint -> typeHint.withConstructor(Collections.emptyList(), - constructorHint -> constructorHint.setModes(ExecutableMode.INVOKE))); + hints.reflection().registerType(String.class, typeHint -> + typeHint.withConstructor(Collections.emptyList(),ExecutableMode.INVOKE)); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETCONSTRUCTOR, this.stringGetConstructor); } @@ -201,15 +201,15 @@ class InstrumentedMethodTests { @Test void classGetDeclaredConstructorShouldMatchInstrospectConstructorHint() { - hints.reflection().registerType(String.class, typeHint -> typeHint.withConstructor(TypeReference.listOf(byte[].class, byte.class), - constructorHint -> constructorHint.setModes(ExecutableMode.INTROSPECT))); + hints.reflection().registerType(String.class, typeHint -> + typeHint.withConstructor(TypeReference.listOf(byte[].class, byte.class), ExecutableMode.INTROSPECT)); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDCONSTRUCTOR, this.stringGetDeclaredConstructor); } @Test void classGetDeclaredConstructorShouldMatchInvokeConstructorHint() { - hints.reflection().registerType(String.class, typeHint -> typeHint.withConstructor(TypeReference.listOf(byte[].class, byte.class), - constructorHint -> constructorHint.setModes(ExecutableMode.INVOKE))); + hints.reflection().registerType(String.class, typeHint -> + typeHint.withConstructor(TypeReference.listOf(byte[].class, byte.class), ExecutableMode.INVOKE)); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDCONSTRUCTOR, this.stringGetDeclaredConstructor); } @@ -237,7 +237,7 @@ class InstrumentedMethodTests { RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CONSTRUCTOR_NEWINSTANCE) .onInstance(String.class.getConstructor()).returnValue("").build(); hints.reflection().registerType(String.class, typeHint -> - typeHint.withConstructor(Collections.emptyList(), constructorHint -> constructorHint.withMode(ExecutableMode.INVOKE))); + typeHint.withConstructor(Collections.emptyList(), ExecutableMode.INVOKE)); assertThatInvocationMatches(InstrumentedMethod.CONSTRUCTOR_NEWINSTANCE, invocation); } @@ -246,7 +246,7 @@ class InstrumentedMethodTests { RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.CONSTRUCTOR_NEWINSTANCE) .onInstance(String.class.getConstructor()).returnValue("").build(); hints.reflection().registerType(String.class, typeHint -> - typeHint.withConstructor(Collections.emptyList(), constructorHint -> constructorHint.withMode(ExecutableMode.INTROSPECT))); + typeHint.withConstructor(Collections.emptyList(), ExecutableMode.INTROSPECT)); assertThatInvocationDoesNotMatch(InstrumentedMethod.CONSTRUCTOR_NEWINSTANCE, invocation); } @@ -285,7 +285,7 @@ class InstrumentedMethodTests { void classGetDeclaredMethodShouldMatchIntrospectMethodHint() { List parameterTypes = TypeReference.listOf(int.class, float.class); hints.reflection().registerType(String.class, typeHint -> - typeHint.withMethod("scale", parameterTypes, methodHint -> methodHint.withMode(ExecutableMode.INTROSPECT))); + typeHint.withMethod("scale", parameterTypes, ExecutableMode.INTROSPECT)); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDMETHOD, this.stringGetScaleMethod); } @@ -293,7 +293,7 @@ class InstrumentedMethodTests { void classGetDeclaredMethodShouldMatchInvokeMethodHint() { List parameterTypes = TypeReference.listOf(int.class, float.class); hints.reflection().registerType(String.class, typeHint -> - typeHint.withMethod("scale", parameterTypes, methodHint -> methodHint.withMode(ExecutableMode.INVOKE))); + typeHint.withMethod("scale", parameterTypes, ExecutableMode.INVOKE)); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETDECLAREDMETHOD, this.stringGetScaleMethod); } @@ -378,14 +378,14 @@ class InstrumentedMethodTests { @Test void classGetMethodShouldMatchIntrospectMethodHint() { hints.reflection().registerType(String.class, typeHint -> - typeHint.withMethod("toString", Collections.emptyList(), methodHint -> methodHint.setModes(ExecutableMode.INTROSPECT))); + typeHint.withMethod("toString", Collections.emptyList(), ExecutableMode.INTROSPECT)); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETMETHOD, this.stringGetToStringMethod); } @Test void classGetMethodShouldMatchInvokeMethodHint() { hints.reflection().registerType(String.class, typeHint -> - typeHint.withMethod("toString", Collections.emptyList(), methodHint -> methodHint.setModes(ExecutableMode.INVOKE))); + typeHint.withMethod("toString", Collections.emptyList(), ExecutableMode.INVOKE)); assertThatInvocationMatches(InstrumentedMethod.CLASS_GETMETHOD, this.stringGetToStringMethod); } @@ -412,7 +412,7 @@ class InstrumentedMethodTests { RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.METHOD_INVOKE) .onInstance(String.class.getMethod("startsWith", String.class)).withArguments("testString", new Object[] { "test" }).build(); hints.reflection().registerType(String.class, typeHint -> typeHint.withMethod("startsWith", - TypeReference.listOf(String.class), methodHint -> methodHint.withMode(ExecutableMode.INVOKE))); + TypeReference.listOf(String.class), ExecutableMode.INVOKE)); assertThatInvocationMatches(InstrumentedMethod.METHOD_INVOKE, invocation); } @@ -421,7 +421,7 @@ class InstrumentedMethodTests { RecordedInvocation invocation = RecordedInvocation.of(InstrumentedMethod.METHOD_INVOKE) .onInstance(String.class.getMethod("toString")).withArguments("", new Object[0]).build(); hints.reflection().registerType(String.class, typeHint -> - typeHint.withMethod("toString", Collections.emptyList(), methodHint -> methodHint.withMode(ExecutableMode.INTROSPECT))); + typeHint.withMethod("toString", Collections.emptyList(), ExecutableMode.INTROSPECT)); assertThatInvocationDoesNotMatch(InstrumentedMethod.METHOD_INVOKE, invocation); } diff --git a/spring-core/src/main/java/org/springframework/aot/hint/ExecutableHint.java b/spring-core/src/main/java/org/springframework/aot/hint/ExecutableHint.java index db06fafd89..cd11fdc1b0 100644 --- a/spring-core/src/main/java/org/springframework/aot/hint/ExecutableHint.java +++ b/spring-core/src/main/java/org/springframework/aot/hint/ExecutableHint.java @@ -19,12 +19,9 @@ package org.springframework.aot.hint; import java.lang.reflect.Constructor; import java.lang.reflect.Executable; import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.LinkedHashSet; import java.util.List; -import java.util.Set; -import org.springframework.util.ObjectUtils; +import org.springframework.lang.Nullable; /** * A hint that describes the need for reflection on a {@link Method} or @@ -37,13 +34,13 @@ public final class ExecutableHint extends MemberHint { private final List parameterTypes; - private final List modes; + private final ExecutableMode mode; private ExecutableHint(Builder builder) { super(builder.name); this.parameterTypes = List.copyOf(builder.parameterTypes); - this.modes = List.copyOf(builder.modes); + this.mode = (builder.mode != null ? builder.mode : ExecutableMode.INVOKE); } /** @@ -75,11 +72,11 @@ public final class ExecutableHint extends MemberHint { } /** - * Return the {@linkplain ExecutableMode modes} that apply to this hint. + * Return the {@linkplain ExecutableMode mode} that apply to this hint. * @return the modes */ - public List getModes() { - return this.modes; + public ExecutableMode getMode() { + return this.mode; } @@ -92,7 +89,8 @@ public final class ExecutableHint extends MemberHint { private final List parameterTypes; - private final Set modes = new LinkedHashSet<>(); + @Nullable + private ExecutableMode mode; Builder(String name, List parameterTypes) { @@ -101,12 +99,14 @@ public final class ExecutableHint extends MemberHint { } /** - * Add the specified {@linkplain ExecutableMode mode} if necessary. - * @param mode the mode to add + * Specify that the {@linkplain ExecutableMode mode} is required. + * @param mode the required mode * @return {@code this}, to facilitate method chaining */ public Builder withMode(ExecutableMode mode) { - this.modes.add(mode); + if (this.mode == null || !this.mode.includes(mode)) { + this.mode = mode; + } return this; } @@ -114,11 +114,15 @@ public final class ExecutableHint extends MemberHint { * Set the {@linkplain ExecutableMode modes} to use. * @param modes the mode to use * @return {@code this}, to facilitate method chaining + * @deprecated only a single mode can be set, use {@link #withMode(ExecutableMode)} instead */ + @Deprecated public Builder setModes(ExecutableMode... modes) { - this.modes.clear(); - if (!ObjectUtils.isEmpty(modes)) { - this.modes.addAll(Arrays.asList(modes)); + if (modes.length > 1) { + throw new UnsupportedOperationException(); + } + if (modes.length == 1) { + withMode(modes[0]); } return this; } diff --git a/spring-core/src/main/java/org/springframework/aot/hint/ExecutableMode.java b/spring-core/src/main/java/org/springframework/aot/hint/ExecutableMode.java index 9d5599ad83..7082859e75 100644 --- a/spring-core/src/main/java/org/springframework/aot/hint/ExecutableMode.java +++ b/spring-core/src/main/java/org/springframework/aot/hint/ExecutableMode.java @@ -18,6 +18,8 @@ package org.springframework.aot.hint; import java.lang.reflect.Executable; +import org.springframework.lang.Nullable; + /** * Represent the need of reflection for a given {@link Executable}. * @@ -37,4 +39,13 @@ public enum ExecutableMode { */ INVOKE; + /** + * Specify if this mode already includes the specified {@code other} mode. + * @param other the other mode to check + * @return {@code true} if this mode includes the other mode + */ + boolean includes(@Nullable ExecutableMode other) { + return (other == null || this.ordinal() >= other.ordinal()); + } + } diff --git a/spring-core/src/main/java/org/springframework/aot/hint/FieldHint.java b/spring-core/src/main/java/org/springframework/aot/hint/FieldHint.java index d6976f2034..593d5b5588 100644 --- a/spring-core/src/main/java/org/springframework/aot/hint/FieldHint.java +++ b/spring-core/src/main/java/org/springframework/aot/hint/FieldHint.java @@ -18,6 +18,8 @@ package org.springframework.aot.hint; import java.lang.reflect.Field; +import org.springframework.lang.Nullable; + /** * A hint that describes the need of reflection on a {@link Field}. * @@ -33,7 +35,7 @@ public final class FieldHint extends MemberHint { private FieldHint(Builder builder) { super(builder.name); - this.allowWrite = builder.allowWrite; + this.allowWrite = (builder.allowWrite != null) ? builder.allowWrite : true; this.allowUnsafeAccess = builder.allowUnsafeAccess; } @@ -61,7 +63,8 @@ public final class FieldHint extends MemberHint { private final String name; - private boolean allowWrite; + @Nullable + private Boolean allowWrite; private boolean allowUnsafeAccess; 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 c5386cfdbb..a30046c2cf 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,8 +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); } /** @@ -180,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}. @@ -187,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/main/java/org/springframework/aot/hint/TypeHint.java b/spring-core/src/main/java/org/springframework/aot/hint/TypeHint.java index 519eba94a6..e7781caabd 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 @@ -173,6 +173,16 @@ public final class TypeHint implements ConditionalHint { return this; } + /** + * Register the need for reflection on the field with the specified name, + * enabling write access. + * @param name the name of the field + * @return {@code this}, to facilitate method chaining + */ + public Builder withField(String name) { + return withField(name, fieldHint -> {}); + } + /** * Register the need for reflection on the constructor with the specified * parameter types. @@ -189,6 +199,27 @@ public final class TypeHint implements ConditionalHint { return this; } + /** + * Register the need for reflection on the constructor with the specified + * parameter types, using the specified {@link ExecutableMode}. + * @param parameterTypes the parameter types of the constructor + * @param mode the requested mode + * @return {@code this}, to facilitate method chaining + */ + public Builder withConstructor(List parameterTypes, ExecutableMode mode) { + return withConstructor(parameterTypes, constructorHint -> constructorHint.withMode(mode)); + } + + /** + * Register the need for reflection on the constructor with the specified + * parameter types, enabling {@link ExecutableMode#INVOKE}. + * @param parameterTypes the parameter types of the constructor + * @return {@code this}, to facilitate method chaining + */ + public Builder withConstructor(List parameterTypes) { + return withConstructor(parameterTypes, ExecutableMode.INVOKE); + } + /** * Register the need for reflection on the method with the specified name * and parameter types. @@ -205,6 +236,29 @@ public final class TypeHint implements ConditionalHint { return this; } + /** + * Register the need for reflection on the method with the specified name + * and parameter types, using the specified {@link ExecutableMode}. + * @param name the name of the method + * @param parameterTypes the parameter types of the constructor + * @param mode the requested mode + * @return {@code this}, to facilitate method chaining + */ + public Builder withMethod(String name, List parameterTypes, ExecutableMode mode) { + return withMethod(name, parameterTypes, methodHint -> methodHint.withMode(mode)); + } + + /** + * Register the need for reflection on the method with the specified name + * and parameter types, enabling {@link ExecutableMode#INVOKE}. + * @param name the name of the method + * @param parameterTypes the parameter types of the constructor + * @return {@code this}, to facilitate method chaining + */ + public Builder withMethod(String name, List parameterTypes) { + return withMethod(name, parameterTypes, ExecutableMode.INVOKE); + } + /** * Adds the specified {@linkplain MemberCategory member categories}. * @param memberCategories the categories to apply 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 4ad5e9363d..6d4d87277a 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 @@ -20,10 +20,7 @@ import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.function.Consumer; -import org.springframework.aot.hint.ExecutableHint.Builder; -import org.springframework.aot.hint.ExecutableMode; import org.springframework.aot.hint.ReflectionHints; /** @@ -36,8 +33,6 @@ import org.springframework.aot.hint.ReflectionHints; */ public class SimpleReflectiveProcessor implements ReflectiveProcessor { - private static final Consumer INVOKE_EXECUTABLE = hint -> hint.setModes(ExecutableMode.INVOKE); - @Override public void registerReflectionHints(ReflectionHints hints, AnnotatedElement element) { if (element instanceof Class type) { @@ -69,7 +64,7 @@ public class SimpleReflectiveProcessor implements ReflectiveProcessor { * @param constructor the constructor to process */ protected void registerConstructorHint(ReflectionHints hints, Constructor constructor) { - hints.registerConstructor(constructor, INVOKE_EXECUTABLE); + hints.registerConstructor(constructor); } /** @@ -87,7 +82,7 @@ public class SimpleReflectiveProcessor implements ReflectiveProcessor { * @param method the method to process */ protected void registerMethodHint(ReflectionHints hints, Method method) { - hints.registerMethod(method, INVOKE_EXECUTABLE); + hints.registerMethod(method); } } diff --git a/spring-core/src/main/java/org/springframework/aot/hint/predicate/ReflectionHintsPredicates.java b/spring-core/src/main/java/org/springframework/aot/hint/predicate/ReflectionHintsPredicates.java index cd4c902a84..61b7dbd845 100644 --- a/spring-core/src/main/java/org/springframework/aot/hint/predicate/ReflectionHintsPredicates.java +++ b/spring-core/src/main/java/org/springframework/aot/hint/predicate/ReflectionHintsPredicates.java @@ -263,11 +263,11 @@ public class ReflectionHintsPredicates { * and the configured {@code ExecutableMode} is compatibe */ static boolean includes(ExecutableHint hint, String name, - List parameterTypes, List executableModes) { + List parameterTypes, ExecutableMode executableModes) { return hint.getName().equals(name) && hint.getParameterTypes().equals(parameterTypes) - && (hint.getModes().contains(ExecutableMode.INVOKE) - || !executableModes.contains(ExecutableMode.INVOKE)); + && (hint.getMode().equals(ExecutableMode.INVOKE) + || !executableModes.equals(ExecutableMode.INVOKE)); } } @@ -302,7 +302,7 @@ public class ReflectionHintsPredicates { List parameters = Arrays.stream(this.executable.getParameterTypes()) .map(TypeReference::of).toList(); return includes(executableHint, "", - parameters, List.of(this.executableMode)); + parameters, this.executableMode); }); } @@ -341,7 +341,7 @@ public class ReflectionHintsPredicates { List parameters = Arrays.stream(this.executable.getParameterTypes()) .map(TypeReference::of).toList(); return includes(executableHint, this.executable.getName(), - parameters, List.of(this.executableMode)); + parameters, this.executableMode); }); } diff --git a/spring-core/src/main/java/org/springframework/aot/nativex/ReflectionHintsWriter.java b/spring-core/src/main/java/org/springframework/aot/nativex/ReflectionHintsWriter.java index bd22234210..d370570c9a 100644 --- a/spring-core/src/main/java/org/springframework/aot/nativex/ReflectionHintsWriter.java +++ b/spring-core/src/main/java/org/springframework/aot/nativex/ReflectionHintsWriter.java @@ -85,10 +85,10 @@ class ReflectionHintsWriter { private void handleExecutables(Map attributes, List hints) { addIfNotEmpty(attributes, "methods", hints.stream() - .filter(h -> h.getModes().contains(ExecutableMode.INVOKE) || h.getModes().isEmpty()) + .filter(h -> h.getMode().equals(ExecutableMode.INVOKE)) .map(this::toAttributes).toList()); addIfNotEmpty(attributes, "queriedMethods", hints.stream() - .filter(h -> h.getModes().contains(ExecutableMode.INTROSPECT)) + .filter(h -> h.getMode().equals(ExecutableMode.INTROSPECT)) .map(this::toAttributes).toList()); } diff --git a/spring-core/src/test/java/org/springframework/aot/hint/ExecutableModeTests.java b/spring-core/src/test/java/org/springframework/aot/hint/ExecutableModeTests.java new file mode 100644 index 0000000000..d19df791de --- /dev/null +++ b/spring-core/src/test/java/org/springframework/aot/hint/ExecutableModeTests.java @@ -0,0 +1,60 @@ +/* + * Copyright 2002-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.aot.hint; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link ExecutableMode}. + * + * @author Stephane Nicoll + */ +class ExecutableModeTests { + + @Test + void invokeIncludesNullMode() { + assertThat(ExecutableMode.INVOKE.includes(null)).isTrue(); + } + + @Test + void invokeIncludesIntrospect() { + assertThat(ExecutableMode.INVOKE.includes(ExecutableMode.INTROSPECT)).isTrue(); + } + + @Test + void invokeIncludesIncludes() { + assertThat(ExecutableMode.INVOKE.includes(ExecutableMode.INVOKE)).isTrue(); + } + + @Test + void introspectIncludesNullMode() { + assertThat(ExecutableMode.INTROSPECT.includes(null)).isTrue(); + } + + @Test + void introspectIncludesIntrospect() { + assertThat(ExecutableMode.INTROSPECT.includes(ExecutableMode.INTROSPECT)).isTrue(); + } + + @Test + void introspectDoesNotIncludeInvoke() { + assertThat(ExecutableMode.INTROSPECT.includes(ExecutableMode.INVOKE)).isFalse(); + } + +} 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 68458881dd..8ab8d7180d 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 @@ -121,14 +121,46 @@ class ReflectionHintsTests { } @Test - void registerField() { + void registerFieldAllowsWriteByDefault() { Field field = ReflectionUtils.findField(TestType.class, "field"); assertThat(field).isNotNull(); this.reflectionHints.registerField(field); + assertTestTypeFieldHint(fieldHint -> { + assertThat(fieldHint.getName()).isEqualTo("field"); + assertThat(fieldHint.isAllowWrite()).isTrue(); + assertThat(fieldHint.isAllowUnsafeAccess()).isFalse(); + }); + } + + @Test + void registerFieldWithEmptyCustomizerAppliesConsistentDefault() { + Field field = ReflectionUtils.findField(TestType.class, "field"); + assertThat(field).isNotNull(); + this.reflectionHints.registerField(field, fieldHint -> {}); + assertTestTypeFieldHint(fieldHint -> { + assertThat(fieldHint.getName()).isEqualTo("field"); + assertThat(fieldHint.isAllowWrite()).isTrue(); + assertThat(fieldHint.isAllowUnsafeAccess()).isFalse(); + }); + } + + @Test + void registerFieldWithCustomizerAppliesCustomization() { + Field field = ReflectionUtils.findField(TestType.class, "field"); + assertThat(field).isNotNull(); + this.reflectionHints.registerField(field, fieldHint -> + fieldHint.allowWrite(false).allowUnsafeAccess(true)); + assertTestTypeFieldHint(fieldHint -> { + assertThat(fieldHint.getName()).isEqualTo("field"); + assertThat(fieldHint.isAllowWrite()).isFalse(); + assertThat(fieldHint.isAllowUnsafeAccess()).isTrue(); + }); + } + + private void assertTestTypeFieldHint(Consumer fieldHint) { assertThat(this.reflectionHints.typeHints()).singleElement().satisfies(typeHint -> { assertThat(typeHint.getType().getCanonicalName()).isEqualTo(TestType.class.getCanonicalName()); - assertThat(typeHint.fields()).singleElement().satisfies(fieldHint -> - assertThat(fieldHint.getName()).isEqualTo("field")); + assertThat(typeHint.fields()).singleElement().satisfies(fieldHint); assertThat(typeHint.constructors()).isEmpty(); assertThat(typeHint.methods()).isEmpty(); assertThat(typeHint.getMemberCategories()).isEmpty(); @@ -138,14 +170,48 @@ 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 -> { + assertThat(constructorHint.getParameterTypes()).isEmpty(); + assertThat(constructorHint.getMode()).isEqualTo(ExecutableMode.INTROSPECT); + }); + } + + @Test + void registerConstructorWithEmptyCustomizerAppliesConsistentDefault() { + this.reflectionHints.registerConstructor(TestType.class.getDeclaredConstructors()[0], + constructorHint -> {}); + 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(); assertThat(typeHint.getType().getCanonicalName()).isEqualTo(TestType.class.getCanonicalName()); assertThat(typeHint.fields()).isEmpty(); - assertThat(typeHint.constructors()).singleElement().satisfies(constructorHint -> { - assertThat(constructorHint.getParameterTypes()).isEmpty(); - assertThat(constructorHint.getModes()).containsOnly(ExecutableMode.INVOKE); - }); + assertThat(typeHint.constructors()).singleElement().satisfies(constructorHint); assertThat(typeHint.methods()).isEmpty(); assertThat(typeHint.getMemberCategories()).isEmpty(); }); @@ -156,15 +222,56 @@ class ReflectionHintsTests { 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); + 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); + assertThat(method).isNotNull(); + this.reflectionHints.registerMethod(method, methodHint -> {}); + assertTestTypeMethodHints(methodHint -> { + assertThat(methodHint.getName()).isEqualTo("setName"); + assertThat(methodHint.getParameterTypes()).containsOnly(TypeReference.of(String.class)); + assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE); + }); + } + + @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()); assertThat(typeHint.fields()).isEmpty(); assertThat(typeHint.constructors()).isEmpty(); - assertThat(typeHint.methods()).singleElement().satisfies(methodHint -> { - assertThat(methodHint.getName()).isEqualTo("setName"); - assertThat(methodHint.getParameterTypes()).containsOnly(TypeReference.of(String.class)); - assertThat(methodHint.getModes()).containsOnly(ExecutableMode.INVOKE); - }); + assertThat(typeHint.methods()).singleElement().satisfies(methodHint); }); } 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 e781352c52..2bdf754c88 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 @@ -17,6 +17,7 @@ package org.springframework.aot.hint; import java.util.List; +import java.util.function.Consumer; import org.junit.jupiter.api.Test; @@ -54,16 +55,38 @@ class TypeHintTests { } @Test - void createWithField() { - TypeHint hint = TypeHint.of(TypeReference.of(String.class)) - .withField("value", fieldHint -> fieldHint.allowWrite(true)).build(); - assertThat(hint.fields()).singleElement().satisfies(fieldHint -> { + void createWithFieldAllowsWriteByDefault() { + assertFieldHint(TypeHint.of(TypeReference.of(String.class)) + .withField("value"), fieldHint -> { assertThat(fieldHint.getName()).isEqualTo("value"); assertThat(fieldHint.isAllowWrite()).isTrue(); assertThat(fieldHint.isAllowUnsafeAccess()).isFalse(); }); } + @Test + void createWithFieldAndEmptyCustomizerAppliesConsistentDefault() { + assertFieldHint(TypeHint.of(TypeReference.of(String.class)) + .withField("value", fieldHint -> {}), fieldHint -> { + assertThat(fieldHint.getName()).isEqualTo("value"); + assertThat(fieldHint.isAllowWrite()).isTrue(); + assertThat(fieldHint.isAllowUnsafeAccess()).isFalse(); + }); + } + + @Test + void createWithFieldAndCustomizerAppliesCustomization() { + assertFieldHint(TypeHint.of(TypeReference.of(String.class)) + .withField("value", fieldHint -> { + fieldHint.allowWrite(false); + fieldHint.allowUnsafeAccess(true); + }), fieldHint -> { + assertThat(fieldHint.getName()).isEqualTo("value"); + assertThat(fieldHint.isAllowWrite()).isFalse(); + assertThat(fieldHint.isAllowUnsafeAccess()).isTrue(); + }); + } + @Test void createWithFieldReuseBuilder() { Builder builder = TypeHint.of(TypeReference.of(String.class)); @@ -72,64 +95,175 @@ class TypeHintTests { fieldHint.allowWrite(true); fieldHint.allowUnsafeAccess(false); }); - TypeHint hint = builder.build(); - assertThat(hint.fields()).singleElement().satisfies(fieldHint -> { + assertFieldHint(builder, fieldHint -> { assertThat(fieldHint.getName()).isEqualTo("value"); assertThat(fieldHint.isAllowWrite()).isTrue(); assertThat(fieldHint.isAllowUnsafeAccess()).isFalse(); }); } + void assertFieldHint(Builder builder, Consumer fieldHint) { + TypeHint hint = builder.build(); + assertThat(hint.fields()).singleElement().satisfies(fieldHint); + assertThat(hint.constructors()).isEmpty(); + assertThat(hint.methods()).isEmpty(); + assertThat(hint.getMemberCategories()).isEmpty(); + } + @Test void createWithConstructor() { List parameterTypes = TypeReference.listOf(byte[].class, int.class); - TypeHint hint = TypeHint.of(TypeReference.of(String.class)).withConstructor(parameterTypes, - constructorHint -> constructorHint.withMode(ExecutableMode.INVOKE)).build(); - assertThat(hint.constructors()).singleElement().satisfies(constructorHint -> { + assertConstructorHint(TypeHint.of(TypeReference.of(String.class)) + .withConstructor(parameterTypes), constructorHint -> { assertThat(constructorHint.getParameterTypes()).containsOnlyOnceElementsOf(parameterTypes); - assertThat(constructorHint.getModes()).containsOnly(ExecutableMode.INVOKE); + 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 -> { + assertThat(constructorHint.getParameterTypes()).containsOnlyOnceElementsOf(parameterTypes); + assertThat(constructorHint.getMode()).isEqualTo(ExecutableMode.INTROSPECT); + }); + } + + @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() { + 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 -> { + assertThat(constructorHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes); + assertThat(constructorHint.getMode()).isEqualTo(ExecutableMode.INVOKE); + }); + } + + @Test + void createConstructorReuseBuilderAndApplyExecutableModePrecedence() { List parameterTypes = TypeReference.listOf(byte[].class, int.class); Builder builder = TypeHint.of(TypeReference.of(String.class)).withConstructor(parameterTypes, constructorHint -> constructorHint.withMode(ExecutableMode.INVOKE)); - TypeHint hint = builder.withConstructor(parameterTypes, constructorHint -> - constructorHint.withMode(ExecutableMode.INTROSPECT)).build(); - assertThat(hint.constructors()).singleElement().satisfies(constructorHint -> { + assertConstructorHint(builder.withConstructor(parameterTypes, constructorHint -> + constructorHint.withMode(ExecutableMode.INTROSPECT)), constructorHint -> { assertThat(constructorHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes); - assertThat(constructorHint.getModes()).containsOnly(ExecutableMode.INVOKE, ExecutableMode.INTROSPECT); + assertThat(constructorHint.getMode()).isEqualTo(ExecutableMode.INVOKE); }); } + void assertConstructorHint(Builder builder, Consumer constructorHint) { + TypeHint hint = builder.build(); + assertThat(hint.fields()).isEmpty(); + assertThat(hint.constructors()).singleElement().satisfies(constructorHint); + assertThat(hint.methods()).isEmpty(); + assertThat(hint.getMemberCategories()).isEmpty(); + } + @Test void createWithMethod() { List parameterTypes = List.of(TypeReference.of(char[].class)); - TypeHint hint = TypeHint.of(TypeReference.of(String.class)).withMethod("valueOf", parameterTypes, - methodHint -> methodHint.withMode(ExecutableMode.INVOKE)).build(); - assertThat(hint.methods()).singleElement().satisfies(methodHint -> { + assertMethodHint(TypeHint.of(TypeReference.of(String.class)) + .withMethod("valueOf", parameterTypes), methodHint -> { assertThat(methodHint.getName()).isEqualTo("valueOf"); assertThat(methodHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes); - assertThat(methodHint.getModes()).containsOnly(ExecutableMode.INVOKE); + assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE); }); } @Test - void createWithMethodReuseBuilder() { - List parameterTypes = TypeReference.listOf(char[].class); - Builder builder = TypeHint.of(TypeReference.of(String.class)).withMethod("valueOf", parameterTypes, - methodHint -> methodHint.withMode(ExecutableMode.INVOKE)); - TypeHint hint = builder.withMethod("valueOf", parameterTypes, - methodHint -> methodHint.setModes(ExecutableMode.INTROSPECT)).build(); - assertThat(hint.methods()).singleElement().satisfies(methodHint -> { + void createWithMethodAndMode() { + List parameterTypes = List.of(TypeReference.of(char[].class)); + assertMethodHint(TypeHint.of(TypeReference.of(String.class)) + .withMethod("valueOf", parameterTypes, ExecutableMode.INTROSPECT), methodHint -> { assertThat(methodHint.getName()).isEqualTo("valueOf"); assertThat(methodHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes); - assertThat(methodHint.getModes()).containsOnly(ExecutableMode.INTROSPECT); + assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INTROSPECT); }); } + @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() { + 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 -> { + assertThat(methodHint.getName()).isEqualTo("valueOf"); + assertThat(methodHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes); + assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE); + }); + } + + @Test + void createWithMethodReuseBuilderAndApplyExecutableModePrecedence() { + 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 -> { + assertThat(methodHint.getName()).isEqualTo("valueOf"); + assertThat(methodHint.getParameterTypes()).containsExactlyElementsOf(parameterTypes); + assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE); + }); + } + + void assertMethodHint(Builder builder, Consumer methodHint) { + TypeHint hint = builder.build(); + assertThat(hint.fields()).isEmpty(); + assertThat(hint.constructors()).isEmpty(); + assertThat(hint.methods()).singleElement().satisfies(methodHint); + assertThat(hint.getMemberCategories()).isEmpty(); + } + + @Test void createWithMemberCategory() { TypeHint hint = TypeHint.of(TypeReference.of(String.class)) diff --git a/spring-core/src/test/java/org/springframework/aot/hint/annotation/SimpleReflectiveProcessorTests.java b/spring-core/src/test/java/org/springframework/aot/hint/annotation/SimpleReflectiveProcessorTests.java index 4ab2c603d8..45bf6f7c9d 100644 --- a/spring-core/src/test/java/org/springframework/aot/hint/annotation/SimpleReflectiveProcessorTests.java +++ b/spring-core/src/test/java/org/springframework/aot/hint/annotation/SimpleReflectiveProcessorTests.java @@ -60,7 +60,7 @@ class SimpleReflectiveProcessorTests { assertThat(typeHint.getMemberCategories()).isEmpty(); assertThat(typeHint.constructors()).singleElement().satisfies(constructorHint -> { assertThat(constructorHint.getName()).isEqualTo(""); - assertThat(constructorHint.getModes()).containsExactly(ExecutableMode.INVOKE); + assertThat(constructorHint.getMode()).isEqualTo(ExecutableMode.INVOKE); assertThat(constructorHint.getParameterTypes()).containsExactly(TypeReference.of(String.class)); }); assertThat(typeHint.fields()).isEmpty(); @@ -93,7 +93,7 @@ class SimpleReflectiveProcessorTests { assertThat(typeHint.fields()).isEmpty(); assertThat(typeHint.methods()).singleElement().satisfies(methodHint -> { assertThat(methodHint.getName()).isEqualTo("setName"); - assertThat(methodHint.getModes()).containsExactly(ExecutableMode.INVOKE); + assertThat(methodHint.getMode()).isEqualTo(ExecutableMode.INVOKE); assertThat(methodHint.getParameterTypes()).containsExactly(TypeReference.of(String.class)); }); }); 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 d3390fe15b..6df28fe571 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 @@ -157,7 +157,7 @@ class ReflectionHintsPredicatesTests { @Test void constructorIntrospectionMatchesConstructorHint() { runtimeHints.reflection().registerType(SampleClass.class, typeHint -> - typeHint.withConstructor(Collections.emptyList(), constructorHint -> {})); + typeHint.withConstructor(Collections.emptyList(), ExecutableMode.INTROSPECT)); assertPredicateMatches(reflection.onConstructor(publicConstructor).introspect()); } @@ -192,15 +192,14 @@ class ReflectionHintsPredicatesTests { @Test void constructorInvocationDoesNotMatchConstructorHint() { runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint. - withConstructor(Collections.emptyList(), constructorHint -> {})); + withConstructor(Collections.emptyList(), ExecutableMode.INTROSPECT)); assertPredicateDoesNotMatch(reflection.onConstructor(publicConstructor).invoke()); } @Test void constructorInvocationMatchesConstructorInvocationHint() { runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint. - withConstructor(Collections.emptyList(), constructorHint -> - constructorHint.withMode(ExecutableMode.INVOKE))); + withConstructor(Collections.emptyList(), ExecutableMode.INVOKE)); assertPredicateMatches(reflection.onConstructor(publicConstructor).invoke()); } @@ -235,7 +234,7 @@ class ReflectionHintsPredicatesTests { @Test void privateConstructorIntrospectionMatchesConstructorHint() { runtimeHints.reflection().registerType(SampleClass.class, typeHint -> - typeHint.withConstructor(TypeReference.listOf(String.class), constructorHint -> {})); + typeHint.withConstructor(TypeReference.listOf(String.class), ExecutableMode.INTROSPECT)); assertPredicateMatches(reflection.onConstructor(privateConstructor).introspect()); } @@ -270,15 +269,14 @@ class ReflectionHintsPredicatesTests { @Test void privateConstructorInvocationDoesNotMatchConstructorHint() { runtimeHints.reflection().registerType(SampleClass.class, typeHint -> - typeHint.withConstructor(TypeReference.listOf(String.class), constructorHint -> {})); + typeHint.withConstructor(TypeReference.listOf(String.class), ExecutableMode.INTROSPECT)); assertPredicateDoesNotMatch(reflection.onConstructor(privateConstructor).invoke()); } @Test void privateConstructorInvocationMatchesConstructorInvocationHint() { runtimeHints.reflection().registerType(SampleClass.class, typeHint -> - typeHint.withConstructor(TypeReference.listOf(String.class), - constructorHint -> constructorHint.withMode(ExecutableMode.INVOKE))); + typeHint.withConstructor(TypeReference.listOf(String.class), ExecutableMode.INVOKE)); assertPredicateMatches(reflection.onConstructor(privateConstructor).invoke()); } @@ -317,8 +315,8 @@ class ReflectionHintsPredicatesTests { @Test void methodIntrospectionMatchesMethodHint() { - runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("publicMethod", Collections.emptyList(), methodHint -> { - })); + runtimeHints.reflection().registerType(SampleClass.class, typeHint -> + typeHint.withMethod("publicMethod", Collections.emptyList(), ExecutableMode.INTROSPECT)); assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").introspect()); } @@ -348,14 +346,15 @@ class ReflectionHintsPredicatesTests { @Test void methodInvocationDoesNotMatchMethodHint() { - runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("publicMethod", Collections.emptyList(), methodHint -> { - })); + runtimeHints.reflection().registerType(SampleClass.class, typeHint -> + typeHint.withMethod("publicMethod", Collections.emptyList(), ExecutableMode.INTROSPECT)); assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "publicMethod").invoke()); } @Test void methodInvocationMatchesMethodInvocationHint() { - runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("publicMethod", Collections.emptyList(), methodHint -> methodHint.withMode(ExecutableMode.INVOKE))); + runtimeHints.reflection().registerType(SampleClass.class, typeHint -> + typeHint.withMethod("publicMethod", Collections.emptyList(), ExecutableMode.INVOKE)); assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").invoke()); } @@ -385,8 +384,8 @@ class ReflectionHintsPredicatesTests { @Test void privateMethodIntrospectionMatchesMethodHint() { - runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("privateMethod", Collections.emptyList(), methodHint -> { - })); + runtimeHints.reflection().registerType(SampleClass.class, typeHint -> + typeHint.withMethod("privateMethod", Collections.emptyList(), ExecutableMode.INTROSPECT)); assertPredicateMatches(reflection.onMethod(SampleClass.class, "privateMethod").introspect()); } @@ -416,14 +415,15 @@ class ReflectionHintsPredicatesTests { @Test void privateMethodInvocationDoesNotMatchMethodHint() { - runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("privateMethod", Collections.emptyList(), methodHint -> { - })); + runtimeHints.reflection().registerType(SampleClass.class, typeHint -> + typeHint.withMethod("privateMethod", Collections.emptyList(), ExecutableMode.INTROSPECT)); assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "privateMethod").invoke()); } @Test void privateMethodInvocationMatchesMethodInvocationHint() { - runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withMethod("privateMethod", Collections.emptyList(), methodHint -> methodHint.withMode(ExecutableMode.INVOKE))); + runtimeHints.reflection().registerType(SampleClass.class, typeHint -> + typeHint.withMethod("privateMethod", Collections.emptyList(), ExecutableMode.INVOKE)); assertPredicateMatches(reflection.onMethod(SampleClass.class, "privateMethod").invoke()); } @@ -470,8 +470,8 @@ class ReflectionHintsPredicatesTests { @Test void fieldWriteReflectionDoesNotMatchFieldHint() { - runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withField("publicField", fieldHint -> { - })); + runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withField("publicField", + fieldHint -> fieldHint.allowWrite(false))); assertPredicateDoesNotMatch(reflection.onField(SampleClass.class, "publicField").allowWrite()); } 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 dc77407689..833ad61d6f 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 @@ -106,16 +106,14 @@ public class FileNativeConfigurationWriterTests { MemberCategory.INTROSPECT_PUBLIC_METHODS, MemberCategory.INTROSPECT_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.PUBLIC_CLASSES, MemberCategory.DECLARED_CLASSES) - .withField("DEFAULT_CHARSET", fieldBuilder -> {}) + .withField("DEFAULT_CHARSET", fieldBuilder -> fieldBuilder.allowWrite(false)) .withField("defaultCharset", fieldBuilder -> { fieldBuilder.allowWrite(true); fieldBuilder.allowUnsafeAccess(true); }) - .withConstructor(TypeReference.listOf(List.class, boolean.class, MimeType.class), constructorHint -> - constructorHint.withMode(ExecutableMode.INTROSPECT)) - .withMethod("setDefaultCharset", TypeReference.listOf(Charset.class), ctorBuilder -> {}) - .withMethod("getDefaultCharset", Collections.emptyList(), constructorHint -> - constructorHint.withMode(ExecutableMode.INTROSPECT)); + .withConstructor(TypeReference.listOf(List.class, boolean.class, MimeType.class), ExecutableMode.INTROSPECT) + .withMethod("setDefaultCharset", TypeReference.listOf(Charset.class)) + .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 9060f5b0af..62abe02908 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 @@ -49,26 +49,22 @@ public class ReflectionHintsWriterTests { @Test void one() throws JSONException { ReflectionHints hints = new ReflectionHints(); - hints.registerType(StringDecoder.class, builder -> { - builder - .onReachableType(TypeReference.of(String.class)) - .withMembers(MemberCategory.PUBLIC_FIELDS, MemberCategory.DECLARED_FIELDS, - MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS, MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS, - MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, - MemberCategory.INTROSPECT_PUBLIC_METHODS, MemberCategory.INTROSPECT_DECLARED_METHODS, - MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.INVOKE_DECLARED_METHODS, - MemberCategory.PUBLIC_CLASSES, MemberCategory.DECLARED_CLASSES) - .withField("DEFAULT_CHARSET", fieldBuilder -> {}) - .withField("defaultCharset", fieldBuilder -> { - fieldBuilder.allowWrite(true); - fieldBuilder.allowUnsafeAccess(true); - }) - .withConstructor(TypeReference.listOf(List.class, boolean.class, MimeType.class), constructorHint -> - constructorHint.withMode(ExecutableMode.INTROSPECT)) - .withMethod("setDefaultCharset", List.of(TypeReference.of(Charset.class)), ctorBuilder -> {}) - .withMethod("getDefaultCharset", Collections.emptyList(), constructorHint -> - constructorHint.withMode(ExecutableMode.INTROSPECT)); - }); + hints.registerType(StringDecoder.class, builder -> builder + .onReachableType(TypeReference.of(String.class)) + .withMembers(MemberCategory.PUBLIC_FIELDS, MemberCategory.DECLARED_FIELDS, + MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS, MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS, + MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, + MemberCategory.INTROSPECT_PUBLIC_METHODS, MemberCategory.INTROSPECT_DECLARED_METHODS, + MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.INVOKE_DECLARED_METHODS, + MemberCategory.PUBLIC_CLASSES, MemberCategory.DECLARED_CLASSES) + .withField("DEFAULT_CHARSET", fieldBuilder -> fieldBuilder.allowWrite(false)) + .withField("defaultCharset", fieldBuilder -> { + fieldBuilder.allowWrite(true); + fieldBuilder.allowUnsafeAccess(true); + }) + .withConstructor(TypeReference.listOf(List.class, boolean.class, MimeType.class), ExecutableMode.INTROSPECT) + .withMethod("setDefaultCharset", List.of(TypeReference.of(Charset.class))) + .withMethod("getDefaultCharset", Collections.emptyList(), ExecutableMode.INTROSPECT)); assertEquals(""" [ { @@ -120,7 +116,7 @@ public class ReflectionHintsWriterTests { void queriedMethods() throws JSONException { ReflectionHints hints = new ReflectionHints(); hints.registerType(Integer.class, builder -> builder.withMethod("parseInt", - TypeReference.listOf(String.class), b -> b.withMode(ExecutableMode.INTROSPECT))); + TypeReference.listOf(String.class), ExecutableMode.INTROSPECT)); assertEquals(""" [ @@ -141,7 +137,7 @@ public class ReflectionHintsWriterTests { void methods() throws JSONException { ReflectionHints hints = new ReflectionHints(); hints.registerType(Integer.class, builder -> builder.withMethod("parseInt", - TypeReference.listOf(String.class), b -> b.withMode(ExecutableMode.INVOKE))); + TypeReference.listOf(String.class), ExecutableMode.INVOKE)); assertEquals(""" [ @@ -162,7 +158,7 @@ public class ReflectionHintsWriterTests { void methodWithInnerClassParameter() throws JSONException { ReflectionHints hints = new ReflectionHints(); hints.registerType(Integer.class, builder -> builder.withMethod("test", - TypeReference.listOf(Inner.class), b -> b.withMode(ExecutableMode.INVOKE))); + TypeReference.listOf(Inner.class), ExecutableMode.INVOKE)); assertEquals(""" [ @@ -183,9 +179,9 @@ public class ReflectionHintsWriterTests { void methodAndQueriedMethods() throws JSONException { ReflectionHints hints = new ReflectionHints(); hints.registerType(Integer.class, builder -> builder.withMethod("parseInt", - TypeReference.listOf(String.class), b -> b.withMode(ExecutableMode.INVOKE))); + TypeReference.listOf(String.class), ExecutableMode.INVOKE)); hints.registerType(Integer.class, builder -> builder.withMethod("parseInt", - TypeReference.listOf(String.class), b -> b.withMode(ExecutableMode.INTROSPECT))); + TypeReference.listOf(String.class, int.class), ExecutableMode.INTROSPECT)); assertEquals(""" [ @@ -194,7 +190,7 @@ public class ReflectionHintsWriterTests { "queriedMethods": [ { "name": "parseInt", - "parameterTypes": ["java.lang.String"] + "parameterTypes": ["java.lang.String", "int"] } ], "methods": [ 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 f1004dc6ba..8fa0be9f60 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.setModes(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.setModes(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 c1661d39f6..ac45533aee 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.setModes(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.setModes(ExecutableMode.INVOKE)); + hints.registerMethod(method, ExecutableMode.INVOKE); for (Parameter parameter : method.getParameters()) { MethodParameter methodParameter = MethodParameter.forParameter(parameter); if (methodParameter.hasParameterAnnotation(RequestBody.class) ||