首页

分享springframework提供的注释AnnotationUtils工具类获取类、或类方法的注释对象内容

标签:注释工具类,springframework,AnnotationUtils     发布时间:2017-11-30   

一、前言

通过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 &quot;annotation inheritance&quot;).@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 &quot;value&quot;} 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 &quot;value&quot;} 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 &quot;value&quot;} 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@}