From 71362c953c48de7dd428d84ddf660ee03d906a42 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Fri, 29 Nov 2024 14:43:59 +0100 Subject: [PATCH] Revisit RuntimeHints API The `RuntimeHints` API mainly reflects what is needed to write GraalVM reachability metadata. The latest GraalVM version simplified its format. This commit applies relevant simplifications as parts of it are not needed anymore. The new metadata format implies methods, constructors and fields introspection as soon as a reflection hint is registered for a type. As a result, `ExecutableMode.INTROSPECT`, and all `MemberCategory` values except `MemberCategory.INVOKE_*` are being deprecated. They have no replacement, as registering a type hint is enough. In practice, it is enough to replace this: ``` hints.reflection().registerType(MyType.class, MemberCategory.DECLARED_FIELDS); ``` By this: ``` hints.reflection().registerType(MyType.class); ``` As for `MemberCategory.PUBLIC_FIELDS` and `MemberCategory.DECLARED_FIELDS`, values were replaced by `INVOKE_PUBLIC_FIELDS` and `INVOKE_DECLARED_FIELDS` to make their original intent clearer and align with the rest of the API. Note, if you were using those values for reflection only, you can safely remove those hints in favor of a simple type hint. See gh-33847 --- .../aot/hint/ExecutableMode.java | 5 +- .../aot/hint/MemberCategory.java | 48 +++++++- .../predicate/ReflectionHintsPredicates.java | 104 ++++++++--------- .../predicate/ResourceHintsPredicates.java | 23 ++-- .../aot/hint/ExecutableHintTests.java | 1 + .../aot/hint/ExecutableModeTests.java | 3 +- .../aot/hint/ReflectionHintsTests.java | 1 + .../aot/hint/ResourceHintsTests.java | 7 +- .../aot/hint/RuntimeHintsTests.java | 8 +- .../aot/hint/TypeHintTests.java | 6 +- .../ReflectionHintsPredicatesTests.java | 105 ++++++++++-------- ...ilePatternResourceHintsRegistrarTests.java | 7 +- 12 files changed, 172 insertions(+), 146 deletions(-) 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 7082859e750..2de9e84c442 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -30,7 +30,10 @@ public enum ExecutableMode { /** * Only retrieving the {@link Executable} and its metadata is required. + * @deprecated with no replacement since introspection is included + * when {@link ReflectionHints#registerType(Class, MemberCategory...) adding a reflection hint for a type}. */ + @Deprecated(since= "7.0", forRemoval = true) INTROSPECT, /** diff --git a/spring-core/src/main/java/org/springframework/aot/hint/MemberCategory.java b/spring-core/src/main/java/org/springframework/aot/hint/MemberCategory.java index 2d09bb0e459..05b60208a6f 100644 --- a/spring-core/src/main/java/org/springframework/aot/hint/MemberCategory.java +++ b/spring-core/src/main/java/org/springframework/aot/hint/MemberCategory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -32,32 +32,62 @@ import java.lang.reflect.Method; public enum MemberCategory { /** - * A category that represents public {@linkplain Field fields}. + * A category that represents introspection on public {@linkplain Field fields}. + * @deprecated with no replacement since introspection is included + * when {@link ReflectionHints#registerType(Class, MemberCategory...) adding a reflection hint for a type}. + * Use {@link #INVOKE_PUBLIC_FIELDS} if getting/setting field values is required. * @see Class#getFields() */ + @Deprecated(since = "7.0", forRemoval = true) PUBLIC_FIELDS, /** - * A category that represents {@linkplain Class#getDeclaredFields() declared + * A category that represents introspection on {@linkplain Class#getDeclaredFields() declared * fields}: all fields defined by the class but not inherited fields. + * @deprecated with no replacement since introspection is included + * when {@link ReflectionHints#registerType(Class, MemberCategory...) adding a reflection hint for a type}. + * Use {@link #INVOKE_DECLARED_FIELDS} if getting/setting field values is required. * @see Class#getDeclaredFields() */ + @Deprecated(since = "7.0", forRemoval = true) DECLARED_FIELDS, + /** + * A category that represents getting/setting values on public {@linkplain Field fields}. + * @see Field#get(Object) + * @see Field#set(Object, Object) + * @since 7.0 + */ + INVOKE_PUBLIC_FIELDS, + + /** + * A category that represents getting/setting values on declared {@linkplain Field fields}. + * @see Field#get(Object) + * @see Field#set(Object, Object) + * @since 7.0 + */ + INVOKE_DECLARED_FIELDS, + /** * A category that defines public {@linkplain Constructor constructors} can * be introspected but not invoked. + * @deprecated with no replacement since introspection is included + * when {@link ReflectionHints#registerType(Class, MemberCategory...) adding a reflection hint for a type}. * @see Class#getConstructors() * @see ExecutableMode#INTROSPECT */ + @Deprecated(since = "7.0", forRemoval = true) INTROSPECT_PUBLIC_CONSTRUCTORS, /** * A category that defines {@linkplain Class#getDeclaredConstructors() all * constructors} can be introspected but not invoked. + * @deprecated with no replacement since introspection is included + * when {@link ReflectionHints#registerType(Class, MemberCategory...) adding a reflection hint for a type}. * @see Class#getDeclaredConstructors() * @see ExecutableMode#INTROSPECT */ + @Deprecated(since = "7.0", forRemoval = true) INTROSPECT_DECLARED_CONSTRUCTORS, /** @@ -79,17 +109,23 @@ public enum MemberCategory { /** * A category that defines public {@linkplain Method methods}, including * inherited ones, can be introspected but not invoked. + * @deprecated with no replacement since introspection is added by default + * when {@link ReflectionHints#registerType(Class, MemberCategory...) adding a reflection hint for a type}. * @see Class#getMethods() * @see ExecutableMode#INTROSPECT */ + @Deprecated(since = "7.0", forRemoval = true) INTROSPECT_PUBLIC_METHODS, /** * A category that defines {@linkplain Class#getDeclaredMethods() all * methods}, excluding inherited ones, can be introspected but not invoked. + * @deprecated with no replacement since introspection is added by default + * when {@link ReflectionHints#registerType(Class, MemberCategory...) adding a reflection hint for a type}. * @see Class#getDeclaredMethods() * @see ExecutableMode#INTROSPECT */ + @Deprecated(since = "7.0", forRemoval = true) INTROSPECT_DECLARED_METHODS, /** @@ -114,7 +150,10 @@ public enum MemberCategory { *

Contrary to other categories, this does not register any particular * reflection for inner classes but rather makes sure they are available * via a call to {@link Class#getClasses}. + * @deprecated with no replacement since introspection is included + * when {@link ReflectionHints#registerType(Class, MemberCategory...) adding a reflection hint for a type}. */ + @Deprecated(since = "7.0", forRemoval = true) PUBLIC_CLASSES, /** @@ -123,7 +162,10 @@ public enum MemberCategory { *

Contrary to other categories, this does not register any particular * reflection for inner classes but rather makes sure they are available * via a call to {@link Class#getDeclaredClasses}. + * @deprecated with no replacement since introspection is included + * when {@link ReflectionHints#registerType(Class, MemberCategory...) adding a reflection hint for a type}. */ + @Deprecated(since = "7.0", forRemoval = true) DECLARED_CLASSES } 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 a9f87de77ac..69ba572e350 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 @@ -255,6 +255,7 @@ public class ReflectionHintsPredicates { } + @SuppressWarnings("removal") public abstract static class ExecutableHintPredicate implements Predicate { protected final T executable; @@ -299,6 +300,7 @@ public class ReflectionHintsPredicates { } + @SuppressWarnings("removal") public static class ConstructorHintPredicate extends ExecutableHintPredicate> { ConstructorHintPredicate(Constructor constructor) { @@ -308,28 +310,17 @@ public class ReflectionHintsPredicates { @Override public boolean test(RuntimeHints runtimeHints) { return (new TypeHintPredicate(TypeReference.of(this.executable.getDeclaringClass())) - .withAnyMemberCategory(getPublicMemberCategories()) - .and(hints -> Modifier.isPublic(this.executable.getModifiers()))) - .or(new TypeHintPredicate(TypeReference.of(this.executable.getDeclaringClass())).withAnyMemberCategory(getDeclaredMemberCategories())) + .and(hints -> this.executableMode == ExecutableMode.INTROSPECT)) + .or(new TypeHintPredicate(TypeReference.of(this.executable.getDeclaringClass())) + .withMemberCategory(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS) + .and(hints -> Modifier.isPublic(this.executable.getModifiers())) + .and(hints -> this.executableMode == ExecutableMode.INVOKE)) + .or(new TypeHintPredicate(TypeReference.of(this.executable.getDeclaringClass())) + .withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS) + .and(hints -> this.executableMode == ExecutableMode.INVOKE)) .or(exactMatch()).test(runtimeHints); } - MemberCategory[] getPublicMemberCategories() { - if (this.executableMode == ExecutableMode.INTROSPECT) { - return new MemberCategory[] { MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS, - MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS }; - } - return new MemberCategory[] { MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS }; - } - - MemberCategory[] getDeclaredMemberCategories() { - if (this.executableMode == ExecutableMode.INTROSPECT) { - return new MemberCategory[] { MemberCategory.INTROSPECT_DECLARED_CONSTRUCTORS, - MemberCategory.INVOKE_DECLARED_CONSTRUCTORS }; - } - return new MemberCategory[] { MemberCategory.INVOKE_DECLARED_CONSTRUCTORS }; - } - @Override Predicate exactMatch() { return hints -> { @@ -343,6 +334,7 @@ public class ReflectionHintsPredicates { } + @SuppressWarnings("removal") public static class MethodHintPredicate extends ExecutableHintPredicate { MethodHintPredicate(Method method) { @@ -352,31 +344,18 @@ public class ReflectionHintsPredicates { @Override public boolean test(RuntimeHints runtimeHints) { return (new TypeHintPredicate(TypeReference.of(this.executable.getDeclaringClass())) - .withAnyMemberCategory(getPublicMemberCategories()) - .and(hints -> Modifier.isPublic(this.executable.getModifiers()))) - .or(new TypeHintPredicate(TypeReference.of(this.executable.getDeclaringClass())) - .withAnyMemberCategory(getDeclaredMemberCategories()) - .and(hints -> !Modifier.isPublic(this.executable.getModifiers()))) + .and(hints -> this.executableMode == ExecutableMode.INTROSPECT)) + .or((new TypeHintPredicate(TypeReference.of(this.executable.getDeclaringClass())) + .withMemberCategory(MemberCategory.INVOKE_PUBLIC_METHODS) + .and(hints -> Modifier.isPublic(this.executable.getModifiers())) + .and(hints -> this.executableMode == ExecutableMode.INVOKE))) + .or((new TypeHintPredicate(TypeReference.of(this.executable.getDeclaringClass())) + .withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS) + .and(hints -> !Modifier.isPublic(this.executable.getModifiers())) + .and(hints -> this.executableMode == ExecutableMode.INVOKE))) .or(exactMatch()).test(runtimeHints); } - MemberCategory[] getPublicMemberCategories() { - if (this.executableMode == ExecutableMode.INTROSPECT) { - return new MemberCategory[] { MemberCategory.INTROSPECT_PUBLIC_METHODS, - MemberCategory.INVOKE_PUBLIC_METHODS }; - } - return new MemberCategory[] { MemberCategory.INVOKE_PUBLIC_METHODS }; - } - - MemberCategory[] getDeclaredMemberCategories() { - - if (this.executableMode == ExecutableMode.INTROSPECT) { - return new MemberCategory[] { MemberCategory.INTROSPECT_DECLARED_METHODS, - MemberCategory.INVOKE_DECLARED_METHODS }; - } - return new MemberCategory[] { MemberCategory.INVOKE_DECLARED_METHODS }; - } - @Override Predicate exactMatch() { return hints -> { @@ -394,31 +373,40 @@ public class ReflectionHintsPredicates { private final Field field; + @Nullable + private ExecutableMode executableMode; + FieldHintPredicate(Field field) { this.field = field; } + /** + * Refine the current predicate to only match if an invocation hint is registered for this field. + * @return the refined {@link RuntimeHints} predicate + * @since 7.0 + */ + public FieldHintPredicate invocation() { + this.executableMode = ExecutableMode.INVOKE; + return this; + } + @Override public boolean test(RuntimeHints runtimeHints) { TypeHint typeHint = runtimeHints.reflection().getTypeHint(this.field.getDeclaringClass()); - if (typeHint == null) { - return false; - } - return memberCategoryMatch(typeHint) || exactMatch(typeHint); - } - - private boolean memberCategoryMatch(TypeHint typeHint) { - if (Modifier.isPublic(this.field.getModifiers())) { - return typeHint.getMemberCategories().contains(MemberCategory.PUBLIC_FIELDS); + if (typeHint != null) { + if (this.executableMode == ExecutableMode.INVOKE) { + if (Modifier.isPublic(this.field.getModifiers())) { + return typeHint.getMemberCategories().contains(MemberCategory.INVOKE_PUBLIC_FIELDS); + } + else { + return typeHint.getMemberCategories().contains(MemberCategory.INVOKE_DECLARED_FIELDS); + } + } + else { + return true; + } } - else { - return typeHint.getMemberCategories().contains(MemberCategory.DECLARED_FIELDS); - } - } - - private boolean exactMatch(TypeHint typeHint) { - return typeHint.fields().anyMatch(fieldHint -> - this.field.getName().equals(fieldHint.getName())); + return false; } } diff --git a/spring-core/src/main/java/org/springframework/aot/hint/predicate/ResourceHintsPredicates.java b/spring-core/src/main/java/org/springframework/aot/hint/predicate/ResourceHintsPredicates.java index d59d48af6cf..30235469436 100644 --- a/spring-core/src/main/java/org/springframework/aot/hint/predicate/ResourceHintsPredicates.java +++ b/spring-core/src/main/java/org/springframework/aot/hint/predicate/ResourceHintsPredicates.java @@ -19,14 +19,13 @@ package org.springframework.aot.hint.predicate; import java.util.ArrayList; import java.util.List; import java.util.function.Predicate; -import java.util.regex.Pattern; import org.springframework.aot.hint.ResourceHints; import org.springframework.aot.hint.ResourcePatternHint; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.TypeReference; +import org.springframework.util.AntPathMatcher; import org.springframework.util.Assert; -import org.springframework.util.ConcurrentLruCache; /** * Generator of {@link ResourceHints} predicates, testing whether the given hints @@ -39,7 +38,7 @@ import org.springframework.util.ConcurrentLruCache; */ public class ResourceHintsPredicates { - private static final ConcurrentLruCache CACHED_RESOURCE_PATTERNS = new ConcurrentLruCache<>(32, ResourcePatternHint::toRegex); + private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher(); ResourceHintsPredicates() { } @@ -100,26 +99,18 @@ public class ResourceHintsPredicates { return hints -> { AggregatedResourcePatternHints aggregatedResourcePatternHints = AggregatedResourcePatternHints.of( hints.resources()); - boolean isExcluded = aggregatedResourcePatternHints.excludes().stream().anyMatch(excluded -> - CACHED_RESOURCE_PATTERNS.get(excluded).matcher(resourceNameToUse).matches()); - if (isExcluded) { - return false; - } return aggregatedResourcePatternHints.includes().stream().anyMatch(included -> - CACHED_RESOURCE_PATTERNS.get(included).matcher(resourceNameToUse).matches()); + PATH_MATCHER.match(included.getPattern(), resourceNameToUse)); }; } - private record AggregatedResourcePatternHints(List includes, List excludes) { + private record AggregatedResourcePatternHints(List includes) { static AggregatedResourcePatternHints of(ResourceHints resourceHints) { List includes = new ArrayList<>(); - List excludes = new ArrayList<>(); - resourceHints.resourcePatternHints().forEach(resourcePatternHint -> { - includes.addAll(resourcePatternHint.getIncludes()); - excludes.addAll(resourcePatternHint.getExcludes()); - }); - return new AggregatedResourcePatternHints(includes, excludes); + resourceHints.resourcePatternHints().forEach(resourcePatternHint -> + includes.addAll(resourcePatternHint.getIncludes())); + return new AggregatedResourcePatternHints(includes); } } diff --git a/spring-core/src/test/java/org/springframework/aot/hint/ExecutableHintTests.java b/spring-core/src/test/java/org/springframework/aot/hint/ExecutableHintTests.java index f817ba56b82..978934af799 100644 --- a/spring-core/src/test/java/org/springframework/aot/hint/ExecutableHintTests.java +++ b/spring-core/src/test/java/org/springframework/aot/hint/ExecutableHintTests.java @@ -28,6 +28,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Phillip Webb * @since 6.0 */ +@SuppressWarnings("removal") class ExecutableHintTests { @Test 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 index d19df791de2..ec16727bb9c 100644 --- a/spring-core/src/test/java/org/springframework/aot/hint/ExecutableModeTests.java +++ b/spring-core/src/test/java/org/springframework/aot/hint/ExecutableModeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 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. @@ -25,6 +25,7 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Stephane Nicoll */ +@SuppressWarnings("removal") class ExecutableModeTests { @Test 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 5e4c3cc14f8..7756980bdb1 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 @@ -37,6 +37,7 @@ import static org.mockito.Mockito.verifyNoInteractions; * @author Stephane Nicoll * @author Sebastien Deleuze */ +@SuppressWarnings("removal") class ReflectionHintsTests { private final ReflectionHints reflectionHints = new ReflectionHints(); diff --git a/spring-core/src/test/java/org/springframework/aot/hint/ResourceHintsTests.java b/spring-core/src/test/java/org/springframework/aot/hint/ResourceHintsTests.java index b8393dd52ba..19e57e873b7 100644 --- a/spring-core/src/test/java/org/springframework/aot/hint/ResourceHintsTests.java +++ b/spring-core/src/test/java/org/springframework/aot/hint/ResourceHintsTests.java @@ -117,7 +117,7 @@ class ResourceHintsTests { @Test void registerPatternWithIncludesAndExcludes() { this.resourceHints.registerPattern(resourceHint -> - resourceHint.includes("com/example/*.properties").excludes("com/example/to-ignore.properties")); + resourceHint.includes("com/example/*.properties")); assertThat(this.resourceHints.resourcePatternHints()).singleElement().satisfies(patternOf( List.of("/", "com", "com/example", "com/example/*.properties"), List.of("com/example/to-ignore.properties"))); @@ -198,10 +198,7 @@ class ResourceHintsTests { } private Consumer patternOf(List includes, List excludes) { - return pattern -> { - assertThat(pattern.getIncludes()).map(ResourcePatternHint::getPattern).containsExactlyInAnyOrderElementsOf(includes); - assertThat(pattern.getExcludes()).map(ResourcePatternHint::getPattern).containsExactlyElementsOf(excludes); - }; + return pattern -> assertThat(pattern.getIncludes()).map(ResourcePatternHint::getPattern).containsExactlyInAnyOrderElementsOf(includes); } static class Nested { diff --git a/spring-core/src/test/java/org/springframework/aot/hint/RuntimeHintsTests.java b/spring-core/src/test/java/org/springframework/aot/hint/RuntimeHintsTests.java index f88170d2870..1778a82f039 100644 --- a/spring-core/src/test/java/org/springframework/aot/hint/RuntimeHintsTests.java +++ b/spring-core/src/test/java/org/springframework/aot/hint/RuntimeHintsTests.java @@ -47,11 +47,9 @@ class RuntimeHintsTests { @Test void resourceHintWithClass() { this.hints.resources().registerType(String.class); - assertThat(this.hints.resources().resourcePatternHints()).singleElement().satisfies(resourceHint -> { - assertThat(resourceHint.getIncludes()).map(ResourcePatternHint::getPattern) - .containsExactlyInAnyOrder("/", "java", "java/lang", "java/lang/String.class"); - assertThat(resourceHint.getExcludes()).isEmpty(); - }); + assertThat(this.hints.resources().resourcePatternHints()).singleElement().satisfies(resourceHint -> + assertThat(resourceHint.getIncludes()).map(ResourcePatternHint::getPattern) + .containsExactlyInAnyOrder("/", "java", "java/lang", "java/lang/String.class")); } @Test 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 5c84ec93bbc..4aa0f6b97a0 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 @@ -31,6 +31,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException * * @author Stephane Nicoll */ +@SuppressWarnings("removal") class TypeHintTests { @Test @@ -169,9 +170,8 @@ class TypeHintTests { void builtWithAppliesMemberCategories() { TypeHint.Builder builder = new TypeHint.Builder(TypeReference.of(String.class)); assertThat(builder.build().getMemberCategories()).isEmpty(); - TypeHint.builtWith(MemberCategory.DECLARED_CLASSES, MemberCategory.DECLARED_FIELDS).accept(builder); - assertThat(builder.build().getMemberCategories()).containsExactlyInAnyOrder(MemberCategory.DECLARED_CLASSES, - MemberCategory.DECLARED_FIELDS); + TypeHint.builtWith(MemberCategory.DECLARED_FIELDS).accept(builder); + assertThat(builder.build().getMemberCategories()).containsExactly(MemberCategory.DECLARED_FIELDS); } } 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 896da82a60d..3ba1db988ab 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -40,6 +40,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; * * @author Brian Clozel */ +@SuppressWarnings("removal") class ReflectionHintsPredicatesTests { private static Constructor privateConstructor; @@ -160,6 +161,12 @@ class ReflectionHintsPredicatesTests { assertPredicateDoesNotMatch(reflection.onConstructor(publicConstructor).introspect()); } + @Test + void constructorIntrospectionMatchesTypeHint() { + runtimeHints.reflection().registerType(SampleClass.class); + assertPredicateMatches(reflection.onConstructor(publicConstructor).introspect()); + } + @Test void constructorIntrospectionMatchesConstructorHint() { runtimeHints.reflection().registerType(SampleClass.class, typeHint -> @@ -230,22 +237,16 @@ class ReflectionHintsPredicatesTests { } @Test - void privateConstructorIntrospectionMatchesConstructorHint() { - runtimeHints.reflection().registerType(SampleClass.class, typeHint -> - typeHint.withConstructor(TypeReference.listOf(String.class), ExecutableMode.INTROSPECT)); + void privateConstructorIntrospectionMatchesTypeHint() { + runtimeHints.reflection().registerType(SampleClass.class); assertPredicateMatches(reflection.onConstructor(privateConstructor).introspect()); } @Test - void privateConstructorIntrospectionDoesNotMatchIntrospectPublicConstructors() { - runtimeHints.reflection().registerType(SampleClass.class, MemberCategory.INTROSPECT_PUBLIC_CONSTRUCTORS); - assertPredicateDoesNotMatch(reflection.onConstructor(privateConstructor).introspect()); - } - - @Test - void privateConstructorIntrospectionDoesNotMatchInvokePublicConstructors() { - runtimeHints.reflection().registerType(SampleClass.class, MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS); - assertPredicateDoesNotMatch(reflection.onConstructor(privateConstructor).introspect()); + void privateConstructorIntrospectionMatchesConstructorHint() { + runtimeHints.reflection().registerType(SampleClass.class, typeHint -> + typeHint.withConstructor(TypeReference.listOf(String.class), ExecutableMode.INTROSPECT)); + assertPredicateMatches(reflection.onConstructor(privateConstructor).introspect()); } @Test @@ -303,6 +304,12 @@ class ReflectionHintsPredicatesTests { @Nested class ReflectionOnMethod { + @Test + void methodIntrospectionMatchesTypeHint() { + runtimeHints.reflection().registerType(SampleClass.class); + assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").introspect()); + } + @Test void methodIntrospectionMatchesMethodHint() { runtimeHints.reflection().registerType(SampleClass.class, typeHint -> @@ -328,18 +335,6 @@ class ReflectionHintsPredicatesTests { assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").introspect()); } - @Test - void methodIntrospectionDoesNotMatchIntrospectDeclaredMethods() { - runtimeHints.reflection().registerType(SampleClass.class, MemberCategory.INTROSPECT_DECLARED_METHODS); - assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "publicMethod").introspect()); - } - - @Test - void methodIntrospectionDoesNotMatchInvokeDeclaredMethods() { - runtimeHints.reflection().registerType(SampleClass.class, MemberCategory.INVOKE_DECLARED_METHODS); - assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "publicMethod").introspect()); - } - @Test void methodInvocationDoesNotMatchMethodHint() { runtimeHints.reflection().registerType(SampleClass.class, typeHint -> @@ -379,22 +374,16 @@ class ReflectionHintsPredicatesTests { } @Test - void privateMethodIntrospectionMatchesMethodHint() { - runtimeHints.reflection().registerType(SampleClass.class, typeHint -> - typeHint.withMethod("privateMethod", Collections.emptyList(), ExecutableMode.INTROSPECT)); + void privateMethodIntrospectionMatchesTypeHint() { + runtimeHints.reflection().registerType(SampleClass.class); assertPredicateMatches(reflection.onMethod(SampleClass.class, "privateMethod").introspect()); } @Test - void privateMethodIntrospectionDoesNotMatchIntrospectPublicMethods() { - runtimeHints.reflection().registerType(SampleClass.class, MemberCategory.INTROSPECT_PUBLIC_METHODS); - assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "privateMethod").introspect()); - } - - @Test - void privateMethodIntrospectionDoesNotMatchInvokePublicMethods() { - runtimeHints.reflection().registerType(SampleClass.class, MemberCategory.INVOKE_PUBLIC_METHODS); - assertPredicateDoesNotMatch(reflection.onMethod(SampleClass.class, "privateMethod").introspect()); + void privateMethodIntrospectionMatchesMethodHint() { + runtimeHints.reflection().registerType(SampleClass.class, typeHint -> + typeHint.withMethod("privateMethod", Collections.emptyList(), ExecutableMode.INTROSPECT)); + assertPredicateMatches(reflection.onMethod(SampleClass.class, "privateMethod").introspect()); } @Test @@ -464,15 +453,15 @@ class ReflectionHintsPredicatesTests { } @Test - void fieldReflectionMatchesFieldHint() { - runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withField("publicField")); + void fieldReflectionMatchesTypeHint() { + runtimeHints.reflection().registerType(SampleClass.class); assertPredicateMatches(reflection.onField(SampleClass.class, "publicField")); } @Test - void fieldReflectionDoesNotMatchNonRegisteredFielddHint() { + void fieldReflectionMatchesFieldHint() { runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withField("publicField")); - assertPredicateDoesNotMatch(reflection.onField(SampleClass.class, "privateField")); + assertPredicateMatches(reflection.onField(SampleClass.class, "publicField")); } @Test @@ -482,21 +471,27 @@ class ReflectionHintsPredicatesTests { } @Test - void fieldReflectionDoesNotMatchDeclaredFieldsHint() { - runtimeHints.reflection().registerType(SampleClass.class, MemberCategory.DECLARED_FIELDS); - assertPredicateDoesNotMatch(reflection.onField(SampleClass.class, "publicField")); + void fieldInvocationMatchesPublicFieldsHint() { + runtimeHints.reflection().registerType(SampleClass.class, MemberCategory.INVOKE_PUBLIC_FIELDS); + assertPredicateMatches(reflection.onField(SampleClass.class, "publicField").invocation()); } @Test - void privateFieldReflectionMatchesFieldHint() { - runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withField("privateField")); + void fieldInvocationDoesNotMatchTypeHint() { + runtimeHints.reflection().registerType(SampleClass.class); + assertPredicateDoesNotMatch(reflection.onField(SampleClass.class, "publicField").invocation()); + } + + @Test + void privateFieldReflectionMatchesTypeHint() { + runtimeHints.reflection().registerType(SampleClass.class); assertPredicateMatches(reflection.onField(SampleClass.class, "privateField")); } @Test - void privateFieldReflectionDoesNotMatchPublicFieldsHint() { - runtimeHints.reflection().registerType(SampleClass.class, MemberCategory.PUBLIC_FIELDS); - assertPredicateDoesNotMatch(reflection.onField(SampleClass.class, "privateField")); + void privateFieldReflectionMatchesFieldHint() { + runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withField("privateField")); + assertPredicateMatches(reflection.onField(SampleClass.class, "privateField")); } @Test @@ -505,6 +500,18 @@ class ReflectionHintsPredicatesTests { assertPredicateMatches(reflection.onField(SampleClass.class, "privateField")); } + @Test + void privateFieldInvocationMatchesDeclaredFieldsHint() { + runtimeHints.reflection().registerType(SampleClass.class, MemberCategory.INVOKE_DECLARED_FIELDS); + assertPredicateMatches(reflection.onField(SampleClass.class, "privateField").invocation()); + } + + @Test + void privateFieldInvocationDoesNotMatchTypeHint() { + runtimeHints.reflection().registerType(SampleClass.class); + assertPredicateDoesNotMatch(reflection.onField(SampleClass.class, "privateField").invocation()); + } + } private void assertPredicateMatches(Predicate predicate) { diff --git a/spring-core/src/test/java/org/springframework/aot/hint/support/FilePatternResourceHintsRegistrarTests.java b/spring-core/src/test/java/org/springframework/aot/hint/support/FilePatternResourceHintsRegistrarTests.java index c34af386712..a8c93b1ec9d 100644 --- a/spring-core/src/test/java/org/springframework/aot/hint/support/FilePatternResourceHintsRegistrarTests.java +++ b/spring-core/src/test/java/org/springframework/aot/hint/support/FilePatternResourceHintsRegistrarTests.java @@ -140,11 +140,8 @@ class FilePatternResourceHintsRegistrarTests { } private Consumer includes(String... patterns) { - return hint -> { - assertThat(hint.getIncludes().stream().map(ResourcePatternHint::getPattern)) - .containsExactlyInAnyOrder(patterns); - assertThat(hint.getExcludes()).isEmpty(); - }; + return hint -> assertThat(hint.getIncludes().stream().map(ResourcePatternHint::getPattern)) + .containsExactlyInAnyOrder(patterns); } }