summaryrefslogtreecommitdiff
path: root/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java
diff options
context:
space:
mode:
authorEmmanuel Bourg <ebourg@apache.org>2015-07-15 23:21:27 +0200
committerEmmanuel Bourg <ebourg@apache.org>2015-07-15 23:21:27 +0200
commitda46d30e80e4c59a41cf52055d06faa1dbb7e383 (patch)
tree52b707fbbccd5b6100088913f32c1cbd00568790 /spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java
parentc03c348db4e91c613982cbe6c99d0cf04ea14fe3 (diff)
Imported Upstream version 4.0.9
Diffstat (limited to 'spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java')
-rw-r--r--spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java466
1 files changed, 357 insertions, 109 deletions
diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java
index 3b4def63..45d7c48c 100644
--- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java
+++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java
@@ -19,31 +19,44 @@ package org.springframework.core.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.WeakHashMap;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
import org.springframework.core.BridgeMethodResolver;
import org.springframework.util.Assert;
+import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
/**
- * General utility methods for working with annotations, handling bridge methods (which the compiler
- * generates for generic declarations) as well as super methods (for optional &quot;annotation inheritance&quot;).
- * Note that none of this is provided by the JDK's introspection facilities themselves.
+ * General utility methods for working with annotations, handling bridge methods
+ * (which the compiler generates for generic declarations) as well as super methods
+ * (for optional &quot;annotation inheritance&quot;). Note that none of this is
+ * provided by the JDK's introspection facilities themselves.
*
- * <p>As a general rule for runtime-retained annotations (e.g. for transaction control, authorization or service
- * exposure), always use the lookup methods on this class (e.g., {@link #findAnnotation(Method, Class)}, {@link
- * #getAnnotation(Method, Class)}, and {@link #getAnnotations(Method)}) instead of the plain annotation lookup
- * methods in the JDK. You can still explicitly choose between lookup on the given class level only ({@link
- * #getAnnotation(Method, Class)}) and lookup in the entire inheritance hierarchy of the given method ({@link
- * #findAnnotation(Method, Class)}).
+ * <p>As a general rule for runtime-retained annotations (e.g. for transaction
+ * control, authorization, or service exposure), always use the lookup methods
+ * on this class (e.g., {@link #findAnnotation(Method, Class)},
+ * {@link #getAnnotation(Method, Class)}, and {@link #getAnnotations(Method)})
+ * instead of the plain annotation lookup methods in the JDK. You can still
+ * explicitly choose between a <em>get</em> lookup on the given class level only
+ * ({@link #getAnnotation(Method, Class)}) and a <em>find</em> lookup in the entire
+ * inheritance hierarchy of the given method ({@link #findAnnotation(Method, Class)}).
*
* @author Rob Harrop
* @author Juergen Hoeller
* @author Sam Brannen
* @author Mark Fisher
* @author Chris Beams
+ * @author Phillip Webb
* @since 2.0
* @see java.lang.reflect.Method#getAnnotations()
* @see java.lang.reflect.Method#getAnnotation(Class)
@@ -51,42 +64,100 @@ import org.springframework.util.ReflectionUtils;
public abstract class AnnotationUtils {
/** The attribute name for annotations with a single element */
- static final String VALUE = "value";
+ public static final String VALUE = "value";
+
private static final Map<Class<?>, Boolean> annotatedInterfaceCache = new WeakHashMap<Class<?>, Boolean>();
+ private static transient Log logger;
+
+
+ /**
+ * Get a single {@link Annotation} of {@code annotationType} from the supplied
+ * annotation: either the given annotation itself or a meta-annotation thereof.
+ * @param ann the Annotation to check
+ * @param annotationType the annotation type to look for, both locally and as a meta-annotation
+ * @return the matching annotation, or {@code null} if none found
+ * @since 4.0
+ */
+ @SuppressWarnings("unchecked")
+ public static <T extends Annotation> T getAnnotation(Annotation ann, Class<T> annotationType) {
+ if (annotationType.isInstance(ann)) {
+ return (T) ann;
+ }
+ try {
+ return ann.annotationType().getAnnotation(annotationType);
+ }
+ catch (Exception ex) {
+ // Assuming nested Class values not resolvable within annotation attributes...
+ logIntrospectionFailure(ann.annotationType(), ex);
+ return null;
+ }
+ }
/**
* Get a single {@link Annotation} of {@code annotationType} from the supplied
* Method, Constructor or Field. Meta-annotations will be searched if the annotation
* is not declared locally on the supplied element.
- * @param ae the Method, Constructor or Field from which to get the annotation
+ * @param annotatedElement the Method, Constructor or Field from which to get the annotation
* @param annotationType the annotation type to look for, both locally and as a meta-annotation
* @return the matching annotation, or {@code null} if none found
* @since 3.1
*/
- public static <T extends Annotation> T getAnnotation(AnnotatedElement ae, Class<T> annotationType) {
- T ann = ae.getAnnotation(annotationType);
- if (ann == null) {
- for (Annotation metaAnn : ae.getAnnotations()) {
- ann = metaAnn.annotationType().getAnnotation(annotationType);
- if (ann != null) {
- break;
+ public static <T extends Annotation> T getAnnotation(AnnotatedElement annotatedElement, Class<T> annotationType) {
+ try {
+ T ann = annotatedElement.getAnnotation(annotationType);
+ if (ann == null) {
+ for (Annotation metaAnn : annotatedElement.getAnnotations()) {
+ ann = metaAnn.annotationType().getAnnotation(annotationType);
+ if (ann != null) {
+ break;
+ }
}
}
+ return ann;
+ }
+ catch (Exception ex) {
+ // Assuming nested Class values not resolvable within annotation attributes...
+ logIntrospectionFailure(annotatedElement, ex);
+ return null;
+ }
+ }
+
+ /**
+ * Get all {@link Annotation Annotations} from the supplied Method, Constructor or Field.
+ * @param annotatedElement the Method, Constructor or Field to retrieve annotations from
+ * @return the annotations found, or {@code null} if not resolvable (e.g. because nested
+ * Class values in annotation attributes failed to resolve at runtime)
+ * @since 4.0.8
+ */
+ public static Annotation[] getAnnotations(AnnotatedElement annotatedElement) {
+ try {
+ return annotatedElement.getAnnotations();
+ }
+ catch (Exception ex) {
+ // Assuming nested Class values not resolvable within annotation attributes...
+ logIntrospectionFailure(annotatedElement, ex);
+ return null;
}
- return ann;
}
/**
* Get all {@link Annotation Annotations} from the supplied {@link Method}.
* <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
- * @param method the method to look for annotations on
+ * @param method the Method to retrieve annotations from
* @return the annotations found
* @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method)
*/
public static Annotation[] getAnnotations(Method method) {
- return BridgeMethodResolver.findBridgedMethod(method).getAnnotations();
+ try {
+ return BridgeMethodResolver.findBridgedMethod(method).getAnnotations();
+ }
+ catch (Exception ex) {
+ // Assuming nested Class values not resolvable within annotation attributes...
+ logIntrospectionFailure(method, ex);
+ return null;
+ }
}
/**
@@ -99,22 +170,61 @@ public abstract class AnnotationUtils {
*/
public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType) {
Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
- A ann = resolvedMethod.getAnnotation(annotationType);
- if (ann == null) {
- for (Annotation metaAnn : resolvedMethod.getAnnotations()) {
- ann = metaAnn.annotationType().getAnnotation(annotationType);
- if (ann != null) {
- break;
- }
+ return getAnnotation((AnnotatedElement) resolvedMethod, annotationType);
+ }
+
+ /**
+ * Get the possibly repeating {@link Annotation}s of {@code annotationType} from the
+ * supplied {@link Method}. Deals with both a single direct annotation and repeating
+ * annotations nested within a containing annotation.
+ * <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
+ * @param method the method to look for annotations on
+ * @param containerAnnotationType the class of the container that holds the annotations
+ * @param annotationType the annotation type to look for
+ * @return the annotations found
+ * @since 4.0
+ * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method)
+ */
+ public static <A extends Annotation> Set<A> getRepeatableAnnotation(Method method,
+ Class<? extends Annotation> containerAnnotationType, Class<A> annotationType) {
+
+ Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
+ return getRepeatableAnnotation((AnnotatedElement) resolvedMethod, containerAnnotationType, annotationType);
+ }
+
+ /**
+ * Get the possibly repeating {@link Annotation}s of {@code annotationType} from the
+ * supplied {@link AnnotatedElement}. Deals with both a single direct annotation and
+ * repeating annotations nested within a containing annotation.
+ * <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
+ * @param annotatedElement the element to look for annotations on
+ * @param containerAnnotationType the class of the container that holds the annotations
+ * @param annotationType the annotation type to look for
+ * @return the annotations found
+ * @since 4.0
+ * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method)
+ */
+ public static <A extends Annotation> Set<A> getRepeatableAnnotation(AnnotatedElement annotatedElement,
+ Class<? extends Annotation> containerAnnotationType, Class<A> annotationType) {
+
+ try {
+ if (annotatedElement.getAnnotations().length > 0) {
+ return new AnnotationCollector<A>(containerAnnotationType, annotationType).getResult(annotatedElement);
}
}
- return ann;
+ catch (Exception ex) {
+ // Assuming nested Class values not resolvable within annotation attributes...
+ logIntrospectionFailure(annotatedElement, ex);
+ }
+ return Collections.emptySet();
}
/**
- * Get a single {@link Annotation} of {@code annotationType} from the supplied {@link Method},
- * traversing its super methods if no annotation can be found on the given method itself.
- * <p>Annotations on methods are not inherited by default, so we need to handle this explicitly.
+ * Find a single {@link Annotation} of {@code annotationType} from the supplied
+ * {@link Method}, traversing its super methods (i.e., from superclasses and
+ * interfaces) if no annotation can be found on the given method itself.
+ * <p>Annotations on methods are not inherited by default, so we need to handle
+ * this explicitly.
* @param method the method to look for annotations on
* @param annotationType the annotation type to look for
* @return the annotation found, or {@code null} if none
@@ -171,9 +281,15 @@ public abstract class AnnotationUtils {
}
boolean found = false;
for (Method ifcMethod : iface.getMethods()) {
- if (ifcMethod.getAnnotations().length > 0) {
- found = true;
- break;
+ try {
+ if (ifcMethod.getAnnotations().length > 0) {
+ found = true;
+ break;
+ }
+ }
+ catch (Exception ex) {
+ // Assuming nested Class values not resolvable within annotation attributes...
+ logIntrospectionFailure(ifcMethod, ex);
}
}
annotatedInterfaceCache.put(iface, found);
@@ -182,45 +298,82 @@ public abstract class AnnotationUtils {
}
/**
- * Find a single {@link Annotation} of {@code annotationType} from the supplied {@link Class},
- * traversing its interfaces and superclasses if no annotation can be found on the given class itself.
- * <p>This method explicitly handles class-level annotations which are not declared as
- * {@link java.lang.annotation.Inherited inherited} <i>as well as annotations on interfaces</i>.
- * <p>The algorithm operates as follows: Searches for an annotation on the given class and returns
- * it if found. Else searches all interfaces that the given class declares, returning the annotation
- * from the first matching candidate, if any. Else proceeds with introspection of the superclass
- * of the given class, checking the superclass itself; if no annotation found there, proceeds
- * with the interfaces that the superclass declares. Recursing up through the entire superclass
- * hierarchy if no match is found.
+ * Find a single {@link Annotation} of {@code annotationType} on the
+ * supplied {@link Class}, traversing its interfaces, annotations, and
+ * superclasses if the annotation is not <em>present</em> on the given class
+ * itself.
+ * <p>This method explicitly handles class-level annotations which are not
+ * declared as {@link java.lang.annotation.Inherited inherited} <em>as well
+ * as meta-annotations and annotations on interfaces</em>.
+ * <p>The algorithm operates as follows:
+ * <ol>
+ * <li>Search for the annotation on the given class and return it if found.
+ * <li>Recursively search through all interfaces that the given class declares.
+ * <li>Recursively search through all annotations that the given class declares.
+ * <li>Recursively search through the superclass hierarchy of the given class.
+ * </ol>
+ * <p>Note: in this context, the term <em>recursively</em> means that the search
+ * process continues by returning to step #1 with the current interface,
+ * annotation, or superclass as the class to look for annotations on.
* @param clazz the class to look for annotations on
- * @param annotationType the annotation type to look for
- * @return the annotation found, or {@code null} if none found
+ * @param annotationType the type of annotation to look for
+ * @return the annotation if found, or {@code null} if not found
*/
public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType) {
+ return findAnnotation(clazz, annotationType, new HashSet<Annotation>());
+ }
+
+ /**
+ * Perform the search algorithm for {@link #findAnnotation(Class, Class)},
+ * avoiding endless recursion by tracking which annotations have already
+ * been <em>visited</em>.
+ * @param clazz the class to look for annotations on
+ * @param annotationType the type of annotation to look for
+ * @param visited the set of annotations that have already been visited
+ * @return the annotation if found, or {@code null} if not found
+ */
+ private static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType, Set<Annotation> visited) {
Assert.notNull(clazz, "Class must not be null");
- A annotation = clazz.getAnnotation(annotationType);
- if (annotation != null) {
- return annotation;
+
+ if (isAnnotationDeclaredLocally(annotationType, clazz)) {
+ try {
+ return clazz.getAnnotation(annotationType);
+ }
+ catch (Exception ex) {
+ // Assuming nested Class values not resolvable within annotation attributes...
+ logIntrospectionFailure(clazz, ex);
+ return null;
+ }
}
+
for (Class<?> ifc : clazz.getInterfaces()) {
- annotation = findAnnotation(ifc, annotationType);
+ A annotation = findAnnotation(ifc, annotationType, visited);
if (annotation != null) {
return annotation;
}
}
- if (!Annotation.class.isAssignableFrom(clazz)) {
- for (Annotation ann : clazz.getAnnotations()) {
- annotation = findAnnotation(ann.annotationType(), annotationType);
- if (annotation != null) {
- return annotation;
+
+ try {
+ for (Annotation ann : clazz.getDeclaredAnnotations()) {
+ if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
+ A annotation = findAnnotation(ann.annotationType(), annotationType, visited);
+ if (annotation != null) {
+ return annotation;
+ }
}
}
}
+ catch (Exception ex) {
+ // Assuming nested Class values not resolvable within annotation attributes...
+ logIntrospectionFailure(clazz, ex);
+ return null;
+ }
+
Class<?> superclass = clazz.getSuperclass();
if (superclass == null || superclass.equals(Object.class)) {
return null;
}
- return findAnnotation(superclass, annotationType);
+ return findAnnotation(superclass, annotationType, visited);
}
/**
@@ -312,12 +465,18 @@ public abstract class AnnotationUtils {
Assert.notNull(annotationType, "Annotation type must not be null");
Assert.notNull(clazz, "Class must not be null");
boolean declaredLocally = false;
- for (Annotation annotation : clazz.getDeclaredAnnotations()) {
- if (annotation.annotationType().equals(annotationType)) {
- declaredLocally = true;
- break;
+ try {
+ for (Annotation ann : clazz.getDeclaredAnnotations()) {
+ if (ann.annotationType().equals(annotationType)) {
+ declaredLocally = true;
+ break;
+ }
}
}
+ catch (Exception ex) {
+ // Assuming nested Class values not resolvable within annotation attributes...
+ logIntrospectionFailure(clazz, ex);
+ }
return declaredLocally;
}
@@ -343,11 +502,21 @@ public abstract class AnnotationUtils {
}
/**
- * Retrieve the given annotation's attributes as a Map, preserving all attribute types
- * as-is.
- * <p>Note: As of Spring 3.1.1, the returned map is actually an
- * {@link AnnotationAttributes} instance, however the Map signature of this method has
- * been preserved for binary compatibility.
+ * Determine if the supplied {@link Annotation} is defined in the core JDK
+ * {@code java.lang.annotation} package.
+ * @param annotation the annotation to check (never {@code null})
+ * @return {@code true} if the annotation is in the {@code java.lang.annotation} package
+ */
+ public static boolean isInJavaLangAnnotationPackage(Annotation annotation) {
+ Assert.notNull(annotation, "Annotation must not be null");
+ return annotation.annotationType().getName().startsWith("java.lang.annotation");
+ }
+
+ /**
+ * Retrieve the given annotation's attributes as a {@link Map}, preserving all
+ * attribute types as-is.
+ * <p>Note: This method actually returns an {@link AnnotationAttributes} instance.
+ * However, the {@code Map} signature has been preserved for binary compatibility.
* @param annotation the annotation to retrieve the attributes for
* @return the Map of annotation attributes, with attribute names as keys and
* corresponding attribute values as values
@@ -357,16 +526,15 @@ public abstract class AnnotationUtils {
}
/**
- * Retrieve the given annotation's attributes as a Map. Equivalent to calling
- * {@link #getAnnotationAttributes(Annotation, boolean, boolean)} with
+ * Retrieve the given annotation's attributes as a {@link Map}. Equivalent to
+ * calling {@link #getAnnotationAttributes(Annotation, boolean, boolean)} with
* the {@code nestedAnnotationsAsMap} parameter set to {@code false}.
- * <p>Note: As of Spring 3.1.1, the returned map is actually an
- * {@link AnnotationAttributes} instance, however the Map signature of this method has
- * been preserved for binary compatibility.
+ * <p>Note: This method actually returns an {@link AnnotationAttributes} instance.
+ * However, the {@code Map} signature has been preserved for binary compatibility.
* @param annotation the annotation to retrieve the attributes for
* @param classValuesAsString whether to turn Class references into Strings (for
- * compatibility with {@link org.springframework.core.type.AnnotationMetadata} or to
- * preserve them as Class references
+ * compatibility with {@link org.springframework.core.type.AnnotationMetadata}
+ * or to preserve them as Class references
* @return the Map of annotation attributes, with attribute names as keys and
* corresponding attribute values as values
*/
@@ -376,13 +544,13 @@ public abstract class AnnotationUtils {
/**
* Retrieve the given annotation's attributes as an {@link AnnotationAttributes}
- * map structure. Implemented in Spring 3.1.1 to provide fully recursive annotation
- * reading capabilities on par with that of the reflection-based
- * {@link org.springframework.core.type.StandardAnnotationMetadata}.
+ * map structure.
+ * <p>This method provides fully recursive annotation reading capabilities on par with
+ * the reflection-based {@link org.springframework.core.type.StandardAnnotationMetadata}.
* @param annotation the annotation to retrieve the attributes for
* @param classValuesAsString whether to turn Class references into Strings (for
- * compatibility with {@link org.springframework.core.type.AnnotationMetadata} or to
- * preserve them as Class references
+ * compatibility with {@link org.springframework.core.type.AnnotationMetadata}
+ * or to preserve them as Class references
* @param nestedAnnotationsAsMap whether to turn nested Annotation instances into
* {@link AnnotationAttributes} maps (for compatibility with
* {@link org.springframework.core.type.AnnotationMetadata} or to preserve them as
@@ -400,34 +568,7 @@ public abstract class AnnotationUtils {
if (method.getParameterTypes().length == 0 && method.getReturnType() != void.class) {
try {
Object value = method.invoke(annotation);
- if (classValuesAsString) {
- if (value instanceof Class) {
- value = ((Class<?>) value).getName();
- }
- else if (value instanceof Class[]) {
- Class<?>[] clazzArray = (Class[]) value;
- String[] newValue = new String[clazzArray.length];
- for (int i = 0; i < clazzArray.length; i++) {
- newValue[i] = clazzArray[i].getName();
- }
- value = newValue;
- }
- }
- if (nestedAnnotationsAsMap && value instanceof Annotation) {
- attrs.put(method.getName(),
- getAnnotationAttributes((Annotation) value, classValuesAsString, true));
- }
- else if (nestedAnnotationsAsMap && value instanceof Annotation[]) {
- Annotation[] realAnnotations = (Annotation[]) value;
- AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length];
- for (int i = 0; i < realAnnotations.length; i++) {
- mappedAnnotations[i] = getAnnotationAttributes(realAnnotations[i], classValuesAsString, true);
- }
- attrs.put(method.getName(), mappedAnnotations);
- }
- else {
- attrs.put(method.getName(), value);
- }
+ attrs.put(method.getName(), adaptValue(value, classValuesAsString, nestedAnnotationsAsMap));
}
catch (Exception ex) {
throw new IllegalStateException("Could not obtain annotation attribute values", ex);
@@ -438,6 +579,48 @@ public abstract class AnnotationUtils {
}
/**
+ * Adapt the given value according to the given class and nested annotation settings.
+ * @param value the annotation attribute value
+ * @param classValuesAsString whether to turn Class references into Strings (for
+ * compatibility with {@link org.springframework.core.type.AnnotationMetadata}
+ * or to preserve them as Class references
+ * @param nestedAnnotationsAsMap whether to turn nested Annotation instances into
+ * {@link AnnotationAttributes} maps (for compatibility with
+ * {@link org.springframework.core.type.AnnotationMetadata} or to preserve them as
+ * Annotation instances
+ * @return the adapted value, or the original value if no adaptation is needed
+ */
+ static Object adaptValue(Object value, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
+ if (classValuesAsString) {
+ if (value instanceof Class) {
+ value = ((Class<?>) value).getName();
+ }
+ else if (value instanceof Class[]) {
+ Class<?>[] clazzArray = (Class[]) value;
+ String[] newValue = new String[clazzArray.length];
+ for (int i = 0; i < clazzArray.length; i++) {
+ newValue[i] = clazzArray[i].getName();
+ }
+ value = newValue;
+ }
+ }
+ if (nestedAnnotationsAsMap && value instanceof Annotation) {
+ return getAnnotationAttributes((Annotation) value, classValuesAsString, true);
+ }
+ else if (nestedAnnotationsAsMap && value instanceof Annotation[]) {
+ Annotation[] realAnnotations = (Annotation[]) value;
+ AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length];
+ for (int i = 0; i < realAnnotations.length; i++) {
+ mappedAnnotations[i] = getAnnotationAttributes(realAnnotations[i], classValuesAsString, true);
+ }
+ return mappedAnnotations;
+ }
+ else {
+ return value;
+ }
+ }
+
+ /**
* Retrieve the <em>value</em> of the {@code &quot;value&quot;} attribute of a
* single-element Annotation, given an annotation instance.
* @param annotation the annotation instance from which to retrieve the value
@@ -449,7 +632,7 @@ public abstract class AnnotationUtils {
}
/**
- * Retrieve the <em>value</em> of a named Annotation attribute, given an annotation instance.
+ * Retrieve the <em>value</em> of a named attribute, given an annotation instance.
* @param annotation the annotation instance from which to retrieve the value
* @param attributeName the name of the attribute value to retrieve
* @return the attribute value, or {@code null} if not found
@@ -478,7 +661,7 @@ public abstract class AnnotationUtils {
}
/**
- * Retrieve the <em>default value</em> of a named Annotation attribute, given an annotation instance.
+ * Retrieve the <em>default value</em> of a named attribute, given an annotation instance.
* @param annotation the annotation instance from which to retrieve the default value
* @param attributeName the name of the attribute value to retrieve
* @return the default value of the named attribute, or {@code null} if not found
@@ -500,7 +683,8 @@ public abstract class AnnotationUtils {
}
/**
- * Retrieve the <em>default value</em> of a named Annotation attribute, given the {@link Class annotation type}.
+ * Retrieve the <em>default value</em> of a named attribute, given the
+ * {@link Class annotation type}.
* @param annotationType the <em>annotation type</em> for which the default value should be retrieved
* @param attributeName the name of the attribute value to retrieve.
* @return the default value of the named attribute, or {@code null} if not found
@@ -515,4 +699,68 @@ public abstract class AnnotationUtils {
}
}
+
+ private static void logIntrospectionFailure(AnnotatedElement annotatedElement, Exception ex) {
+ Log loggerToUse = logger;
+ if (loggerToUse == null) {
+ loggerToUse = LogFactory.getLog(AnnotationUtils.class);
+ logger = loggerToUse;
+ }
+ if (loggerToUse.isInfoEnabled()) {
+ loggerToUse.info("Failed to introspect annotations on [" + annotatedElement + "]: " + ex);
+ }
+ }
+
+
+ private static class AnnotationCollector<A extends Annotation> {
+
+ private final Class<? extends Annotation> containerAnnotationType;
+
+ private final Class<A> annotationType;
+
+ private final Set<AnnotatedElement> visited = new HashSet<AnnotatedElement>();
+
+ private final Set<A> result = new LinkedHashSet<A>();
+
+ public AnnotationCollector(Class<? extends Annotation> containerAnnotationType, Class<A> annotationType) {
+ this.containerAnnotationType = containerAnnotationType;
+ this.annotationType = annotationType;
+ }
+
+ public Set<A> getResult(AnnotatedElement element) {
+ process(element);
+ return Collections.unmodifiableSet(this.result);
+ }
+
+ @SuppressWarnings("unchecked")
+ private void process(AnnotatedElement annotatedElement) {
+ if (this.visited.add(annotatedElement)) {
+ for (Annotation ann : annotatedElement.getAnnotations()) {
+ if (ObjectUtils.nullSafeEquals(this.annotationType, ann.annotationType())) {
+ this.result.add((A) ann);
+ }
+ else if (ObjectUtils.nullSafeEquals(this.containerAnnotationType, ann.annotationType())) {
+ this.result.addAll(getValue(ann));
+ }
+ else if (!isInJavaLangAnnotationPackage(ann)) {
+ process(ann.annotationType());
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private List<A> getValue(Annotation annotation) {
+ try {
+ Method method = annotation.annotationType().getDeclaredMethod("value");
+ ReflectionUtils.makeAccessible(method);
+ return Arrays.asList((A[]) method.invoke(annotation));
+ }
+ catch (Exception ex) {
+ // Unable to read value from repeating annotation container -> ignore it.
+ return Collections.emptyList();
+ }
+ }
+ }
+
}