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
This commit is contained in:
Stephane Nicoll 2022-08-24 07:21:02 +02:00
parent c8f7a76659
commit 58a2b79699
14 changed files with 97 additions and 165 deletions

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}
}
}

View File

@ -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> 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.Builder> 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.Builder> 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.Builder> 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<ExecutableHint.Builder> 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<ExecutableHint.Builder> methodHint) {
return registerType(TypeReference.of(method.getDeclaringClass()),
typeHint -> typeHint.withMethod(method.getName(), mapParameters(method), methodHint));

View File

@ -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<TypeReference> 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<TypeReference> parameterTypes, Consumer<ExecutableHint.Builder> constructorHint) {
@Deprecated
public Builder withConstructor(List<TypeReference> parameterTypes,
Consumer<ExecutableHint.Builder> constructorHint) {
ExecutableKey key = new ExecutableKey("<init>", 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<TypeReference> 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<TypeReference> parameterTypes, Consumer<ExecutableHint.Builder> methodHint) {
@Deprecated
public Builder withMethod(String name, List<TypeReference> parameterTypes,
Consumer<ExecutableHint.Builder> 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));

View File

@ -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);
}
}

View File

@ -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<ExecutableHint> 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<ExecutableHint> methodHint) {
assertThat(this.reflectionHints.typeHints()).singleElement().satisfies(typeHint -> {
assertThat(typeHint.getType().getCanonicalName()).isEqualTo(TestType.class.getCanonicalName());

View File

@ -70,16 +70,6 @@ class TypeHintTests {
@Test
void createWithConstructor() {
List<TypeReference> 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<TypeReference> 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<TypeReference> 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<TypeReference> 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<TypeReference> 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<TypeReference> 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<TypeReference> 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<TypeReference> 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<TypeReference> 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<TypeReference> 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<TypeReference> 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<TypeReference> 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);

View File

@ -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());
}

View File

@ -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("""

View File

@ -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("""
[

View File

@ -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]);