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 45110dccae6..6694d1347d2 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 @@ -114,6 +114,23 @@ public class ReflectionHintsPredicates { return new MethodHintPredicate(getMethod(type, methodName)); } + /** + * Return a predicate that checks whether a reflection hint is registered for the method that matches the given selector. + * This looks up a method on the given type with the expected name, if unique. + * By default, both introspection and invocation hints match. + *

The returned type exposes additional methods that refine the predicate behavior. + * @param className the name of the class holding the method + * @param methodName the method name + * @return the {@link RuntimeHints} predicate + * @throws ClassNotFoundException if the class cannot be resolved. + * @throws IllegalArgumentException if the method cannot be found or if multiple methods are found with the same name. + */ + public MethodHintPredicate onMethod(String className, String methodName) throws ClassNotFoundException { + Assert.notNull(className, "'className' should not be null"); + Assert.hasText(methodName, "'methodName' should not be null"); + return onMethod(Class.forName(className), methodName); + } + private Method getMethod(Class type, String methodName) { ReflectionUtils.MethodFilter selector = method -> methodName.equals(method.getName()); Set methods = MethodIntrospector.selectMethods(type, selector); @@ -148,6 +165,23 @@ public class ReflectionHintsPredicates { return new FieldHintPredicate(field); } + /** + * Return a predicate that checks whether a reflection hint is registered for the field that matches the given selector. + * This looks up a field on the given type with the expected name, if present. + * By default, unsafe or write access are not considered. + *

The returned type exposes additional methods that refine the predicate behavior. + * @param className the name of the class holding the field + * @param fieldName the field name + * @return the {@link RuntimeHints} predicate + * @throws ClassNotFoundException if the class cannot be resolved. + * @throws IllegalArgumentException if a field cannot be found with the given name. + */ + public FieldHintPredicate onField(String className, String fieldName) throws ClassNotFoundException { + Assert.notNull(className, "'className' should not be null"); + Assert.hasText(fieldName, "'fieldName' should not be empty"); + return onField(Class.forName(className), fieldName); + } + /** * Return a predicate that checks whether a reflection hint is registered for the given field. * By default, unsafe or write access are not considered. 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 2b8ebf338de..e4ae6127de7 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 @@ -33,6 +33,7 @@ import org.springframework.aot.hint.TypeReference; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * Tests for {@link ReflectionHintsPredicates} @@ -319,6 +320,12 @@ class ReflectionHintsPredicatesTests { assertPredicateMatches(reflection.onMethod(SampleClass.class, "publicMethod").introspect()); } + @Test + void methodIntrospectionFailsForUnknownType() { + assertThatThrownBy(() -> reflection.onMethod("com.example.DoesNotExist", "publicMethod").introspect()) + .isInstanceOf(ClassNotFoundException.class); + } + @Test void methodIntrospectionMatchesIntrospectPublicMethods() { runtimeHints.reflection().registerType(SampleClass.class, MemberCategory.INTROSPECT_PUBLIC_METHODS); @@ -472,6 +479,12 @@ class ReflectionHintsPredicatesTests { assertThatIllegalArgumentException().isThrownBy(() -> reflection.onField(SampleClass.class, "missingField")); } + @Test + void shouldFailForUnknownClass() { + assertThatThrownBy(() -> reflection.onField("com.example.DoesNotExist", "missingField")) + .isInstanceOf(ClassNotFoundException.class); + } + @Test void fieldReflectionMatchesFieldHint() { runtimeHints.reflection().registerType(SampleClass.class, typeHint -> typeHint.withField("publicField"));