From a2f152ce8bdbd99f97e4d4b5aced4b5b2b778d86 Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Tue, 26 May 2015 16:45:40 +0200 Subject: [PATCH] Support nested annotations in AnnotationAttributes This commit introduces support in AnnotationAttributes for retrieving nested annotations that is on par with the existing type-safe support for retrieving nested AnnotationAttributes. Issue: SPR-13074 --- .../core/annotation/AnnotationAttributes.java | 39 +++++++++++++++ .../annotation/AnnotationAttributesTests.java | 49 ++++++++++++++++--- 2 files changed, 82 insertions(+), 6 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java index 426648be87..c23bc12666 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationAttributes.java @@ -205,6 +205,8 @@ public class AnnotationAttributes extends LinkedHashMap { /** * Get the {@link AnnotationAttributes} stored under the specified * {@code attributeName}. + *

Note: if you expect an actual annotation, invoke + * {@link #getAnnotation(String, Class)} instead. * @param attributeName the name of the attribute to get; never * {@code null} or empty * @return the {@code AnnotationAttributes} @@ -215,12 +217,29 @@ public class AnnotationAttributes extends LinkedHashMap { return doGet(attributeName, AnnotationAttributes.class); } + /** + * Get the annotation of type {@code annotationType} stored under the + * specified {@code attributeName}. + * @param attributeName the name of the attribute to get; never + * {@code null} or empty + * @param annotationType the expected annotation type; never {@code null} + * @return the annotation + * @throws IllegalArgumentException if the attribute does not exist or + * if it is not of the expected type + * @since 4.2 + */ + public A getAnnotation(String attributeName, Class annotationType) { + return doGet(attributeName, annotationType); + } + /** * Get the array of {@link AnnotationAttributes} stored under the specified * {@code attributeName}. *

If the value stored under the specified {@code attributeName} is * an instance of {@code AnnotationAttributes}, it will be wrapped in * a single-element array before returning it. + *

Note: if you expect an actual array of annotations, invoke + * {@link #getAnnotationArray(String, Class)} instead. * @param attributeName the name of the attribute to get; never * {@code null} or empty * @return the array of {@code AnnotationAttributes} @@ -231,6 +250,26 @@ public class AnnotationAttributes extends LinkedHashMap { return doGet(attributeName, AnnotationAttributes[].class); } + /** + * Get the array of type {@code annotationType} stored under the specified + * {@code attributeName}. + *

If the value stored under the specified {@code attributeName} is + * an {@code Annotation}, it will be wrapped in a single-element array + * before returning it. + * @param attributeName the name of the attribute to get; never + * {@code null} or empty + * @param annotationType the expected annotation type; never {@code null} + * @return the annotation array + * @throws IllegalArgumentException if the attribute does not exist or + * if it is not of the expected type + * @since 4.2 + */ + @SuppressWarnings("unchecked") + public A[] getAnnotationArray(String attributeName, Class annotationType) { + Object array = Array.newInstance(annotationType, 0); + return (A[]) doGet(attributeName, array.getClass()); + } + /** * Get the value stored under the specified {@code attributeName}, * ensuring that the value is of the {@code expectedType}. diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAttributesTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAttributesTests.java index a547599748..b6ce14b296 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAttributesTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationAttributesTests.java @@ -16,6 +16,9 @@ package org.springframework.core.annotation; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -32,10 +35,6 @@ import static org.junit.Assert.*; */ public class AnnotationAttributesTests { - enum Color { - RED, WHITE, BLUE - } - private final AnnotationAttributes attributes = new AnnotationAttributes(); @Rule @@ -72,7 +71,9 @@ public class AnnotationAttributesTests { } @Test - public void singleElementToSingleElementArrayConversionSupport() { + public void singleElementToSingleElementArrayConversionSupport() throws Exception { + Filter filter = FilteredClass.class.getAnnotation(Filter.class); + AnnotationAttributes nestedAttributes = new AnnotationAttributes(); nestedAttributes.put("name", "Dilbert"); @@ -80,15 +81,38 @@ public class AnnotationAttributesTests { attributes.put("names", "Dogbert"); attributes.put("classes", Number.class); attributes.put("nestedAttributes", nestedAttributes); + attributes.put("filters", filter); // Get back arrays of single elements assertThat(attributes.getStringArray("names"), equalTo(new String[] { "Dogbert" })); assertThat(attributes.getClassArray("classes"), equalTo(new Class[] { Number.class })); + AnnotationAttributes[] array = attributes.getAnnotationArray("nestedAttributes"); assertNotNull(array); - assertTrue(array.getClass().isArray()); assertThat(array.length, is(1)); assertThat(array[0].getString("name"), equalTo("Dilbert")); + + Filter[] filters = attributes.getAnnotationArray("filters", Filter.class); + assertNotNull(filters); + assertThat(filters.length, is(1)); + assertThat(filters[0].pattern(), equalTo("foo")); + } + + @Test + public void nestedAnnotations() throws Exception { + Filter filter = FilteredClass.class.getAnnotation(Filter.class); + + attributes.put("filter", filter); + attributes.put("filters", new Filter[] { filter, filter }); + + Filter retrievedFilter = attributes.getAnnotation("filter", Filter.class); + assertThat(retrievedFilter, equalTo(filter)); + assertThat(retrievedFilter.pattern(), equalTo("foo")); + + Filter[] retrievedFilters = attributes.getAnnotationArray("filters", Filter.class); + assertNotNull(retrievedFilters); + assertEquals(2, retrievedFilters.length); + assertThat(retrievedFilters[1].pattern(), equalTo("foo")); } @Test @@ -120,4 +144,17 @@ public class AnnotationAttributesTests { attributes.getEnum("color"); } + + enum Color { + RED, WHITE, BLUE + } + + @Retention(RetentionPolicy.RUNTIME) + @interface Filter { + String pattern(); + } + + @Filter(pattern = "foo") + static class FilteredClass { + } }