Browse Source

Add TYPE_HIERARCHY_AND_ENCLOSING_CLASSES strategy

Add a `TYPE_HIERARCHY_AND_ENCLOSING_CLASSES` annotation search strategy
that can be used to search the full type hierarchy as well as any
enclosing classes.

Closes gh-23378
pull/23393/head
Phillip Webb 5 years ago
parent
commit
17518ecbbe
  1. 35
      spring-core/src/main/java/org/springframework/core/annotation/AnnotationsScanner.java
  2. 14
      spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotations.java
  3. 66
      spring-core/src/test/java/org/springframework/core/annotation/AnnotationEnclosingClassSample.java
  4. 23
      spring-core/src/test/java/org/springframework/core/annotation/AnnotationsScannerTests.java

35
spring-core/src/main/java/org/springframework/core/annotation/AnnotationsScanner.java

@ -123,10 +123,12 @@ abstract class AnnotationsScanner { @@ -123,10 +123,12 @@ abstract class AnnotationsScanner {
case INHERITED_ANNOTATIONS:
return processClassInheritedAnnotations(context, source, processor, classFilter);
case SUPERCLASS:
return processClassHierarchy(context, new int[] {0}, source, processor, classFilter, false);
return processClassHierarchy(context, source, processor, classFilter, false, false);
case EXHAUSTIVE:
case TYPE_HIERARCHY:
return processClassHierarchy(context, new int[] {0}, source, processor, classFilter, true);
return processClassHierarchy(context, source, processor, classFilter, true, false);
case TYPE_HIERARCHY_AND_ENCLOSING_CLASSES:
return processClassHierarchy(context, source, processor, classFilter, true, true);
}
throw new IllegalStateException("Unsupported search strategy " + searchStrategy);
}
@ -184,10 +186,22 @@ abstract class AnnotationsScanner { @@ -184,10 +186,22 @@ abstract class AnnotationsScanner {
return null;
}
@Nullable
private static <C, R> R processClassHierarchy(C context, Class<?> source,
AnnotationsProcessor<C, R> processor,
@Nullable BiPredicate<C, Class<?>> classFilter, boolean includeInterfaces,
boolean includeEnclosing) {
int[] aggregateIndex = new int[] { 0 };
return processClassHierarchy(context, aggregateIndex, source, processor,
classFilter, includeInterfaces, includeEnclosing);
}
@Nullable
private static <C, R> R processClassHierarchy(C context, int[] aggregateIndex,
Class<?> source, AnnotationsProcessor<C, R> processor,
@Nullable BiPredicate<C, Class<?>> classFilter, boolean includeInterfaces) {
@Nullable BiPredicate<C, Class<?>> classFilter, boolean includeInterfaces,
boolean includeEnclosing) {
R result = processor.doWithAggregate(context, aggregateIndex[0]);
if (result != null) {
@ -205,7 +219,7 @@ abstract class AnnotationsScanner { @@ -205,7 +219,7 @@ abstract class AnnotationsScanner {
if (includeInterfaces) {
for (Class<?> interfaceType : source.getInterfaces()) {
R interfacesResult = processClassHierarchy(context, aggregateIndex,
interfaceType, processor, classFilter, true);
interfaceType, processor, classFilter, true, includeEnclosing);
if (interfacesResult != null) {
return interfacesResult;
}
@ -214,11 +228,21 @@ abstract class AnnotationsScanner { @@ -214,11 +228,21 @@ abstract class AnnotationsScanner {
Class<?> superclass = source.getSuperclass();
if (superclass != Object.class && superclass != null) {
R superclassResult = processClassHierarchy(context, aggregateIndex,
superclass, processor, classFilter, includeInterfaces);
superclass, processor, classFilter, includeInterfaces,
includeEnclosing);
if (superclassResult != null) {
return superclassResult;
}
}
Class<?> enclosingClass = source.getEnclosingClass();
if (includeEnclosing && enclosingClass != null) {
R enclosingResult = processClassHierarchy(context, aggregateIndex,
enclosingClass, processor, classFilter, includeInterfaces,
includeEnclosing);
if (enclosingResult != null) {
return enclosingResult;
}
}
return null;
}
@ -237,6 +261,7 @@ abstract class AnnotationsScanner { @@ -237,6 +261,7 @@ abstract class AnnotationsScanner {
processor, classFilter, source, false);
case EXHAUSTIVE:
case TYPE_HIERARCHY:
case TYPE_HIERARCHY_AND_ENCLOSING_CLASSES:
return processMethodHierarchy(context, new int[] {0}, source.getDeclaringClass(),
processor, classFilter, source, true);
}

14
spring-core/src/main/java/org/springframework/core/annotation/MergedAnnotations.java

@ -434,12 +434,22 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation> @@ -434,12 +434,22 @@ public interface MergedAnnotations extends Iterable<MergedAnnotation<Annotation>
EXHAUSTIVE,
/**
* Perform a full search of the entire type hierarchy , including
* Perform a full search of the entire type hierarchy, including
* superclasses and implemented interfaces. Superclass annotations do
* not need to be meta-annotated with {@link Inherited @Inherited}.
*/
TYPE_HIERARCHY
TYPE_HIERARCHY,
/**
* Perform a full search of the entire type hierarchy on the source
* <em>and</em> any enclosing classes. This strategy is similar to
* {@link #TYPE_HIERARCHY} except that {@link Class#getEnclosingClass()
* enclosing classes} are also searched. Superclass annotations do not
* need to be meta-annotated with {@link Inherited @Inherited}. When
* searching a {@link Method} source, this strategy is identical to
* {@link #TYPE_HIERARCHY}.
*/
TYPE_HIERARCHY_AND_ENCLOSING_CLASSES
}
}

66
spring-core/src/test/java/org/springframework/core/annotation/AnnotationEnclosingClassSample.java

@ -0,0 +1,66 @@ @@ -0,0 +1,66 @@
/*
* Copyright 2002-2019 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Example class used to test {@link AnnotationsScanner} with enclosing classes.
*
* @author Phillip Webb
* @since 5.2
*/
@AnnotationEnclosingClassSample.EnclosedOne
public class AnnotationEnclosingClassSample {
@EnclosedTwo
public static class EnclosedStatic {
@EnclosedThree
public static class EnclosedStaticStatic {
}
}
@EnclosedTwo
public class EnclosedInner {
@EnclosedThree
public class EnclosedInnerInner {
}
}
@Retention(RetentionPolicy.RUNTIME)
public static @interface EnclosedOne {
}
@Retention(RetentionPolicy.RUNTIME)
public static @interface EnclosedTwo {
}
@Retention(RetentionPolicy.RUNTIME)
public static @interface EnclosedThree {
}
}

23
spring-core/src/test/java/org/springframework/core/annotation/AnnotationsScannerTests.java

@ -454,6 +454,29 @@ public class AnnotationsScannerTests { @@ -454,6 +454,29 @@ public class AnnotationsScannerTests {
"0:TestAnnotation1");
}
@Test
public void typeHierarchyWithEnclosedStrategyOnEnclosedStaticClassScansAnnotations() {
Class<?> source = AnnotationEnclosingClassSample.EnclosedStatic.EnclosedStaticStatic.class;
assertThat(scan(source, SearchStrategy.TYPE_HIERARCHY_AND_ENCLOSING_CLASSES))
.containsExactly("0:EnclosedThree", "1:EnclosedTwo", "2:EnclosedOne");
}
@Test
public void typeHierarchyWithEnclosedStrategyOnEnclosedInnerClassScansAnnotations() {
Class<?> source = AnnotationEnclosingClassSample.EnclosedInner.EnclosedInnerInner.class;
assertThat(scan(source, SearchStrategy.TYPE_HIERARCHY_AND_ENCLOSING_CLASSES))
.containsExactly("0:EnclosedThree", "1:EnclosedTwo", "2:EnclosedOne");
}
@Test
public void typeHierarchyWithEnclosedStrategyOnMethodHierarchyUsesTypeHierarchyScan() {
Method source = methodFrom(WithHierarchy.class);
assertThat(scan(source, SearchStrategy.TYPE_HIERARCHY_AND_ENCLOSING_CLASSES)).containsExactly(
"0:TestAnnotation1", "1:TestAnnotation5", "1:TestInheritedAnnotation5",
"2:TestAnnotation6", "3:TestAnnotation2", "3:TestInheritedAnnotation2",
"4:TestAnnotation3", "5:TestAnnotation4");
}
@Test
public void scanWhenProcessorReturnsFromDoWithAggregateExitsEarly() {
String result = AnnotationsScanner.scan(this, WithSingleSuperclass.class,

Loading…
Cancel
Save