/* * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.springframework.core.annotation;import java.lang.annotation.Annotation;import java.lang.reflect.AnnotatedElement;import java.lang.reflect.Array;import java.lang.reflect.InvocationHandler;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.util.ArrayList;import java.util.Collections;import java.util.HashSet;import java.util.LinkedHashMap;import java.util.LinkedHashSet;import java.util.List;import java.util.Map;import java.util.Set;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.ConcurrentReferenceHashMap;import org.springframework.util.ObjectUtils;import org.springframework.util.ReflectionUtils;import org.springframework.util.StringUtils;/** * General utility methods for working with annotations, handling meta-annotations, * bridge methods (which the compiler generates for generic declarations) as well * as super methods (for optional annotation inheritance). * *Note that most of the features of this class are not provided by the * JDK's introspection facilities themselves. * *
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 get lookup on the given class level only * ({@link #getAnnotation(Method, Class)}) and a find lookup in the entire * inheritance hierarchy of the given method ({@link #findAnnotation(Method, Class)}). * * get* lookup on the given class level only 只在当前类/字段/方法/构造函数上查找注解 * find* lookup in the entire inheritance hierarchy of the given 在当前类/字段/方法/构造函数上的继承结构上查找注解 * * *
Terminology
* The terms directly present, indirectly present, and * present have the same meanings as defined in the class-level * Javadoc for {@link AnnotatedElement} (in Java 8). * *An annotation is meta-present on an element if the annotation * is declared as a meta-annotation on some other annotation which is * present on the element. Annotation {@code A} is meta-present * on another annotation if {@code A} is either directly present or * meta-present on the other annotation. * *
Meta-annotation Support
*Most {@code find*()} methods and some {@code get*()} methods in this * class provide support for finding annotations used as meta-annotations. * Consult the Javadoc for each method in this class for details. For support * for meta-annotations with attribute overrides in * composed annotations, use {@link AnnotatedElementUtils} instead. * *
Attribute Aliases
*All public methods in this class that return annotations, arrays of * annotations, or {@link AnnotationAttributes} transparently support attribute * aliases configured via {@link AliasFor @AliasFor}. Consult the various * {@code synthesizeAnnotation*(..)} methods for details. * *
Search Scope
采用的查找算法是,找到第一个存在的注解类型就返回 *The search algorithms used by methods in this class stop searching for * an annotation once the first annotation of the specified type has been * found. As a consequence, additional annotations of the specified type will * be silently ignored. * * @author Rob Harrop * @author Juergen Hoeller * @author Sam Brannen * @author Mark Fisher * @author Chris Beams * @author Phillip Webb * @since 2.0 * @see AliasFor * @see AnnotationAttributes * @see AnnotatedElementUtils * @see BridgeMethodResolver * @see java.lang.reflect.AnnotatedElement#getAnnotations() * @see java.lang.reflect.AnnotatedElement#getAnnotation(Class) * @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotations() */public abstract class AnnotationUtils { /** * The attribute name for annotations with a single element. */ public static final String VALUE = "value"; private static final Map
findAnnotationCache = new ConcurrentReferenceHashMap (256); private static final Map metaPresentCache = new ConcurrentReferenceHashMap (256); private static final Map , Boolean> annotatedInterfaceCache = new ConcurrentReferenceHashMap , Boolean>(256); private static final Map , Boolean> synthesizableCache = new ConcurrentReferenceHashMap , Boolean>(256); private static final Map , Map >> attributeAliasesCache = new ConcurrentReferenceHashMap , Map >>(256); private static final Map , List > attributeMethodsCache = new ConcurrentReferenceHashMap , List >(256); private static final Map aliasDescriptorCache = new ConcurrentReferenceHashMap (256); private static transient Log logger; /** * 在注解上查找指定类型的注解,例如在@ConditionalOnClass查找@Conditional * Get a single {@link Annotation} of {@code annotationType} from the supplied * annotation: either the given annotation itself or a direct meta-annotation * thereof. * Note that this method supports only a single level of meta-annotations. * For support for arbitrary levels of meta-annotations, use one of the * {@code find*()} methods instead. * @param ann the Annotation to check * @param annotationType the annotation type to look for, both locally and as a meta-annotation * @return the first matching annotation, or {@code null} if not found * @since 4.0 */ @SuppressWarnings("unchecked") public static A getAnnotation(Annotation ann, Class annotationType) { if (annotationType.isInstance(ann)) { return synthesizeAnnotation((A) ann); } Class annotatedElement = ann.annotationType(); try { return synthesizeAnnotation(annotatedElement.getAnnotation(annotationType), annotatedElement); } catch (Exception ex) { handleIntrospectionFailure(annotatedElement, ex); } return null; } /** * 在注解元素上查找注解,如果注解元素上没有指定的注解,则到元素的注解中查找指定注解元素 * Get a single {@link Annotation} of {@code annotationType} from the supplied * {@link AnnotatedElement}, where the annotation is either present or * meta-present on the {@code AnnotatedElement}. *
Note that this method supports only a single level of meta-annotations. * For support for arbitrary levels of meta-annotations, use * {@link #findAnnotation(AnnotatedElement, Class)} instead. * @param annotatedElement the {@code AnnotatedElement} from which to get the annotation * @param annotationType the annotation type to look for, both locally and as a meta-annotation * @return the first matching annotation, or {@code null} if not found * @since 3.1 */ public static A getAnnotation(AnnotatedElement annotatedElement, Class annotationType) { try { A annotation = annotatedElement.getAnnotation(annotationType); if (annotation == null) { for (Annotation metaAnn : annotatedElement.getAnnotations()) { annotation = metaAnn.annotationType().getAnnotation(annotationType); if (annotation != null) { break; } } } return synthesizeAnnotation(annotation, annotatedElement); } catch (Exception ex) { handleIntrospectionFailure(annotatedElement, ex); } return null; } /** * Get a single {@link Annotation} of {@code annotationType} from the * supplied {@link Method}, where the annotation is either present * or meta-present on the method. *
Correctly handles bridge {@link Method Methods} generated by the compiler. *
Note that this method supports only a single level of meta-annotations. * For support for arbitrary levels of meta-annotations, use * {@link #findAnnotation(Method, Class)} instead. * @param method the method to look for annotations on * @param annotationType the annotation type to look for * @return the first matching annotation, or {@code null} if not found * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method) * @see #getAnnotation(AnnotatedElement, Class) */ public static A getAnnotation(Method method, Class annotationType) { Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method); return getAnnotation((AnnotatedElement) resolvedMethod, annotationType); } /** * Get all {@link Annotation Annotations} that are present on the * supplied {@link AnnotatedElement}. *
Correctly handles bridge {@link Method Methods} generated by the compiler. *
Meta-annotations will not be searched. * @param method the Method to retrieve annotations from * @return the annotations found, an empty array, or {@code null} if not * resolvable (e.g. because nested Class values in annotation attributes * failed to resolve at runtime) * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method) * @see AnnotatedElement#getAnnotations() */ public static Annotation[] getAnnotations(Method method) { try { return synthesizeAnnotationArray(BridgeMethodResolver.findBridgedMethod(method).getAnnotations(), method); } catch (Exception ex) { handleIntrospectionFailure(method, ex); } return null; } /** * Delegates to {@link #getRepeatableAnnotations(AnnotatedElement, Class, Class)}. * @since 4.0 * @see #getRepeatableAnnotations(AnnotatedElement, Class, Class) * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class) * @deprecated As of Spring Framework 4.2, use {@code getRepeatableAnnotations()} * or {@code getDeclaredRepeatableAnnotations()} instead. */ @Deprecated public static Set getRepeatableAnnotation(Method method, Class containerAnnotationType, Class annotationType) { return getRepeatableAnnotations(method, annotationType, containerAnnotationType); } /** * Delegates to {@link #getRepeatableAnnotations(AnnotatedElement, Class, Class)}. * @since 4.0 * @see #getRepeatableAnnotations(AnnotatedElement, Class, Class) * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class) * @deprecated As of Spring Framework 4.2, use {@code getRepeatableAnnotations()} * or {@code getDeclaredRepeatableAnnotations()} instead. */ @Deprecated public static Set getRepeatableAnnotation(AnnotatedElement annotatedElement, Class containerAnnotationType, Class annotationType) { return getRepeatableAnnotations(annotatedElement, annotationType, containerAnnotationType); } /** * Get the repeatable {@linkplain Annotation annotations} of * {@code annotationType} from the supplied {@link AnnotatedElement}, where * such annotations are either present, indirectly present, * or meta-present on the element. *
Handles both single annotations and annotations nested within a * container annotation. *
Meta-annotations will be searched if the annotation is not * present on the supplied element. * @param annotatedElement the element to look for annotations on * @param annotationType the annotation type to look for * @return the annotations found or an empty set (never {@code null}) * @since 4.2 * @see #getRepeatableAnnotations(AnnotatedElement, Class, Class) * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class) * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod * @see java.lang.annotation.Repeatable * @see java.lang.reflect.AnnotatedElement#getAnnotationsByType */ public static Set getRepeatableAnnotations(AnnotatedElement annotatedElement, Class annotationType) { return getRepeatableAnnotations(annotatedElement, annotationType, null); } /** * Get the repeatable {@linkplain Annotation annotations} of * {@code annotationType} from the supplied {@link AnnotatedElement}, where * such annotations are either present, indirectly present, * or meta-present on the element. *
Handles both single annotations and annotations nested within a * container annotation. *
Meta-annotations will be searched if the annotation is not * present on the supplied element. * @param annotatedElement the element to look for annotations on * @param annotationType the annotation type to look for * @param containerAnnotationType the type of the container that holds * the annotations; may be {@code null} if a container is not supported * or if it should be looked up via @{@link java.lang.annotation.Repeatable} * when running on Java 8 or higher * @return the annotations found or an empty set (never {@code null}) * @since 4.2 * @see #getRepeatableAnnotations(AnnotatedElement, Class) * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class) * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class) * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod * @see java.lang.annotation.Repeatable * @see java.lang.reflect.AnnotatedElement#getAnnotationsByType */ public static Set getRepeatableAnnotations(AnnotatedElement annotatedElement, Class annotationType, Class containerAnnotationType) { Set annotations = getDeclaredRepeatableAnnotations(annotatedElement, annotationType, containerAnnotationType); if (!annotations.isEmpty()) { return annotations; } if (annotatedElement instanceof Class) { Class superclass = ((Class ) annotatedElement).getSuperclass(); if (superclass != null && Object.class != superclass) { return getRepeatableAnnotations(superclass, annotationType, containerAnnotationType); } } return getRepeatableAnnotations(annotatedElement, annotationType, containerAnnotationType, false); } /** * Get the declared repeatable {@linkplain Annotation annotations} * of {@code annotationType} from the supplied {@link AnnotatedElement}, * where such annotations are either directly present, * indirectly present, or meta-present on the element. *
Handles both single annotations and annotations nested within a * container annotation. *
Meta-annotations will be searched if the annotation is not * present on the supplied element. * @param annotatedElement the element to look for annotations on * @param annotationType the annotation type to look for * @return the annotations found or an empty set (never {@code null}) * @since 4.2 * @see #getRepeatableAnnotations(AnnotatedElement, Class) * @see #getRepeatableAnnotations(AnnotatedElement, Class, Class) * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class) * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod * @see java.lang.annotation.Repeatable * @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType */ public static Set getDeclaredRepeatableAnnotations(AnnotatedElement annotatedElement, Class annotationType) { return getDeclaredRepeatableAnnotations(annotatedElement, annotationType, null); } /** * Get the declared repeatable {@linkplain Annotation annotations} * of {@code annotationType} from the supplied {@link AnnotatedElement}, * where such annotations are either directly present, * indirectly present, or meta-present on the element. *
Handles both single annotations and annotations nested within a * container annotation. *
Meta-annotations will be searched if the annotation is not * present on the supplied element. * @param annotatedElement the element to look for annotations on * @param annotationType the annotation type to look for * @param containerAnnotationType the type of the container that holds * the annotations; may be {@code null} if a container is not supported * or if it should be looked up via @{@link java.lang.annotation.Repeatable} * when running on Java 8 or higher * @return the annotations found or an empty set (never {@code null}) * @since 4.2 * @see #getRepeatableAnnotations(AnnotatedElement, Class) * @see #getRepeatableAnnotations(AnnotatedElement, Class, Class) * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class) * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod * @see java.lang.annotation.Repeatable * @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType */ public static Set getDeclaredRepeatableAnnotations(AnnotatedElement annotatedElement, Class annotationType, Class containerAnnotationType) { return getRepeatableAnnotations(annotatedElement, annotationType, containerAnnotationType, true); } /** * Perform the actual work for {@link #getRepeatableAnnotations(AnnotatedElement, Class, Class)} * and {@link #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class)}. *
Meta-annotations will be searched if the annotation is not * present on the supplied element. * @param annotatedElement the element to look for annotations on * @param annotationType the annotation type to look for * @param containerAnnotationType the type of the container that holds * the annotations; may be {@code null} if a container is not supported * or if it should be looked up via @{@link java.lang.annotation.Repeatable} * when running on Java 8 or higher * @param declaredMode {@code true} if only declared annotations (i.e., * directly or indirectly present) should be considered * @return the annotations found or an empty set (never {@code null}) * @since 4.2 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod * @see java.lang.annotation.Repeatable */ private static Set getRepeatableAnnotations(AnnotatedElement annotatedElement, Class annotationType, Class containerAnnotationType, boolean declaredMode) { Assert.notNull(annotatedElement, "AnnotatedElement must not be null"); Assert.notNull(annotationType, "Annotation type must not be null"); try { if (annotatedElement instanceof Method) { annotatedElement = BridgeMethodResolver.findBridgedMethod((Method) annotatedElement); } return new AnnotationCollector(annotationType, containerAnnotationType, declaredMode).getResult(annotatedElement); } catch (Exception ex) { handleIntrospectionFailure(annotatedElement, ex); } return Collections.emptySet(); } /** * Find a single {@link Annotation} of {@code annotationType} on the * supplied {@link AnnotatedElement}. *
Warning: this method operates generically on * annotated elements. In other words, this method does not execute * specialized search algorithms for classes or methods. If you require * the more specific semantics of {@link #findAnnotation(Class, Class)} * or {@link #findAnnotation(Method, Class)}, invoke one of those methods * instead. * @param annotatedElement the {@code AnnotatedElement} on which to find the annotation * @param annotationType the annotation type to look for, both locally and as a meta-annotation * @return the first matching annotation, or {@code null} if not found * @since 4.2 */ public static A findAnnotation(AnnotatedElement annotatedElement, Class annotationType) { Assert.notNull(annotatedElement, "AnnotatedElement must not be null"); if (annotationType == null) { return null; } // Do NOT store result in the findAnnotationCache since doing so could break // findAnnotation(Class, Class) and findAnnotation(Method, Class). A ann = findAnnotation(annotatedElement, annotationType, new HashSet
()); return synthesizeAnnotation(ann, annotatedElement); } /** * 在使用注解的类上查找指定类型的注解,遍历注解本身,直到找到指定的类型。支持在自定义注解的基础上查找 * 框架定义的基本注解,譬如在@SpringBootApplication上查找@Component * Perform the search algorithm for {@link #findAnnotation(AnnotatedElement, Class)} * avoiding endless recursion by tracking which annotations have already * been visited. * @param annotatedElement the {@code AnnotatedElement} on which to find the annotation * @param annotationType the annotation type to look for, both locally and as a meta-annotation * @param visited the set of annotations that have already been visited * @return the first matching annotation, or {@code null} if not found * @since 4.2 */ @SuppressWarnings("unchecked") private static A findAnnotation(AnnotatedElement annotatedElement, Class annotationType, Setvisited) { try { Annotation[] anns = annotatedElement.getDeclaredAnnotations(); for (Annotation ann : anns) { if (ann.annotationType() == annotationType) { return (A) ann; } } for (Annotation ann : anns) { if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) { A annotation = findAnnotation((AnnotatedElement) ann.annotationType(), annotationType, visited); if (annotation != null) { return annotation; } } } } catch (Exception ex) { handleIntrospectionFailure(annotatedElement, ex); } return null; } /** * Find a single {@link Annotation} of {@code annotationType} on the supplied * {@link Method}, traversing its super methods (i.e., from superclasses and * interfaces) if the annotation is not directly present on the given * method itself. * Correctly handles bridge {@link Method Methods} generated by the compiler. *
Meta-annotations will be searched if the annotation is not * directly present on the method. *
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 first matching annotation, or {@code null} if not found * @see #getAnnotation(Method, Class) */ @SuppressWarnings("unchecked") public static A findAnnotation(Method method, Class annotationType) { Assert.notNull(method, "Method must not be null"); if (annotationType == null) { return null; } AnnotationCacheKey cacheKey = new AnnotationCacheKey(method, annotationType); A result = (A) findAnnotationCache.get(cacheKey); if (result == null) { Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method); result = findAnnotation((AnnotatedElement) resolvedMethod, annotationType); if (result == null) { result = searchOnInterfaces(method, annotationType, method.getDeclaringClass().getInterfaces()); } Class clazz = method.getDeclaringClass(); while (result == null) { clazz = clazz.getSuperclass(); if (clazz == null || Object.class == clazz) { break; } try { Method equivalentMethod = clazz.getDeclaredMethod(method.getName(), method.getParameterTypes()); Method resolvedEquivalentMethod = BridgeMethodResolver.findBridgedMethod(equivalentMethod); result = findAnnotation((AnnotatedElement) resolvedEquivalentMethod, annotationType); } catch (NoSuchMethodException ex) { // No equivalent method found } if (result == null) { result = searchOnInterfaces(method, annotationType, clazz.getInterfaces()); } } if (result != null) { result = synthesizeAnnotation(result, method); findAnnotationCache.put(cacheKey, result); } } return result; } private static A searchOnInterfaces(Method method, Class annotationType, Class ... ifcs) { A annotation = null; for (Class iface : ifcs) { if (isInterfaceWithAnnotatedMethods(iface)) { try { Method equivalentMethod = iface.getMethod(method.getName(), method.getParameterTypes()); annotation = getAnnotation(equivalentMethod, annotationType); } catch (NoSuchMethodException ex) { // Skip this interface - it doesn't have the method... } if (annotation != null) { break; } } } return annotation; } static boolean isInterfaceWithAnnotatedMethods(Class iface) { Boolean found = annotatedInterfaceCache.get(iface); if (found != null) { return found.booleanValue(); } found = Boolean.FALSE; for (Method ifcMethod : iface.getMethods()) { try { if (ifcMethod.getAnnotations().length > 0) { found = Boolean.TRUE; break; } } catch (Exception ex) { handleIntrospectionFailure(ifcMethod, ex); } } annotatedInterfaceCache.put(iface, found); return found.booleanValue(); } /** * Find a single {@link Annotation} of {@code annotationType} on the * supplied {@link Class}, traversing its interfaces, annotations, and * superclasses if the annotation is not directly present on * the given class itself. *
The algorithm operates as follows: *
*Note: in this context, the term recursively 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 type of annotation to look for * @return the first matching annotation, or {@code null} if not found */ public static A findAnnotation(Class clazz, Class annotationType) { return findAnnotation(clazz, annotationType, true); } /** * Perform the actual work for {@link #findAnnotation(AnnotatedElement, Class)}, * honoring the {@code synthesize} flag. * @param clazz the class to look for annotations on * @param annotationType the type of annotation to look for * @param synthesize {@code true} if the result should be * {@linkplain #synthesizeAnnotation(Annotation) synthesized} * @return the first matching annotation, or {@code null} if not found * @since 4.2.1 */ @SuppressWarnings("unchecked") private static A findAnnotation(Class clazz, Class annotationType, boolean synthesize) { Assert.notNull(clazz, "Class must not be null"); if (annotationType == null) { return null; } AnnotationCacheKey cacheKey = new AnnotationCacheKey(clazz, annotationType); A result = (A) findAnnotationCache.get(cacheKey); if (result == null) { result = findAnnotation(clazz, annotationType, new HashSet
()); if (result != null && synthesize) { result = synthesizeAnnotation(result, clazz); findAnnotationCache.put(cacheKey, result); } } return result; } /** * Perform the search algorithm for {@link #findAnnotation(Class, Class)}, * avoiding endless recursion by tracking which annotations have already * been visited. * @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 first matching annotation, or {@code null} if not found */ @SuppressWarnings("unchecked") private static A findAnnotation(Class clazz, Class annotationType, Setvisited) { try { Annotation[] anns = clazz.getDeclaredAnnotations(); for (Annotation ann : anns) { if (ann.annotationType() == annotationType) { return (A) ann; } } for (Annotation ann : anns) { if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) { A annotation = findAnnotation(ann.annotationType(), annotationType, visited); if (annotation != null) { return annotation; } } } } catch (Exception ex) { handleIntrospectionFailure(clazz, ex); return null; } for (Class ifc : clazz.getInterfaces()) { A annotation = findAnnotation(ifc, annotationType, visited); if (annotation != null) { return annotation; } } Class superclass = clazz.getSuperclass(); if (superclass == null || Object.class == superclass) { return null; } return findAnnotation(superclass, annotationType, visited); } /** * Find the first {@link Class} in the inheritance hierarchy of the * specified {@code clazz} (including the specified {@code clazz} itself) * on which an annotation of the specified {@code annotationType} is * directly present. * Meta-annotations will not be searched. *
Meta-annotations will not be searched. *
The supplied {@link Class} may represent any type. *
Meta-annotations will not be searched. *
Meta-annotations will not be searched. *
* @param annotatedElement the element that is annotated with the supplied annotation; * may be {@code null} if unknown * @param annotation the annotation to retrieve the attributes for * @param classValuesAsString whether to convert Class references into Strings (for * compatibility with {@link org.springframework.core.type.AnnotationMetadata}) * or to preserve them as Class references * @param nestedAnnotationsAsMap whether to convert nested annotations into * {@link AnnotationAttributes} maps (for compatibility with * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as * {@code Annotation} instances * @return the annotation attributes (a specialized Map) with attribute names as keys * and corresponding attribute values as values (never {@code null}) * @since 4.2 * @see #postProcessAnnotationAttributes */ static AnnotationAttributes retrieveAnnotationAttributes(AnnotatedElement annotatedElement, Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { Class annotationType = annotation.annotationType(); AnnotationAttributes attributes = new AnnotationAttributes(annotationType); for (Method method : getAttributeMethods(annotationType)) { try { // 获取注解属性方法对应的值 Object attributeValue = method.invoke(annotation); // 方法默认值 Method.getDefaultValue,此方法只适用注解类的方法。 Object defaultValue = method.getDefaultValue(); // 注解值为空|等于默认值,但默认值非空 if (defaultValue != null && ObjectUtils.nullSafeEquals(attributeValue, defaultValue)) { attributeValue = new DefaultValueHolder(defaultValue); } // 将注解方法识别为属性,对值进行转换处理 attributes.put(method.getName(), adaptValue(annotatedElement, attributeValue, classValuesAsString, nestedAnnotationsAsMap)); } catch (Exception ex) { if (ex instanceof InvocationTargetException) { Throwable targetException = ((InvocationTargetException) ex).getTargetException(); rethrowAnnotationConfigurationException(targetException); } throw new IllegalStateException("Could not obtain annotation attribute value for " + method, ex); } } return attributes; } /** * Adapt the given value according to the given class and nested annotation settings. *Nested annotations will be * {@linkplain #synthesizeAnnotation(Annotation, AnnotatedElement) synthesized}. * @param annotatedElement the element that is annotated, used for contextual * logging; may be {@code null} if unknown * @param value the annotation attribute value * @param classValuesAsString whether to convert Class references into Strings (for * compatibility with {@link org.springframework.core.type.AnnotationMetadata}) * or to preserve them as Class references * @param nestedAnnotationsAsMap whether to convert nested annotations into * {@link AnnotationAttributes} maps (for compatibility with * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as * {@code Annotation} instances * @return the adapted value, or the original value if no adaptation is needed */ static Object adaptValue(AnnotatedElement annotatedElement, Object value, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { if (classValuesAsString) { // 如果注解值为Class,返回ClassName if (value instanceof Class) { return ((Class
) value).getName(); } // 如果注解为Class数组,返回ClassName数组 else if (value instanceof Class[]) { Class [] clazzArray = (Class []) value; String[] classNames = new String[clazzArray.length]; for (int i = 0; i < clazzArray.length; i++) { classNames[i] = clazzArray[i].getName(); } return classNames; } } // 如果注解值为注解,且允许嵌套注解,获取嵌套注解的值 if (value instanceof Annotation) { Annotation annotation = (Annotation) value; if (nestedAnnotationsAsMap) { return getAnnotationAttributes(annotatedElement, annotation, classValuesAsString, true); } else { return synthesizeAnnotation(annotation, annotatedElement); } } // 如果注解值为注解数组 if (value instanceof Annotation[]) { Annotation[] annotations = (Annotation[]) value; if (nestedAnnotationsAsMap) { AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[annotations.length]; for (int i = 0; i < annotations.length; i++) { mappedAnnotations[i] = getAnnotationAttributes(annotatedElement, annotations[i], classValuesAsString, true); } return mappedAnnotations; } else { return synthesizeAnnotationArray(annotations, annotatedElement); } } // Fallback return value; } /** * Post-process the supplied {@link AnnotationAttributes}. *Specifically, this method enforces attribute alias semantics * for annotation attributes that are annotated with {@link AliasFor @AliasFor} * and replaces default value placeholders with their original default values. * @param annotatedElement the element that is annotated with an annotation or * annotation hierarchy from which the supplied attributes were created; * may be {@code null} if unknown * @param attributes the annotation attributes to post-process * @param classValuesAsString whether to convert Class references into Strings (for * compatibility with {@link org.springframework.core.type.AnnotationMetadata}) * or to preserve them as Class references * @param nestedAnnotationsAsMap whether to convert nested annotations into * {@link AnnotationAttributes} maps (for compatibility with * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as * {@code Annotation} instances * @since 4.2 * @see #retrieveAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean) * @see #getDefaultValue(Class, String) */ static void postProcessAnnotationAttributes(AnnotatedElement annotatedElement, AnnotationAttributes attributes, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { // Abort? if (attributes == null) { return; } Class annotationType = attributes.annotationType(); // Track which attribute values have already been replaced so that we can short // circuit the search algorithms. Set
valuesAlreadyReplaced = new HashSet A synthesizeAnnotation(A annotation) { return synthesizeAnnotation(annotation, null); } /** * 如果注解字段带有别名则返回代理,不带有别名则返回注解本身 * Synthesize an annotation from the supplied {@code annotation} * by wrapping it in a dynamic proxy that transparently enforces * attribute alias semantics for annotation attributes that are * annotated with {@link AliasFor @AliasFor}. * @param annotation the annotation to synthesize * @param annotatedElement the element that is annotated with the supplied * annotation; may be {@code null} if unknown * @return the synthesized annotation if the supplied annotation is * synthesizable; {@code null} if the supplied annotation is * {@code null}; otherwise the supplied annotation unmodified * @throws AnnotationConfigurationException if invalid configuration of * {@code @AliasFor} is detected * @since 4.2 * @see #synthesizeAnnotation(Map, Class, AnnotatedElement) * @see #synthesizeAnnotation(Class) */ @SuppressWarnings("unchecked") public static A synthesizeAnnotation(A annotation, AnnotatedElement annotatedElement) { if (annotation == null) { return null; } if (annotation instanceof SynthesizedAnnotation) { return annotation; } Class annotationType = annotation.annotationType(); if (!isSynthesizable(annotationType)) { return annotation; } DefaultAnnotationAttributeExtractor attributeExtractor = new DefaultAnnotationAttributeExtractor(annotation, annotatedElement); InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(attributeExtractor); // Can always expose Spring's SynthesizedAnnotation marker since we explicitly check for a // synthesizable annotation before (which needs to declare @AliasFor from the same package) Class [] exposedInterfaces = new Class [] {annotationType, SynthesizedAnnotation.class}; return (A) Proxy.newProxyInstance(annotation.getClass().getClassLoader(), exposedInterfaces, handler); } /** * Synthesize an annotation from the supplied map of annotation * attributes by wrapping the map in a dynamic proxy that implements an * annotation of the specified {@code annotationType} and transparently * enforces attribute alias semantics for annotation attributes * that are annotated with {@link AliasFor @AliasFor}. *(); // Validate @AliasFor configuration Map > aliasMap = getAttributeAliasMap(annotationType); for (String attributeName : aliasMap.keySet()) { if (valuesAlreadyReplaced.contains(attributeName)) { continue; } Object value = attributes.get(attributeName); boolean valuePresent = (value != null && !(value instanceof DefaultValueHolder)); for (String aliasedAttributeName : aliasMap.get(attributeName)) { if (valuesAlreadyReplaced.contains(aliasedAttributeName)) { continue; } Object aliasedValue = attributes.get(aliasedAttributeName); boolean aliasPresent = (aliasedValue != null && !(aliasedValue instanceof DefaultValueHolder)); // Something to validate or replace with an alias? if (valuePresent || aliasPresent) { if (valuePresent && aliasPresent) { // Since annotation attributes can be arrays, we must use ObjectUtils.nullSafeEquals(). if (!ObjectUtils.nullSafeEquals(value, aliasedValue)) { String elementAsString = (annotatedElement != null ? annotatedElement.toString() : "unknown element"); throw new AnnotationConfigurationException(String.format( "In AnnotationAttributes for annotation [%s] declared on %s, " + "attribute '%s' and its alias '%s' are declared with values of [%s] and [%s], " + "but only one is permitted.", annotationType.getName(), elementAsString, attributeName, aliasedAttributeName, ObjectUtils.nullSafeToString(value), ObjectUtils.nullSafeToString(aliasedValue))); } } else if (aliasPresent) { // Replace value with aliasedValue attributes.put(attributeName, adaptValue(annotatedElement, aliasedValue, classValuesAsString, nestedAnnotationsAsMap)); valuesAlreadyReplaced.add(attributeName); } else { // Replace aliasedValue with value attributes.put(aliasedAttributeName, adaptValue(annotatedElement, value, classValuesAsString, nestedAnnotationsAsMap)); valuesAlreadyReplaced.add(aliasedAttributeName); } } } } // Replace any remaining placeholders with actual default values for (String attributeName : attributes.keySet()) { if (valuesAlreadyReplaced.contains(attributeName)) { continue; } Object value = attributes.get(attributeName); if (value instanceof DefaultValueHolder) { value = ((DefaultValueHolder) value).defaultValue; attributes.put(attributeName, adaptValue(annotatedElement, value, classValuesAsString, nestedAnnotationsAsMap)); } } } /** * Retrieve the value of the {@code value} attribute of a * single-element Annotation, given an annotation instance. * @param annotation the annotation instance from which to retrieve the value * @return the attribute value, or {@code null} if not found * @see #getValue(Annotation, String) */ public static Object getValue(Annotation annotation) { return getValue(annotation, VALUE); } /** * Retrieve the value 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 * @see #getValue(Annotation) */ public static Object getValue(Annotation annotation, String attributeName) { if (annotation == null || !StringUtils.hasText(attributeName)) { return null; } try { Method method = annotation.annotationType().getDeclaredMethod(attributeName); ReflectionUtils.makeAccessible(method); return method.invoke(annotation); } catch (Exception ex) { return null; } } /** * Retrieve the default value of the {@code value} attribute * of a single-element Annotation, given an annotation instance. * @param annotation the annotation instance from which to retrieve the default value * @return the default value, or {@code null} if not found * @see #getDefaultValue(Annotation, String) */ public static Object getDefaultValue(Annotation annotation) { return getDefaultValue(annotation, VALUE); } /** * Retrieve the default value 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 * @see #getDefaultValue(Class, String) */ public static Object getDefaultValue(Annotation annotation, String attributeName) { if (annotation == null) { return null; } return getDefaultValue(annotation.annotationType(), attributeName); } /** * Retrieve the default value of the {@code value} attribute * of a single-element Annotation, given the {@link Class annotation type}. * @param annotationType the annotation type for which the default value should be retrieved * @return the default value, or {@code null} if not found * @see #getDefaultValue(Class, String) */ public static Object getDefaultValue(Class annotationType) { return getDefaultValue(annotationType, VALUE); } /** * Retrieve the default value of a named attribute, given the * {@link Class annotation type}. * @param annotationType the annotation type 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 * @see #getDefaultValue(Annotation, String) */ public static Object getDefaultValue(Class annotationType, String attributeName) { if (annotationType == null || !StringUtils.hasText(attributeName)) { return null; } try { return annotationType.getDeclaredMethod(attributeName).getDefaultValue(); } catch (Exception ex) { return null; } } /** * Synthesize an annotation from the supplied {@code annotation} * by wrapping it in a dynamic proxy that transparently enforces * attribute alias semantics for annotation attributes that are * annotated with {@link AliasFor @AliasFor}. * @param annotation the annotation to synthesize * @return the synthesized annotation, if the supplied annotation is * synthesizable; {@code null} if the supplied annotation is * {@code null}; otherwise, the supplied annotation unmodified * @throws AnnotationConfigurationException if invalid configuration of * {@code @AliasFor} is detected * @since 4.2 * @see #synthesizeAnnotation(Annotation, AnnotatedElement) */ static Note that {@link AnnotationAttributes} is a specialized type of * {@link Map} that is an ideal candidate for this method's * {@code attributes} argument. * @param attributes the map of annotation attributes to synthesize * @param annotationType the type of annotation to synthesize * @param annotatedElement the element that is annotated with the annotation * corresponding to the supplied attributes; may be {@code null} if unknown * @return the synthesized annotation, or {@code null} if the supplied attributes * map is {@code null} * @throws IllegalArgumentException if a required attribute is missing or if an * attribute is not of the correct type * @throws AnnotationConfigurationException if invalid configuration of * {@code @AliasFor} is detected * @since 4.2 * @see #synthesizeAnnotation(Annotation, AnnotatedElement) * @see #synthesizeAnnotation(Class) * @see #getAnnotationAttributes(AnnotatedElement, Annotation) * @see #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean) */ @SuppressWarnings("unchecked") public static A synthesizeAnnotation(Map
attributes, Class annotationType, AnnotatedElement annotatedElement) { Assert.notNull(annotationType, "annotationType must not be null"); if (attributes == null) { return null; } MapAnnotationAttributeExtractor attributeExtractor = new MapAnnotationAttributeExtractor(attributes, annotationType, annotatedElement); InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(attributeExtractor); Class [] exposedInterfaces = (canExposeSynthesizedMarker(annotationType) ? new Class [] {annotationType, SynthesizedAnnotation.class} : new Class [] {annotationType}); return (A) Proxy.newProxyInstance(annotationType.getClassLoader(), exposedInterfaces, handler); } /** * Synthesize an annotation from its default attributes values. *This method simply delegates to * {@link #synthesizeAnnotation(Map, Class, AnnotatedElement)}, * supplying an empty map for the source attribute values and {@code null} * for the {@link AnnotatedElement}. * @param annotationType the type of annotation to synthesize * @return the synthesized annotation * @throws IllegalArgumentException if a required attribute is missing * @throws AnnotationConfigurationException if invalid configuration of * {@code @AliasFor} is detected * @since 4.2 * @see #synthesizeAnnotation(Map, Class, AnnotatedElement) * @see #synthesizeAnnotation(Annotation, AnnotatedElement) */ public static A synthesizeAnnotation(Class annotationType) { return synthesizeAnnotation(Collections.
emptyMap(), annotationType, null); } /** * Synthesize an array of annotations from the supplied array * of {@code annotations} by creating a new array of the same size and * type and populating it with {@linkplain #synthesizeAnnotation(Annotation) * synthesized} versions of the annotations from the input array. * @param annotations the array of annotations to synthesize * @param annotatedElement the element that is annotated with the supplied * array of annotations; may be {@code null} if unknown * @return a new array of synthesized annotations, or {@code null} if * the supplied array is {@code null} * @throws AnnotationConfigurationException if invalid configuration of * {@code @AliasFor} is detected * @since 4.2 * @see #synthesizeAnnotation(Annotation, AnnotatedElement) * @see #synthesizeAnnotation(Map, Class, AnnotatedElement) */ public static Annotation[] synthesizeAnnotationArray(Annotation[] annotations, AnnotatedElement annotatedElement) { if (annotations == null) { return null; } Annotation[] synthesized = (Annotation[]) Array.newInstance( annotations.getClass().getComponentType(), annotations.length); for (int i = 0; i < annotations.length; i++) { synthesized[i] = synthesizeAnnotation(annotations[i], annotatedElement); } return synthesized; } /** * Synthesize an array of annotations from the supplied array * of {@code maps} of annotation attributes by creating a new array of * {@code annotationType} with the same size and populating it with * {@linkplain #synthesizeAnnotation(Map, Class, AnnotatedElement) * synthesized} versions of the maps from the input array. * @param maps the array of maps of annotation attributes to synthesize * @param annotationType the type of annotations to synthesize; never * {@code null} * @return a new array of synthesized annotations, or {@code null} if * the supplied array is {@code null} * @throws AnnotationConfigurationException if invalid configuration of * {@code @AliasFor} is detected * @since 4.2.1 * @see #synthesizeAnnotation(Map, Class, AnnotatedElement) * @see #synthesizeAnnotationArray(Annotation[], AnnotatedElement) */ @SuppressWarnings("unchecked") static A[] synthesizeAnnotationArray(Map[] maps, Class annotationType) { Assert.notNull(annotationType, "annotationType must not be null"); if (maps == null) { return null; } A[] synthesized = (A[]) Array.newInstance(annotationType, maps.length); for (int i = 0; i < maps.length; i++) { synthesized[i] = synthesizeAnnotation(maps[i], annotationType, null); } return synthesized; } /** * Get a map of all attribute aliases declared via {@code @AliasFor} * in the supplied annotation type. *Otherwise, this method logs an introspection failure (in particular * {@code TypeNotPresentExceptions}) before moving on, assuming nested * Class values were not resolvable within annotation attributes and * thereby effectively pretending there were no annotations on the specified * element. * @param element the element that we tried to introspect annotations on * @param ex the exception that we encountered * @see #rethrowAnnotationConfigurationException */ static void handleIntrospectionFailure(AnnotatedElement element, Exception ex) { rethrowAnnotationConfigurationException(ex); Log loggerToUse = logger; if (loggerToUse == null) { loggerToUse = LogFactory.getLog(AnnotationUtils.class); logger = loggerToUse; } if (element instanceof Class && Annotation.class.isAssignableFrom((Class ) element)) { // Meta-annotation lookup on an annotation type if (loggerToUse.isDebugEnabled()) { loggerToUse.debug("Failed to introspect meta-annotations on [" + element + "]: " + ex); } } else { // Direct annotation lookup on regular Class, Method, Field if (loggerToUse.isInfoEnabled()) { loggerToUse.info("Failed to introspect annotations on [" + element + "]: " + ex); } } } /** * Cache key for the AnnotatedElement cache. */ private static class AnnotationCacheKey { private final AnnotatedElement element; private final Class annotationType; public AnnotationCacheKey(AnnotatedElement element, Class annotationType) { this.element = element; this.annotationType = annotationType; } @Override public boolean equals(Object other) { if (this == other) { return true; } if (!(other instanceof AnnotationCacheKey)) { return false; } AnnotationCacheKey otherKey = (AnnotationCacheKey) other; return (this.element.equals(otherKey.element) && this.annotationType.equals(otherKey.annotationType)); } @Override public int hashCode() { return (this.element.hashCode() * 29 + this.annotationType.hashCode()); } } private static class AnnotationCollector { private static final String REPEATABLE_CLASS_NAME = "java.lang.annotation.Repeatable"; private final Class annotationType; private final Class containerAnnotationType; private final boolean declaredMode; private final Set
visited = new HashSet result = new LinkedHashSet(); AnnotationCollector(Class annotationType, Class containerAnnotationType, boolean declaredMode) { this.annotationType = annotationType; this.containerAnnotationType = (containerAnnotationType != null ? containerAnnotationType : resolveContainerAnnotationType(annotationType)); this.declaredMode = declaredMode; } @SuppressWarnings("unchecked") static Class resolveContainerAnnotationType(Class annotationType) { try { Annotation repeatable = getAnnotation(annotationType, REPEATABLE_CLASS_NAME); if (repeatable != null) { Object value = AnnotationUtils.getValue(repeatable); return (Class ) value; } } catch (Exception ex) { handleIntrospectionFailure(annotationType, ex); } return null; } Set getResult(AnnotatedElement element) { process(element); return Collections.unmodifiableSet(this.result); } @SuppressWarnings("unchecked") private void process(AnnotatedElement element) { if (this.visited.add(element)) { try { Annotation[] annotations = (this.declaredMode ? element.getDeclaredAnnotations() : element.getAnnotations()); for (Annotation ann : annotations) { Class currentAnnotationType = ann.annotationType(); if (ObjectUtils.nullSafeEquals(this.annotationType, currentAnnotationType)) { this.result.add(synthesizeAnnotation((A) ann, element)); } else if (ObjectUtils.nullSafeEquals(this.containerAnnotationType, currentAnnotationType)) { this.result.addAll(getValue(element, ann)); } else if (!isInJavaLangAnnotationPackage(ann)) { process(currentAnnotationType); } } } catch (Exception ex) { handleIntrospectionFailure(element, ex); } } } @SuppressWarnings("unchecked") private List getValue(AnnotatedElement element, Annotation annotation) { try { List synthesizedAnnotations = new ArrayList(); for (A anno : (A[]) AnnotationUtils.getValue(annotation)) { synthesizedAnnotations.add(synthesizeAnnotation(anno, element)); } return synthesizedAnnotations; } catch (Exception ex) { handleIntrospectionFailure(element, ex); } // Unable to read value from repeating annotation container -> ignore it. return Collections.emptyList(); } } /** * {@code AliasDescriptor} encapsulates the declaration of {@code @AliasFor} * on a given annotation attribute and includes support for validating * the configuration of aliases (both explicit and implicit). * @since 4.2.1 * @see #from * @see #getAttributeAliasNames * @see #getAttributeOverrideName */ private static class AliasDescriptor { private final Method sourceAttribute; private final Class sourceAnnotationType; private final String sourceAttributeName; private final Method aliasedAttribute; private final Class aliasedAnnotationType; private final String aliasedAttributeName; private final boolean isAliasPair; /** * Create an {@code AliasDescriptor} from the declaration * of {@code @AliasFor} on the supplied annotation attribute and * validate the configuration of {@code @AliasFor}. * @param attribute the annotation attribute that is annotated with * {@code @AliasFor} * @return an alias descriptor, or {@code null} if the attribute * is not annotated with {@code @AliasFor} * @see #validateAgainst */ public static AliasDescriptor from(Method attribute) { AliasDescriptor descriptor = aliasDescriptorCache.get(attribute); if (descriptor != null) { return descriptor; } AliasFor aliasFor = attribute.getAnnotation(AliasFor.class); if (aliasFor == null) { return null; } descriptor = new AliasDescriptor(attribute, aliasFor); descriptor.validate(); aliasDescriptorCache.put(attribute, descriptor); return descriptor; } @SuppressWarnings("unchecked") private AliasDescriptor(Method sourceAttribute, AliasFor aliasFor) { Class declaringClass = sourceAttribute.getDeclaringClass(); Assert.isTrue(declaringClass.isAnnotation(), "sourceAttribute must be from an annotation"); this.sourceAttribute = sourceAttribute; this.sourceAnnotationType = (Class ) declaringClass; this.sourceAttributeName = sourceAttribute.getName(); this.aliasedAnnotationType = (Annotation.class == aliasFor.annotation() ? this.sourceAnnotationType : aliasFor.annotation()); this.aliasedAttributeName = getAliasedAttributeName(aliasFor, sourceAttribute); if (this.aliasedAnnotationType == this.sourceAnnotationType && this.aliasedAttributeName.equals(this.sourceAttributeName)) { String msg = String.format("@AliasFor declaration on attribute [%s] in annotation [%s] points to " + "itself. Specify 'annotation' to point to a same-named attribute on a meta-annotation.", sourceAttribute.getName(), declaringClass.getName()); throw new AnnotationConfigurationException(msg); } try { this.aliasedAttribute = this.aliasedAnnotationType.getDeclaredMethod(this.aliasedAttributeName); } catch (NoSuchMethodException ex) { String msg = String.format( "Attribute [%s] in annotation [%s] is declared as an @AliasFor nonexistent attribute [%s] in annotation [%s].", this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName, this.aliasedAnnotationType.getName()); throw new AnnotationConfigurationException(msg, ex); } this.isAliasPair = (this.sourceAnnotationType == this.aliasedAnnotationType); } private void validate() { // Target annotation is not meta-present? if (!this.isAliasPair && !isAnnotationMetaPresent(this.sourceAnnotationType, this.aliasedAnnotationType)) { String msg = String.format("@AliasFor declaration on attribute [%s] in annotation [%s] declares " + "an alias for attribute [%s] in meta-annotation [%s] which is not meta-present.", this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName, this.aliasedAnnotationType.getName()); throw new AnnotationConfigurationException(msg); } if (this.isAliasPair) { AliasFor mirrorAliasFor = this.aliasedAttribute.getAnnotation(AliasFor.class); if (mirrorAliasFor == null) { String msg = String.format("Attribute [%s] in annotation [%s] must be declared as an @AliasFor [%s].", this.aliasedAttributeName, this.sourceAnnotationType.getName(), this.sourceAttributeName); throw new AnnotationConfigurationException(msg); } String mirrorAliasedAttributeName = getAliasedAttributeName(mirrorAliasFor, this.aliasedAttribute); if (!this.sourceAttributeName.equals(mirrorAliasedAttributeName)) { String msg = String.format("Attribute [%s] in annotation [%s] must be declared as an @AliasFor [%s], not [%s].", this.aliasedAttributeName, this.sourceAnnotationType.getName(), this.sourceAttributeName, mirrorAliasedAttributeName); throw new AnnotationConfigurationException(msg); } } Class returnType = this.sourceAttribute.getReturnType(); Class aliasedReturnType = this.aliasedAttribute.getReturnType(); if (returnType != aliasedReturnType) { String msg = String.format("Misconfigured aliases: attribute [%s] in annotation [%s] " + "and attribute [%s] in annotation [%s] must declare the same return type.", this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName, this.aliasedAnnotationType.getName()); throw new AnnotationConfigurationException(msg); } if (this.isAliasPair) { validateDefaultValueConfiguration(this.aliasedAttribute); } } private void validateDefaultValueConfiguration(Method aliasedAttribute) { Assert.notNull(aliasedAttribute, "aliasedAttribute must not be null"); Object defaultValue = this.sourceAttribute.getDefaultValue(); Object aliasedDefaultValue = aliasedAttribute.getDefaultValue(); if (defaultValue == null || aliasedDefaultValue == null) { String msg = String.format("Misconfigured aliases: attribute [%s] in annotation [%s] " + "and attribute [%s] in annotation [%s] must declare default values.", this.sourceAttributeName, this.sourceAnnotationType.getName(), aliasedAttribute.getName(), aliasedAttribute.getDeclaringClass().getName()); throw new AnnotationConfigurationException(msg); } if (!ObjectUtils.nullSafeEquals(defaultValue, aliasedDefaultValue)) { String msg = String.format("Misconfigured aliases: attribute [%s] in annotation [%s] " + "and attribute [%s] in annotation [%s] must declare the same default value.", this.sourceAttributeName, this.sourceAnnotationType.getName(), aliasedAttribute.getName(), aliasedAttribute.getDeclaringClass().getName()); throw new AnnotationConfigurationException(msg); } } /** * Validate this descriptor against the supplied descriptor. *(); private final Set