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:
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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("""
|
||||
|
||||
@@ -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("""
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user