diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java index c52937b7a1e..2447039dbee 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java @@ -451,6 +451,38 @@ public class AnnotatedElementUtils { return AnnotationUtils.synthesizeAnnotation(attributes, annotationType, element); } + /** + * Get all annotations of the specified {@code annotationType} + * within the annotation hierarchy above the supplied {@code element}; + * and for each annotation found, merge that annotation's attributes with + * matching attributes from annotations in lower levels of the annotation + * hierarchy and synthesize the results back into an annotation of the specified + * {@code annotationType}. + *
{@link AliasFor @AliasFor} semantics are fully supported, both within a + * single annotation and within annotation hierarchies. + *
This method follows get semantics as described in the
+ * {@linkplain AnnotatedElementUtils class-level javadoc}.
+ * @param element the annotated element; never {@code null}
+ * @param annotationType the annotation type to find; never {@code null}
+ * @return the set of all merged, synthesized {@code Annotations} found, or an empty
+ * set if none were found
+ * @since 4.3
+ * @see #getMergedAnnotation(AnnotatedElement, Class)
+ * @see #getAllAnnotationAttributes(AnnotatedElement, String)
+ * @see #findAllMergedAnnotations(AnnotatedElement, Class)
+ */
+ public static Set getAllMergedAnnotations(AnnotatedElement element,
+ Class annotationType) {
+
+ Assert.notNull(element, "AnnotatedElement must not be null");
+ Assert.notNull(annotationType, "annotationType must not be null");
+
+ MergedAnnotationAttributesProcessor processor =
+ new MergedAnnotationAttributesProcessor(annotationType, null, false, false, true);
+ searchWithGetSemantics(element, annotationType, null, processor);
+ return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults());
+ }
+
/**
* Get the annotation attributes of all annotations of the specified
* {@code annotationName} in the annotation hierarchy above the supplied
@@ -688,7 +720,7 @@ public class AnnotatedElementUtils {
* within the annotation hierarchy above the supplied {@code element};
* and for each annotation found, merge that annotation's attributes with
* matching attributes from annotations in lower levels of the annotation
- * hierarchy and synthesize the result back into an annotation of the specified
+ * hierarchy and synthesize the results back into an annotation of the specified
* {@code annotationType}.
* {@link AliasFor @AliasFor} semantics are fully supported, both within a
* single annotation and within annotation hierarchies.
@@ -700,6 +732,7 @@ public class AnnotatedElementUtils {
* set if none were found
* @since 4.3
* @see #findMergedAnnotation(AnnotatedElement, Class)
+ * @see #getAllMergedAnnotations(AnnotatedElement, Class)
*/
public static Set findAllMergedAnnotations(AnnotatedElement element,
Class annotationType) {
@@ -710,13 +743,7 @@ public class AnnotatedElementUtils {
MergedAnnotationAttributesProcessor processor =
new MergedAnnotationAttributesProcessor(annotationType, null, false, false, true);
searchWithFindSemantics(element, annotationType, annotationType.getName(), processor);
-
- Set annotations = new LinkedHashSet();
- for (AnnotationAttributes attributes : processor.getAggregatedResults()) {
- AnnotationUtils.postProcessAnnotationAttributes(element, attributes, false, false);
- annotations.add(AnnotationUtils.synthesizeAnnotation(attributes, annotationType, element));
- }
- return annotations;
+ return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults());
}
/**
@@ -724,7 +751,7 @@ public class AnnotatedElementUtils {
* within the annotation hierarchy above the supplied {@code element};
* and for each annotation found, merge that annotation's attributes with
* matching attributes from annotations in lower levels of the annotation
- * hierarchy and synthesize the result back into an annotation of the specified
+ * hierarchy and synthesize the results back into an annotation of the specified
* {@code annotationType}.
* The container type that holds the repeatable annotations will be looked up
* via {@link java.lang.annotation.Repeatable}.
@@ -754,7 +781,7 @@ public class AnnotatedElementUtils {
* within the annotation hierarchy above the supplied {@code element};
* and for each annotation found, merge that annotation's attributes with
* matching attributes from annotations in lower levels of the annotation
- * hierarchy and synthesize the result back into an annotation of the specified
+ * hierarchy and synthesize the results back into an annotation of the specified
* {@code annotationType}.
* {@link AliasFor @AliasFor} semantics are fully supported, both within a
* single annotation and within annotation hierarchies.
@@ -791,13 +818,7 @@ public class AnnotatedElementUtils {
MergedAnnotationAttributesProcessor processor =
new MergedAnnotationAttributesProcessor(annotationType, null, false, false, true);
searchWithFindSemantics(element, annotationType, annotationType.getName(), containerType, processor);
-
- Set annotations = new LinkedHashSet();
- for (AnnotationAttributes attributes : processor.getAggregatedResults()) {
- AnnotationUtils.postProcessAnnotationAttributes(element, attributes, false, false);
- annotations.add(AnnotationUtils.synthesizeAnnotation(attributes, annotationType, element));
- }
- return annotations;
+ return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults());
}
/**
@@ -906,13 +927,18 @@ public class AnnotatedElementUtils {
// Search in annotations
for (Annotation annotation : annotations) {
+ // Note: we only check for (metaDepth > 0) due to the nuances of getMetaAnnotationTypes().
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation) &&
- ((annotationType != null ? annotation.annotationType() == annotationType :
- annotation.annotationType().getName().equals(annotationName)) ||
- metaDepth > 0)) {
+ ((annotation.annotationType() == annotationType
+ || annotation.annotationType().getName().equals(annotationName)) || metaDepth > 0)) {
T result = processor.process(annotatedElement, annotation, metaDepth);
if (result != null) {
- return result;
+ if (processor.aggregates() && metaDepth == 0) {
+ processor.getAggregatedResults().add(result);
+ }
+ else {
+ return result;
+ }
}
}
}
@@ -924,7 +950,12 @@ public class AnnotatedElementUtils {
annotationName, processor, visited, metaDepth + 1);
if (result != null) {
processor.postProcess(annotatedElement, annotation, result);
- return result;
+ if (processor.aggregates() && metaDepth == 0) {
+ processor.getAggregatedResults().add(result);
+ }
+ else {
+ return result;
+ }
}
}
}
@@ -1234,6 +1265,20 @@ public class AnnotatedElementUtils {
}
}
+ /**
+ * @since 4.3
+ */
+ private static Set postProcessAndSynthesizeAggregatedResults(AnnotatedElement element,
+ Class annotationType, List If this method returns {@code true}, then {@link #getAggregatedResults()}
* must return a non-null value.
- * WARNING: aggregation is currently only supported for find semantics.
* @return {@code true} if this processor supports aggregated results
* @see #getAggregatedResults
* @since 4.3
@@ -1319,7 +1363,6 @@ public class AnnotatedElementUtils {
* responsible for asking this processor if it {@link #aggregates} results
* and then adding the post-processed results to the list returned by this
* method.
- * WARNING: aggregation is currently only supported for find semantics.
* @return the list of results aggregated by this processor; never
* {@code null} unless {@link #aggregates} returns {@code false}
* @see #aggregates
diff --git a/spring-core/src/test/java/org/springframework/core/annotation/MultipleComposedAnnotationsOnSingleAnnotatedElementTests.java b/spring-core/src/test/java/org/springframework/core/annotation/MultipleComposedAnnotationsOnSingleAnnotatedElementTests.java
index 3083793a21a..395b9c31b53 100644
--- a/spring-core/src/test/java/org/springframework/core/annotation/MultipleComposedAnnotationsOnSingleAnnotatedElementTests.java
+++ b/spring-core/src/test/java/org/springframework/core/annotation/MultipleComposedAnnotationsOnSingleAnnotatedElementTests.java
@@ -26,6 +26,7 @@ import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Set;
+import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.*;
@@ -45,43 +46,145 @@ import static org.springframework.core.annotation.AnnotatedElementUtils.*;
public class MultipleComposedAnnotationsOnSingleAnnotatedElementTests {
@Test
- public void multipleComposedAnnotationsOnClass() {
- assertMultipleComposedAnnotations(MultipleComposedCachesClass.class);
+ public void getMultipleComposedAnnotationsOnClass() {
+ assertGetAllMergedAnnotationsBehavior(MultipleComposedCachesClass.class);
}
@Test
- public void composedPlusLocalAnnotationsOnClass() {
- assertMultipleComposedAnnotations(ComposedPlusLocalCachesClass.class);
+ public void getMultipleInheritedComposedAnnotationsOnSuperclass() {
+ assertGetAllMergedAnnotationsBehavior(SubMultipleComposedCachesClass.class);
}
@Test
- public void multipleComposedAnnotationsOnInterface() {
- assertMultipleComposedAnnotations(MultipleComposedCachesOnInterfaceClass.class);
+ public void getMultipleNoninheritedComposedAnnotationsOnClass() {
+ Class> element = MultipleNoninheritedComposedCachesClass.class;
+ Set