Introduce predicate for searching enclosing classes in MergedAnnotations

Due to the deprecation of the TYPE_HIERARCHY_AND_ENCLOSING_CLASSES
search strategy (see gh-28079), this commit introduces a way for users
to provide a Predicate<Class<?>> that is used to decide when the
enclosing class for the class supplied to the predicate should be
searched.

This gives the user complete control over the "enclosing classes"
aspect of the search algorithm in MergedAnnotations.

- To achieve the same behavior as TYPE_HIERARCHY_AND_ENCLOSING_CLASSES,
  a user can provide `clazz -> true` as the predicate.

- To limit the enclosing class search to inner classes, a user can
  provide `ClassUtils::isInnerClass` as the predicate.

- To limit the enclosing class search to static nested classes, a user
  can provide `ClassUtils::isStaticClass` as the predicate.

- For more advanced use cases, the user can provide a custom predicate.

For example, the following performs a search on MyInnerClass within the
entire type hierarchy and enclosing class hierarchy of that class.

MergedAnnotations mergedAnnotations =
   MergedAnnotations.search(TYPE_HIERARCHY)
      .withEnclosingClasses(ClassUtils::isInnerClass)
      .from(MyInnerClass.class);

In addition, TestContextAnnotationUtils in spring-test has been
revised to use this new feature where feasible.

Closes gh-28207
This commit is contained in:
Sam Brannen
2022-03-24 15:22:21 +01:00
parent 7161940b53
commit 1fe394f11d
6 changed files with 217 additions and 51 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* 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.
@@ -91,7 +91,10 @@ public abstract class TestContextAnnotationUtils {
* @see #findMergedAnnotation(Class, Class)
*/
public static boolean hasAnnotation(Class<?> clazz, Class<? extends Annotation> annotationType) {
return (findMergedAnnotation(clazz, annotationType) != null);
return MergedAnnotations.search(SearchStrategy.TYPE_HIERARCHY)
.withEnclosingClasses(TestContextAnnotationUtils::searchEnclosingClass)
.from(clazz)
.isPresent(annotationType);
}
/**
@@ -125,9 +128,11 @@ public abstract class TestContextAnnotationUtils {
private static <T extends Annotation> T findMergedAnnotation(Class<?> clazz, Class<T> annotationType,
Predicate<Class<?>> searchEnclosingClass) {
AnnotationDescriptor<T> descriptor =
findAnnotationDescriptor(clazz, annotationType, searchEnclosingClass, new HashSet<>());
return (descriptor != null ? descriptor.getAnnotation() : null);
return MergedAnnotations.search(SearchStrategy.TYPE_HIERARCHY)
.withEnclosingClasses(searchEnclosingClass)
.from(clazz)
.get(annotationType)
.synthesize(MergedAnnotation::isPresent).orElse(null);
}
/**