一、前言
通过spring-core提供额org.springframework.core.annotation.AnnotationUtils工具类提供的findAnnotation、getAnnotation等方法查找类、及其方法的注释对象内容。
二、源码分享
package org.springframework.core.annotation;@b@@b@import java.lang.annotation.Annotation;@b@import java.lang.reflect.AnnotatedElement;@b@import java.lang.reflect.Method;@b@import java.util.List;@b@import java.util.Map;@b@import java.util.WeakHashMap;@b@@b@import org.springframework.core.BridgeMethodResolver;@b@import org.springframework.util.Assert;@b@@b@/**@b@ * General utility methods for working with annotations, handling bridge methods (which the compiler@b@ * generates for generic declarations) as well as super methods (for optional "annotation inheritance").@b@ * Note that none of this is provided by the JDK's introspection facilities themselves.@b@ *@b@ * <p>As a general rule for runtime-retained annotations (e.g. for transaction control, authorization or service@b@ * exposure), always use the lookup methods on this class (e.g., {@link #findAnnotation(Method, Class)}, {@link@b@ * #getAnnotation(Method, Class)}, and {@link #getAnnotations(Method)}) instead of the plain annotation lookup@b@ * methods in the JDK. You can still explicitly choose between lookup on the given class level only ({@link@b@ * #getAnnotation(Method, Class)}) and lookup in the entire inheritance hierarchy of the given method ({@link@b@ * #findAnnotation(Method, Class)}).@b@ *@b@ * @author Rob Harrop@b@ * @author Juergen Hoeller@b@ * @author Sam Brannen@b@ * @author Mark Fisher@b@ * @author Chris Beams@b@ * @since 2.0@b@ * @see java.lang.reflect.Method#getAnnotations()@b@ * @see java.lang.reflect.Method#getAnnotation(Class)@b@ */@b@public abstract class AnnotationUtils {@b@@b@ /** The attribute name for annotations with a single element */@b@ static final String VALUE = "value";@b@@b@ private static final Map<Class<?>, Boolean> annotatedInterfaceCache = new WeakHashMap<Class<?>, Boolean>();@b@@b@@b@ /**@b@ * Get a single {@link Annotation} of {@code annotationType} from the supplied@b@ * Method, Constructor or Field. Meta-annotations will be searched if the annotation@b@ * is not declared locally on the supplied element.@b@ * @param ae the Method, Constructor or Field from which to get the annotation@b@ * @param annotationType the annotation class to look for, both locally and as a meta-annotation@b@ * @return the matching annotation or {@code null} if not found@b@ * @since 3.1@b@ */@b@ public static <T extends Annotation> T getAnnotation(AnnotatedElement ae, Class<T> annotationType) {@b@ T ann = ae.getAnnotation(annotationType);@b@ if (ann == null) {@b@ for (Annotation metaAnn : ae.getAnnotations()) {@b@ ann = metaAnn.annotationType().getAnnotation(annotationType);@b@ if (ann != null) {@b@ break;@b@ }@b@ }@b@ }@b@ return ann;@b@ }@b@@b@ /**@b@ * Get all {@link Annotation Annotations} from the supplied {@link Method}.@b@ * <p>Correctly handles bridge {@link Method Methods} generated by the compiler.@b@ * @param method the method to look for annotations on@b@ * @return the annotations found@b@ * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method)@b@ */@b@ public static Annotation[] getAnnotations(Method method) {@b@ return BridgeMethodResolver.findBridgedMethod(method).getAnnotations();@b@ }@b@@b@ /**@b@ * Get a single {@link Annotation} of {@code annotationType} from the supplied {@link Method}.@b@ * <p>Correctly handles bridge {@link Method Methods} generated by the compiler.@b@ * @param method the method to look for annotations on@b@ * @param annotationType the annotation class to look for@b@ * @return the annotations found@b@ * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method)@b@ */@b@ public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType) {@b@ Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);@b@ A ann = resolvedMethod.getAnnotation(annotationType);@b@ if (ann == null) {@b@ for (Annotation metaAnn : resolvedMethod.getAnnotations()) {@b@ ann = metaAnn.annotationType().getAnnotation(annotationType);@b@ if (ann != null) {@b@ break;@b@ }@b@ }@b@ }@b@ return ann;@b@ }@b@@b@ /**@b@ * Get a single {@link Annotation} of {@code annotationType} from the supplied {@link Method},@b@ * traversing its super methods if no annotation can be found on the given method itself.@b@ * <p>Annotations on methods are not inherited by default, so we need to handle this explicitly.@b@ * @param method the method to look for annotations on@b@ * @param annotationType the annotation class to look for@b@ * @return the annotation found, or {@code null} if none found@b@ */@b@ public static <A extends Annotation> A findAnnotation(Method method, Class<A> annotationType) {@b@ A annotation = getAnnotation(method, annotationType);@b@ Class<?> clazz = method.getDeclaringClass();@b@ if (annotation == null) {@b@ annotation = searchOnInterfaces(method, annotationType, clazz.getInterfaces());@b@ }@b@ while (annotation == null) {@b@ clazz = clazz.getSuperclass();@b@ if (clazz == null || clazz.equals(Object.class)) {@b@ break;@b@ }@b@ try {@b@ Method equivalentMethod = clazz.getDeclaredMethod(method.getName(), method.getParameterTypes());@b@ annotation = getAnnotation(equivalentMethod, annotationType);@b@ }@b@ catch (NoSuchMethodException ex) {@b@ // No equivalent method found@b@ }@b@ if (annotation == null) {@b@ annotation = searchOnInterfaces(method, annotationType, clazz.getInterfaces());@b@ }@b@ }@b@ return annotation;@b@ }@b@@b@ private static <A extends Annotation> A searchOnInterfaces(Method method, Class<A> annotationType, Class<?>[] ifcs) {@b@ A annotation = null;@b@ for (Class<?> iface : ifcs) {@b@ if (isInterfaceWithAnnotatedMethods(iface)) {@b@ try {@b@ Method equivalentMethod = iface.getMethod(method.getName(), method.getParameterTypes());@b@ annotation = getAnnotation(equivalentMethod, annotationType);@b@ }@b@ catch (NoSuchMethodException ex) {@b@ // Skip this interface - it doesn't have the method...@b@ }@b@ if (annotation != null) {@b@ break;@b@ }@b@ }@b@ }@b@ return annotation;@b@ }@b@@b@ private static boolean isInterfaceWithAnnotatedMethods(Class<?> iface) {@b@ synchronized (annotatedInterfaceCache) {@b@ Boolean flag = annotatedInterfaceCache.get(iface);@b@ if (flag != null) {@b@ return flag;@b@ }@b@ boolean found = false;@b@ for (Method ifcMethod : iface.getMethods()) {@b@ if (ifcMethod.getAnnotations().length > 0) {@b@ found = true;@b@ break;@b@ }@b@ }@b@ annotatedInterfaceCache.put(iface, found);@b@ return found;@b@ }@b@ }@b@@b@ /**@b@ * Find a single {@link Annotation} of {@code annotationType} from the supplied {@link Class},@b@ * traversing its interfaces and superclasses if no annotation can be found on the given class itself.@b@ * <p>This method explicitly handles class-level annotations which are not declared as@b@ * {@link java.lang.annotation.Inherited inherited} <i>as well as annotations on interfaces</i>.@b@ * <p>The algorithm operates as follows: Searches for an annotation on the given class and returns@b@ * it if found. Else searches all interfaces that the given class declares, returning the annotation@b@ * from the first matching candidate, if any. Else proceeds with introspection of the superclass@b@ * of the given class, checking the superclass itself; if no annotation found there, proceeds@b@ * with the interfaces that the superclass declares. Recursing up through the entire superclass@b@ * hierarchy if no match is found.@b@ * @param clazz the class to look for annotations on@b@ * @param annotationType the annotation class to look for@b@ * @return the annotation found, or {@code null} if none found@b@ */@b@ public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType) {@b@ Assert.notNull(clazz, "Class must not be null");@b@ A annotation = clazz.getAnnotation(annotationType);@b@ if (annotation != null) {@b@ return annotation;@b@ }@b@ for (Class<?> ifc : clazz.getInterfaces()) {@b@ annotation = findAnnotation(ifc, annotationType);@b@ if (annotation != null) {@b@ return annotation;@b@ }@b@ }@b@ if (!Annotation.class.isAssignableFrom(clazz)) {@b@ for (Annotation ann : clazz.getAnnotations()) {@b@ annotation = findAnnotation(ann.annotationType(), annotationType);@b@ if (annotation != null) {@b@ return annotation;@b@ }@b@ }@b@ }@b@ Class<?> superClass = clazz.getSuperclass();@b@ if (superClass == null || superClass.equals(Object.class)) {@b@ return null;@b@ }@b@ return findAnnotation(superClass, annotationType);@b@ }@b@@b@ /**@b@ * Find the first {@link Class} in the inheritance hierarchy of the specified {@code clazz}@b@ * (including the specified {@code clazz} itself) which declares an annotation for the@b@ * specified {@code annotationType}, or {@code null} if not found. If the supplied@b@ * {@code clazz} is {@code null}, {@code null} will be returned.@b@ * <p>If the supplied {@code clazz} is an interface, only the interface itself will be checked;@b@ * the inheritance hierarchy for interfaces will not be traversed.@b@ * <p>The standard {@link Class} API does not provide a mechanism for determining which class@b@ * in an inheritance hierarchy actually declares an {@link Annotation}, so we need to handle@b@ * this explicitly.@b@ * @param annotationType the Class object corresponding to the annotation type@b@ * @param clazz the Class object corresponding to the class on which to check for the annotation,@b@ * or {@code null}@b@ * @return the first {@link Class} in the inheritance hierarchy of the specified {@code clazz}@b@ * which declares an annotation for the specified {@code annotationType}, or {@code null}@b@ * if not found@b@ * @see Class#isAnnotationPresent(Class)@b@ * @see Class#getDeclaredAnnotations()@b@ * @see #findAnnotationDeclaringClassForTypes(List, Class)@b@ * @see #isAnnotationDeclaredLocally(Class, Class)@b@ */@b@ public static Class<?> findAnnotationDeclaringClass(Class<? extends Annotation> annotationType, Class<?> clazz) {@b@ Assert.notNull(annotationType, "Annotation type must not be null");@b@ if (clazz == null || clazz.equals(Object.class)) {@b@ return null;@b@ }@b@ return (isAnnotationDeclaredLocally(annotationType, clazz)) ? clazz : findAnnotationDeclaringClass(@b@ annotationType, clazz.getSuperclass());@b@ }@b@@b@ /**@b@ * Find the first {@link Class} in the inheritance hierarchy of the specified@b@ * {@code clazz} (including the specified {@code clazz} itself) which declares@b@ * at least one of the specified {@code annotationTypes}, or {@code null} if@b@ * none of the specified annotation types could be found.@b@ * <p>If the supplied {@code clazz} is {@code null}, {@code null} will be@b@ * returned.@b@ * <p>If the supplied {@code clazz} is an interface, only the interface itself@b@ * will be checked; the inheritance hierarchy for interfaces will not be traversed.@b@ * <p>The standard {@link Class} API does not provide a mechanism for determining@b@ * which class in an inheritance hierarchy actually declares one of several@b@ * candidate {@linkplain Annotation annotations}, so we need to handle this@b@ * explicitly.@b@ * @param annotationTypes the list of Class objects corresponding to the@b@ * annotation types@b@ * @param clazz the Class object corresponding to the class on which to check@b@ * for the annotations, or {@code null}@b@ * @return the first {@link Class} in the inheritance hierarchy of the specified@b@ * {@code clazz} which declares an annotation of at least one of the specified@b@ * {@code annotationTypes}, or {@code null} if not found@b@ * @since 3.2.2@b@ * @see Class#isAnnotationPresent(Class)@b@ * @see Class#getDeclaredAnnotations()@b@ * @see #findAnnotationDeclaringClass(Class, Class)@b@ * @see #isAnnotationDeclaredLocally(Class, Class)@b@ */@b@ public static Class<?> findAnnotationDeclaringClassForTypes(List<Class<? extends Annotation>> annotationTypes, Class<?> clazz) {@b@ Assert.notEmpty(annotationTypes, "The list of annotation types must not be empty");@b@ if (clazz == null || clazz.equals(Object.class)) {@b@ return null;@b@ }@b@ for (Class<? extends Annotation> annotationType : annotationTypes) {@b@ if (isAnnotationDeclaredLocally(annotationType, clazz)) {@b@ return clazz;@b@ }@b@ }@b@ return findAnnotationDeclaringClassForTypes(annotationTypes, clazz.getSuperclass());@b@ }@b@@b@ /**@b@ * Determine whether an annotation for the specified {@code annotationType} is@b@ * declared locally on the supplied {@code clazz}. The supplied {@link Class}@b@ * may represent any type.@b@ * <p>Note: This method does <strong>not</strong> determine if the annotation is@b@ * {@linkplain java.lang.annotation.Inherited inherited}. For greater clarity@b@ * regarding inherited annotations, consider using@b@ * {@link #isAnnotationInherited(Class, Class)} instead.@b@ * @param annotationType the Class object corresponding to the annotation type@b@ * @param clazz the Class object corresponding to the class on which to check for the annotation@b@ * @return {@code true} if an annotation for the specified {@code annotationType}@b@ * is declared locally on the supplied {@code clazz}@b@ * @see Class#getDeclaredAnnotations()@b@ * @see #isAnnotationInherited(Class, Class)@b@ */@b@ public static boolean isAnnotationDeclaredLocally(Class<? extends Annotation> annotationType, Class<?> clazz) {@b@ Assert.notNull(annotationType, "Annotation type must not be null");@b@ Assert.notNull(clazz, "Class must not be null");@b@ boolean declaredLocally = false;@b@ for (Annotation annotation : clazz.getDeclaredAnnotations()) {@b@ if (annotation.annotationType().equals(annotationType)) {@b@ declaredLocally = true;@b@ break;@b@ }@b@ }@b@ return declaredLocally;@b@ }@b@@b@ /**@b@ * Determine whether an annotation for the specified {@code annotationType} is present@b@ * on the supplied {@code clazz} and is {@linkplain java.lang.annotation.Inherited inherited}@b@ * (i.e., not declared locally for the class).@b@ * <p>If the supplied {@code clazz} is an interface, only the interface itself will be checked.@b@ * In accordance with standard meta-annotation semantics, the inheritance hierarchy for interfaces@b@ * will not be traversed. See the {@linkplain java.lang.annotation.Inherited Javadoc} for the@b@ * {@code @Inherited} meta-annotation for further details regarding annotation inheritance.@b@ * @param annotationType the Class object corresponding to the annotation type@b@ * @param clazz the Class object corresponding to the class on which to check for the annotation@b@ * @return {@code true} if an annotation for the specified {@code annotationType} is present@b@ * on the supplied {@code clazz} and is <em>inherited</em>@b@ * @see Class#isAnnotationPresent(Class)@b@ * @see #isAnnotationDeclaredLocally(Class, Class)@b@ */@b@ public static boolean isAnnotationInherited(Class<? extends Annotation> annotationType, Class<?> clazz) {@b@ Assert.notNull(annotationType, "Annotation type must not be null");@b@ Assert.notNull(clazz, "Class must not be null");@b@ return (clazz.isAnnotationPresent(annotationType) && !isAnnotationDeclaredLocally(annotationType, clazz));@b@ }@b@@b@ /**@b@ * Retrieve the given annotation's attributes as a Map, preserving all attribute types@b@ * as-is.@b@ * <p>Note: As of Spring 3.1.1, the returned map is actually an@b@ * {@link AnnotationAttributes} instance, however the Map signature of this method has@b@ * been preserved for binary compatibility.@b@ * @param annotation the annotation to retrieve the attributes for@b@ * @return the Map of annotation attributes, with attribute names as keys and@b@ * corresponding attribute values as values@b@ */@b@ public static Map<String, Object> getAnnotationAttributes(Annotation annotation) {@b@ return getAnnotationAttributes(annotation, false, false);@b@ }@b@@b@ /**@b@ * Retrieve the given annotation's attributes as a Map. Equivalent to calling@b@ * {@link #getAnnotationAttributes(Annotation, boolean, boolean)} with@b@ * the {@code nestedAnnotationsAsMap} parameter set to {@code false}.@b@ * <p>Note: As of Spring 3.1.1, the returned map is actually an@b@ * {@link AnnotationAttributes} instance, however the Map signature of this method has@b@ * been preserved for binary compatibility.@b@ * @param annotation the annotation to retrieve the attributes for@b@ * @param classValuesAsString whether to turn Class references into Strings (for@b@ * compatibility with {@link org.springframework.core.type.AnnotationMetadata} or to@b@ * preserve them as Class references@b@ * @return the Map of annotation attributes, with attribute names as keys and@b@ * corresponding attribute values as values@b@ */@b@ public static Map<String, Object> getAnnotationAttributes(Annotation annotation, boolean classValuesAsString) {@b@ return getAnnotationAttributes(annotation, classValuesAsString, false);@b@ }@b@@b@ /**@b@ * Retrieve the given annotation's attributes as an {@link AnnotationAttributes}@b@ * map structure. Implemented in Spring 3.1.1 to provide fully recursive annotation@b@ * reading capabilities on par with that of the reflection-based@b@ * {@link org.springframework.core.type.StandardAnnotationMetadata}.@b@ * @param annotation the annotation to retrieve the attributes for@b@ * @param classValuesAsString whether to turn Class references into Strings (for@b@ * compatibility with {@link org.springframework.core.type.AnnotationMetadata} or to@b@ * preserve them as Class references@b@ * @param nestedAnnotationsAsMap whether to turn nested Annotation instances into@b@ * {@link AnnotationAttributes} maps (for compatibility with@b@ * {@link org.springframework.core.type.AnnotationMetadata} or to preserve them as@b@ * Annotation instances@b@ * @return the annotation attributes (a specialized Map) with attribute names as keys@b@ * and corresponding attribute values as values@b@ * @since 3.1.1@b@ */@b@ public static AnnotationAttributes getAnnotationAttributes(Annotation annotation, boolean classValuesAsString,@b@ boolean nestedAnnotationsAsMap) {@b@@b@ AnnotationAttributes attrs = new AnnotationAttributes();@b@ Method[] methods = annotation.annotationType().getDeclaredMethods();@b@ for (Method method : methods) {@b@ if (method.getParameterTypes().length == 0 && method.getReturnType() != void.class) {@b@ try {@b@ Object value = method.invoke(annotation);@b@ if (classValuesAsString) {@b@ if (value instanceof Class) {@b@ value = ((Class<?>) value).getName();@b@ }@b@ else if (value instanceof Class[]) {@b@ Class<?>[] clazzArray = (Class[]) value;@b@ String[] newValue = new String[clazzArray.length];@b@ for (int i = 0; i < clazzArray.length; i++) {@b@ newValue[i] = clazzArray[i].getName();@b@ }@b@ value = newValue;@b@ }@b@ }@b@ if (nestedAnnotationsAsMap && value instanceof Annotation) {@b@ attrs.put(method.getName(),@b@ getAnnotationAttributes((Annotation) value, classValuesAsString, nestedAnnotationsAsMap));@b@ }@b@ else if (nestedAnnotationsAsMap && value instanceof Annotation[]) {@b@ Annotation[] realAnnotations = (Annotation[]) value;@b@ AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length];@b@ for (int i = 0; i < realAnnotations.length; i++) {@b@ mappedAnnotations[i] = getAnnotationAttributes(realAnnotations[i], classValuesAsString,@b@ nestedAnnotationsAsMap);@b@ }@b@ attrs.put(method.getName(), mappedAnnotations);@b@ }@b@ else {@b@ attrs.put(method.getName(), value);@b@ }@b@ }@b@ catch (Exception ex) {@b@ throw new IllegalStateException("Could not obtain annotation attribute values", ex);@b@ }@b@ }@b@ }@b@ return attrs;@b@ }@b@@b@ /**@b@ * Retrieve the <em>value</em> of the {@code "value"} attribute of a@b@ * single-element Annotation, given an annotation instance.@b@ * @param annotation the annotation instance from which to retrieve the value@b@ * @return the attribute value, or {@code null} if not found@b@ * @see #getValue(Annotation, String)@b@ */@b@ public static Object getValue(Annotation annotation) {@b@ return getValue(annotation, VALUE);@b@ }@b@@b@ /**@b@ * Retrieve the <em>value</em> of a named Annotation attribute, given an annotation instance.@b@ * @param annotation the annotation instance from which to retrieve the value@b@ * @param attributeName the name of the attribute value to retrieve@b@ * @return the attribute value, or {@code null} if not found@b@ * @see #getValue(Annotation)@b@ */@b@ public static Object getValue(Annotation annotation, String attributeName) {@b@ try {@b@ Method method = annotation.annotationType().getDeclaredMethod(attributeName, new Class[0]);@b@ return method.invoke(annotation);@b@ }@b@ catch (Exception ex) {@b@ return null;@b@ }@b@ }@b@@b@ /**@b@ * Retrieve the <em>default value</em> of the {@code "value"} attribute@b@ * of a single-element Annotation, given an annotation instance.@b@ * @param annotation the annotation instance from which to retrieve the default value@b@ * @return the default value, or {@code null} if not found@b@ * @see #getDefaultValue(Annotation, String)@b@ */@b@ public static Object getDefaultValue(Annotation annotation) {@b@ return getDefaultValue(annotation, VALUE);@b@ }@b@@b@ /**@b@ * Retrieve the <em>default value</em> of a named Annotation attribute, given an annotation instance.@b@ * @param annotation the annotation instance from which to retrieve the default value@b@ * @param attributeName the name of the attribute value to retrieve@b@ * @return the default value of the named attribute, or {@code null} if not found@b@ * @see #getDefaultValue(Class, String)@b@ */@b@ public static Object getDefaultValue(Annotation annotation, String attributeName) {@b@ return getDefaultValue(annotation.annotationType(), attributeName);@b@ }@b@@b@ /**@b@ * Retrieve the <em>default value</em> of the {@code "value"} attribute@b@ * of a single-element Annotation, given the {@link Class annotation type}.@b@ * @param annotationType the <em>annotation type</em> for which the default value should be retrieved@b@ * @return the default value, or {@code null} if not found@b@ * @see #getDefaultValue(Class, String)@b@ */@b@ public static Object getDefaultValue(Class<? extends Annotation> annotationType) {@b@ return getDefaultValue(annotationType, VALUE);@b@ }@b@@b@ /**@b@ * Retrieve the <em>default value</em> of a named Annotation attribute, given the {@link Class annotation type}.@b@ * @param annotationType the <em>annotation type</em> for which the default value should be retrieved@b@ * @param attributeName the name of the attribute value to retrieve.@b@ * @return the default value of the named attribute, or {@code null} if not found@b@ * @see #getDefaultValue(Annotation, String)@b@ */@b@ public static Object getDefaultValue(Class<? extends Annotation> annotationType, String attributeName) {@b@ try {@b@ Method method = annotationType.getDeclaredMethod(attributeName, new Class[0]);@b@ return method.getDefaultValue();@b@ }@b@ catch (Exception ex) {@b@ return null;@b@ }@b@ }@b@@b@}