001/* 002 * Copyright 2002-2019 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.core.annotation; 018 019import java.lang.annotation.Annotation; 020import java.lang.reflect.AnnotatedElement; 021import java.lang.reflect.Array; 022import java.lang.reflect.InvocationHandler; 023import java.lang.reflect.InvocationTargetException; 024import java.lang.reflect.Method; 025import java.lang.reflect.Modifier; 026import java.lang.reflect.Proxy; 027import java.util.ArrayList; 028import java.util.Collections; 029import java.util.HashSet; 030import java.util.LinkedHashMap; 031import java.util.LinkedHashSet; 032import java.util.List; 033import java.util.Map; 034import java.util.Set; 035 036import org.apache.commons.logging.Log; 037import org.apache.commons.logging.LogFactory; 038 039import org.springframework.core.BridgeMethodResolver; 040import org.springframework.util.Assert; 041import org.springframework.util.ConcurrentReferenceHashMap; 042import org.springframework.util.ObjectUtils; 043import org.springframework.util.ReflectionUtils; 044import org.springframework.util.StringUtils; 045 046/** 047 * General utility methods for working with annotations, handling meta-annotations, 048 * bridge methods (which the compiler generates for generic declarations) as well 049 * as super methods (for optional <em>annotation inheritance</em>). 050 * 051 * <p>Note that most of the features of this class are not provided by the 052 * JDK's introspection facilities themselves. 053 * 054 * <p>As a general rule for runtime-retained application annotations (e.g. for 055 * transaction control, authorization, or service exposure), always use the 056 * lookup methods on this class (e.g. {@link #findAnnotation(Method, Class)} or 057 * {@link #getAnnotation(Method, Class)}) instead of the plain annotation lookup 058 * methods in the JDK. You can still explicitly choose between a <em>get</em> 059 * lookup on the given class level only ({@link #getAnnotation(Method, Class)}) 060 * and a <em>find</em> lookup in the entire inheritance hierarchy of the given 061 * method ({@link #findAnnotation(Method, Class)}). 062 * 063 * <h3>Terminology</h3> 064 * The terms <em>directly present</em>, <em>indirectly present</em>, and 065 * <em>present</em> have the same meanings as defined in the class-level 066 * javadoc for {@link AnnotatedElement} (in Java 8). 067 * 068 * <p>An annotation is <em>meta-present</em> on an element if the annotation 069 * is declared as a meta-annotation on some other annotation which is 070 * <em>present</em> on the element. Annotation {@code A} is <em>meta-present</em> 071 * on another annotation if {@code A} is either <em>directly present</em> or 072 * <em>meta-present</em> on the other annotation. 073 * 074 * <h3>Meta-annotation Support</h3> 075 * <p>Most {@code find*()} methods and some {@code get*()} methods in this class 076 * provide support for finding annotations used as meta-annotations. Consult the 077 * javadoc for each method in this class for details. For fine-grained support for 078 * meta-annotations with <em>attribute overrides</em> in <em>composed annotations</em>, 079 * consider using {@link AnnotatedElementUtils}'s more specific methods instead. 080 * 081 * <h3>Attribute Aliases</h3> 082 * <p>All public methods in this class that return annotations, arrays of 083 * annotations, or {@link AnnotationAttributes} transparently support attribute 084 * aliases configured via {@link AliasFor @AliasFor}. Consult the various 085 * {@code synthesizeAnnotation*(..)} methods for details. 086 * 087 * <h3>Search Scope</h3> 088 * <p>The search algorithms used by methods in this class stop searching for 089 * an annotation once the first annotation of the specified type has been 090 * found. As a consequence, additional annotations of the specified type will 091 * be silently ignored. 092 * 093 * @author Rob Harrop 094 * @author Juergen Hoeller 095 * @author Sam Brannen 096 * @author Mark Fisher 097 * @author Chris Beams 098 * @author Phillip Webb 099 * @since 2.0 100 * @see AliasFor 101 * @see AnnotationAttributes 102 * @see AnnotatedElementUtils 103 * @see BridgeMethodResolver 104 * @see java.lang.reflect.AnnotatedElement#getAnnotations() 105 * @see java.lang.reflect.AnnotatedElement#getAnnotation(Class) 106 * @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotations() 107 */ 108public abstract class AnnotationUtils { 109 110 /** 111 * The attribute name for annotations with a single element. 112 */ 113 public static final String VALUE = "value"; 114 115 private static final String REPEATABLE_CLASS_NAME = "java.lang.annotation.Repeatable"; 116 117 private static final Map<AnnotationCacheKey, Annotation> findAnnotationCache = 118 new ConcurrentReferenceHashMap<AnnotationCacheKey, Annotation>(256); 119 120 private static final Map<AnnotationCacheKey, Boolean> metaPresentCache = 121 new ConcurrentReferenceHashMap<AnnotationCacheKey, Boolean>(256); 122 123 private static final Map<Class<?>, Boolean> annotatedInterfaceCache = 124 new ConcurrentReferenceHashMap<Class<?>, Boolean>(256); 125 126 private static final Map<Class<? extends Annotation>, Boolean> synthesizableCache = 127 new ConcurrentReferenceHashMap<Class<? extends Annotation>, Boolean>(256); 128 129 private static final Map<Class<? extends Annotation>, Map<String, List<String>>> attributeAliasesCache = 130 new ConcurrentReferenceHashMap<Class<? extends Annotation>, Map<String, List<String>>>(256); 131 132 private static final Map<Class<? extends Annotation>, List<Method>> attributeMethodsCache = 133 new ConcurrentReferenceHashMap<Class<? extends Annotation>, List<Method>>(256); 134 135 private static final Map<Method, AliasDescriptor> aliasDescriptorCache = 136 new ConcurrentReferenceHashMap<Method, AliasDescriptor>(256); 137 138 private static transient Log logger; 139 140 141 /** 142 * Get a single {@link Annotation} of {@code annotationType} from the supplied 143 * annotation: either the given annotation itself or a direct meta-annotation 144 * thereof. 145 * <p>Note that this method supports only a single level of meta-annotations. 146 * For support for arbitrary levels of meta-annotations, use one of the 147 * {@code find*()} methods instead. 148 * @param annotation the Annotation to check 149 * @param annotationType the annotation type to look for, both locally and as a meta-annotation 150 * @return the first matching annotation, or {@code null} if not found 151 * @since 4.0 152 */ 153 @SuppressWarnings("unchecked") 154 public static <A extends Annotation> A getAnnotation(Annotation annotation, Class<A> annotationType) { 155 if (annotationType.isInstance(annotation)) { 156 return synthesizeAnnotation((A) annotation); 157 } 158 Class<? extends Annotation> annotatedElement = annotation.annotationType(); 159 try { 160 return synthesizeAnnotation(annotatedElement.getAnnotation(annotationType), annotatedElement); 161 } 162 catch (Throwable ex) { 163 handleIntrospectionFailure(annotatedElement, ex); 164 return null; 165 } 166 } 167 168 /** 169 * Get a single {@link Annotation} of {@code annotationType} from the supplied 170 * {@link AnnotatedElement}, where the annotation is either <em>present</em> or 171 * <em>meta-present</em> on the {@code AnnotatedElement}. 172 * <p>Note that this method supports only a single level of meta-annotations. 173 * For support for arbitrary levels of meta-annotations, use 174 * {@link #findAnnotation(AnnotatedElement, Class)} instead. 175 * @param annotatedElement the {@code AnnotatedElement} from which to get the annotation 176 * @param annotationType the annotation type to look for, both locally and as a meta-annotation 177 * @return the first matching annotation, or {@code null} if not found 178 * @since 3.1 179 */ 180 public static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) { 181 try { 182 A annotation = annotatedElement.getAnnotation(annotationType); 183 if (annotation == null) { 184 for (Annotation metaAnn : annotatedElement.getAnnotations()) { 185 annotation = metaAnn.annotationType().getAnnotation(annotationType); 186 if (annotation != null) { 187 break; 188 } 189 } 190 } 191 return synthesizeAnnotation(annotation, annotatedElement); 192 } 193 catch (Throwable ex) { 194 handleIntrospectionFailure(annotatedElement, ex); 195 return null; 196 } 197 } 198 199 /** 200 * Get a single {@link Annotation} of {@code annotationType} from the 201 * supplied {@link Method}, where the annotation is either <em>present</em> 202 * or <em>meta-present</em> on the method. 203 * <p>Correctly handles bridge {@link Method Methods} generated by the compiler. 204 * <p>Note that this method supports only a single level of meta-annotations. 205 * For support for arbitrary levels of meta-annotations, use 206 * {@link #findAnnotation(Method, Class)} instead. 207 * @param method the method to look for annotations on 208 * @param annotationType the annotation type to look for 209 * @return the first matching annotation, or {@code null} if not found 210 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method) 211 * @see #getAnnotation(AnnotatedElement, Class) 212 */ 213 public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType) { 214 Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method); 215 return getAnnotation((AnnotatedElement) resolvedMethod, annotationType); 216 } 217 218 /** 219 * Get all {@link Annotation Annotations} that are <em>present</em> on the 220 * supplied {@link AnnotatedElement}. 221 * <p>Meta-annotations will <em>not</em> be searched. 222 * @param annotatedElement the Method, Constructor or Field to retrieve annotations from 223 * @return the annotations found, an empty array, or {@code null} if not 224 * resolvable (e.g. because nested Class values in annotation attributes 225 * failed to resolve at runtime) 226 * @since 4.0.8 227 * @see AnnotatedElement#getAnnotations() 228 */ 229 public static Annotation[] getAnnotations(AnnotatedElement annotatedElement) { 230 try { 231 return synthesizeAnnotationArray(annotatedElement.getAnnotations(), annotatedElement); 232 } 233 catch (Throwable ex) { 234 handleIntrospectionFailure(annotatedElement, ex); 235 return null; 236 } 237 } 238 239 /** 240 * Get all {@link Annotation Annotations} that are <em>present</em> on the 241 * supplied {@link Method}. 242 * <p>Correctly handles bridge {@link Method Methods} generated by the compiler. 243 * <p>Meta-annotations will <em>not</em> be searched. 244 * @param method the Method to retrieve annotations from 245 * @return the annotations found, an empty array, or {@code null} if not 246 * resolvable (e.g. because nested Class values in annotation attributes 247 * failed to resolve at runtime) 248 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method) 249 * @see AnnotatedElement#getAnnotations() 250 */ 251 public static Annotation[] getAnnotations(Method method) { 252 try { 253 return synthesizeAnnotationArray(BridgeMethodResolver.findBridgedMethod(method).getAnnotations(), method); 254 } 255 catch (Throwable ex) { 256 handleIntrospectionFailure(method, ex); 257 return null; 258 } 259 } 260 261 /** 262 * Delegates to {@link #getRepeatableAnnotations(AnnotatedElement, Class, Class)}. 263 * @since 4.0 264 * @see #getRepeatableAnnotations(AnnotatedElement, Class, Class) 265 * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class) 266 * @deprecated As of Spring Framework 4.2, use {@code getRepeatableAnnotations()} 267 * or {@code getDeclaredRepeatableAnnotations()} instead. 268 */ 269 @Deprecated 270 public static <A extends Annotation> Set<A> getRepeatableAnnotation(Method method, 271 Class<? extends Annotation> containerAnnotationType, Class<A> annotationType) { 272 273 return getRepeatableAnnotations(method, annotationType, containerAnnotationType); 274 } 275 276 /** 277 * Delegates to {@link #getRepeatableAnnotations(AnnotatedElement, Class, Class)}. 278 * @since 4.0 279 * @see #getRepeatableAnnotations(AnnotatedElement, Class, Class) 280 * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class) 281 * @deprecated As of Spring Framework 4.2, use {@code getRepeatableAnnotations()} 282 * or {@code getDeclaredRepeatableAnnotations()} instead. 283 */ 284 @Deprecated 285 public static <A extends Annotation> Set<A> getRepeatableAnnotation(AnnotatedElement annotatedElement, 286 Class<? extends Annotation> containerAnnotationType, Class<A> annotationType) { 287 288 return getRepeatableAnnotations(annotatedElement, annotationType, containerAnnotationType); 289 } 290 291 /** 292 * Get the <em>repeatable</em> {@linkplain Annotation annotations} of 293 * {@code annotationType} from the supplied {@link AnnotatedElement}, where 294 * such annotations are either <em>present</em>, <em>indirectly present</em>, 295 * or <em>meta-present</em> on the element. 296 * <p>This method mimics the functionality of Java 8's 297 * {@link java.lang.reflect.AnnotatedElement#getAnnotationsByType(Class)} 298 * with support for automatic detection of a <em>container annotation</em> 299 * declared via @{@link java.lang.annotation.Repeatable} (when running on 300 * Java 8 or higher) and with additional support for meta-annotations. 301 * <p>Handles both single annotations and annotations nested within a 302 * <em>container annotation</em>. 303 * <p>Correctly handles <em>bridge methods</em> generated by the 304 * compiler if the supplied element is a {@link Method}. 305 * <p>Meta-annotations will be searched if the annotation is not 306 * <em>present</em> on the supplied element. 307 * @param annotatedElement the element to look for annotations on 308 * @param annotationType the annotation type to look for 309 * @return the annotations found or an empty set (never {@code null}) 310 * @since 4.2 311 * @see #getRepeatableAnnotations(AnnotatedElement, Class, Class) 312 * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class) 313 * @see AnnotatedElementUtils#getMergedRepeatableAnnotations(AnnotatedElement, Class) 314 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod 315 * @see java.lang.annotation.Repeatable 316 * @see java.lang.reflect.AnnotatedElement#getAnnotationsByType 317 */ 318 public static <A extends Annotation> Set<A> getRepeatableAnnotations(AnnotatedElement annotatedElement, 319 Class<A> annotationType) { 320 321 return getRepeatableAnnotations(annotatedElement, annotationType, null); 322 } 323 324 /** 325 * Get the <em>repeatable</em> {@linkplain Annotation annotations} of 326 * {@code annotationType} from the supplied {@link AnnotatedElement}, where 327 * such annotations are either <em>present</em>, <em>indirectly present</em>, 328 * or <em>meta-present</em> on the element. 329 * <p>This method mimics the functionality of Java 8's 330 * {@link java.lang.reflect.AnnotatedElement#getAnnotationsByType(Class)} 331 * with additional support for meta-annotations. 332 * <p>Handles both single annotations and annotations nested within a 333 * <em>container annotation</em>. 334 * <p>Correctly handles <em>bridge methods</em> generated by the 335 * compiler if the supplied element is a {@link Method}. 336 * <p>Meta-annotations will be searched if the annotation is not 337 * <em>present</em> on the supplied element. 338 * @param annotatedElement the element to look for annotations on 339 * @param annotationType the annotation type to look for 340 * @param containerAnnotationType the type of the container that holds 341 * the annotations; may be {@code null} if a container is not supported 342 * or if it should be looked up via @{@link java.lang.annotation.Repeatable} 343 * when running on Java 8 or higher 344 * @return the annotations found or an empty set (never {@code null}) 345 * @since 4.2 346 * @see #getRepeatableAnnotations(AnnotatedElement, Class) 347 * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class) 348 * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class) 349 * @see AnnotatedElementUtils#getMergedRepeatableAnnotations(AnnotatedElement, Class, Class) 350 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod 351 * @see java.lang.annotation.Repeatable 352 * @see java.lang.reflect.AnnotatedElement#getAnnotationsByType 353 */ 354 public static <A extends Annotation> Set<A> getRepeatableAnnotations(AnnotatedElement annotatedElement, 355 Class<A> annotationType, Class<? extends Annotation> containerAnnotationType) { 356 357 Set<A> annotations = getDeclaredRepeatableAnnotations(annotatedElement, annotationType, containerAnnotationType); 358 if (!annotations.isEmpty()) { 359 return annotations; 360 } 361 362 if (annotatedElement instanceof Class) { 363 Class<?> superclass = ((Class<?>) annotatedElement).getSuperclass(); 364 if (superclass != null && Object.class != superclass) { 365 return getRepeatableAnnotations(superclass, annotationType, containerAnnotationType); 366 } 367 } 368 369 return getRepeatableAnnotations(annotatedElement, annotationType, containerAnnotationType, false); 370 } 371 372 /** 373 * Get the declared <em>repeatable</em> {@linkplain Annotation annotations} 374 * of {@code annotationType} from the supplied {@link AnnotatedElement}, 375 * where such annotations are either <em>directly present</em>, 376 * <em>indirectly present</em>, or <em>meta-present</em> on the element. 377 * <p>This method mimics the functionality of Java 8's 378 * {@link java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType(Class)} 379 * with support for automatic detection of a <em>container annotation</em> 380 * declared via @{@link java.lang.annotation.Repeatable} (when running on 381 * Java 8 or higher) and with additional support for meta-annotations. 382 * <p>Handles both single annotations and annotations nested within a 383 * <em>container annotation</em>. 384 * <p>Correctly handles <em>bridge methods</em> generated by the 385 * compiler if the supplied element is a {@link Method}. 386 * <p>Meta-annotations will be searched if the annotation is not 387 * <em>present</em> on the supplied element. 388 * @param annotatedElement the element to look for annotations on 389 * @param annotationType the annotation type to look for 390 * @return the annotations found or an empty set (never {@code null}) 391 * @since 4.2 392 * @see #getRepeatableAnnotations(AnnotatedElement, Class) 393 * @see #getRepeatableAnnotations(AnnotatedElement, Class, Class) 394 * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class) 395 * @see AnnotatedElementUtils#getMergedRepeatableAnnotations(AnnotatedElement, Class) 396 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod 397 * @see java.lang.annotation.Repeatable 398 * @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType 399 */ 400 public static <A extends Annotation> Set<A> getDeclaredRepeatableAnnotations(AnnotatedElement annotatedElement, 401 Class<A> annotationType) { 402 403 return getDeclaredRepeatableAnnotations(annotatedElement, annotationType, null); 404 } 405 406 /** 407 * Get the declared <em>repeatable</em> {@linkplain Annotation annotations} 408 * of {@code annotationType} from the supplied {@link AnnotatedElement}, 409 * where such annotations are either <em>directly present</em>, 410 * <em>indirectly present</em>, or <em>meta-present</em> on the element. 411 * <p>This method mimics the functionality of Java 8's 412 * {@link java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType(Class)} 413 * with additional support for meta-annotations. 414 * <p>Handles both single annotations and annotations nested within a 415 * <em>container annotation</em>. 416 * <p>Correctly handles <em>bridge methods</em> generated by the 417 * compiler if the supplied element is a {@link Method}. 418 * <p>Meta-annotations will be searched if the annotation is not 419 * <em>present</em> on the supplied element. 420 * @param annotatedElement the element to look for annotations on 421 * @param annotationType the annotation type to look for 422 * @param containerAnnotationType the type of the container that holds 423 * the annotations; may be {@code null} if a container is not supported 424 * or if it should be looked up via @{@link java.lang.annotation.Repeatable} 425 * when running on Java 8 or higher 426 * @return the annotations found or an empty set (never {@code null}) 427 * @since 4.2 428 * @see #getRepeatableAnnotations(AnnotatedElement, Class) 429 * @see #getRepeatableAnnotations(AnnotatedElement, Class, Class) 430 * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class) 431 * @see AnnotatedElementUtils#getMergedRepeatableAnnotations(AnnotatedElement, Class, Class) 432 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod 433 * @see java.lang.annotation.Repeatable 434 * @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType 435 */ 436 public static <A extends Annotation> Set<A> getDeclaredRepeatableAnnotations(AnnotatedElement annotatedElement, 437 Class<A> annotationType, Class<? extends Annotation> containerAnnotationType) { 438 439 return getRepeatableAnnotations(annotatedElement, annotationType, containerAnnotationType, true); 440 } 441 442 /** 443 * Perform the actual work for {@link #getRepeatableAnnotations(AnnotatedElement, Class, Class)} 444 * and {@link #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class)}. 445 * <p>Correctly handles <em>bridge methods</em> generated by the 446 * compiler if the supplied element is a {@link Method}. 447 * <p>Meta-annotations will be searched if the annotation is not 448 * <em>present</em> on the supplied element. 449 * @param annotatedElement the element to look for annotations on 450 * @param annotationType the annotation type to look for 451 * @param containerAnnotationType the type of the container that holds 452 * the annotations; may be {@code null} if a container is not supported 453 * or if it should be looked up via @{@link java.lang.annotation.Repeatable} 454 * when running on Java 8 or higher 455 * @param declaredMode {@code true} if only declared annotations (i.e., 456 * directly or indirectly present) should be considered 457 * @return the annotations found or an empty set (never {@code null}) 458 * @since 4.2 459 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod 460 * @see java.lang.annotation.Repeatable 461 */ 462 private static <A extends Annotation> Set<A> getRepeatableAnnotations(AnnotatedElement annotatedElement, 463 Class<A> annotationType, Class<? extends Annotation> containerAnnotationType, boolean declaredMode) { 464 465 Assert.notNull(annotatedElement, "AnnotatedElement must not be null"); 466 Assert.notNull(annotationType, "Annotation type must not be null"); 467 468 try { 469 if (annotatedElement instanceof Method) { 470 annotatedElement = BridgeMethodResolver.findBridgedMethod((Method) annotatedElement); 471 } 472 return new AnnotationCollector<A>(annotationType, containerAnnotationType, declaredMode).getResult(annotatedElement); 473 } 474 catch (Throwable ex) { 475 handleIntrospectionFailure(annotatedElement, ex); 476 return Collections.emptySet(); 477 } 478 } 479 480 /** 481 * Find a single {@link Annotation} of {@code annotationType} on the 482 * supplied {@link AnnotatedElement}. 483 * <p>Meta-annotations will be searched if the annotation is not 484 * <em>directly present</em> on the supplied element. 485 * <p><strong>Warning</strong>: this method operates generically on 486 * annotated elements. In other words, this method does not execute 487 * specialized search algorithms for classes or methods. If you require 488 * the more specific semantics of {@link #findAnnotation(Class, Class)} 489 * or {@link #findAnnotation(Method, Class)}, invoke one of those methods 490 * instead. 491 * @param annotatedElement the {@code AnnotatedElement} on which to find the annotation 492 * @param annotationType the annotation type to look for, both locally and as a meta-annotation 493 * @return the first matching annotation, or {@code null} if not found 494 * @since 4.2 495 */ 496 public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) { 497 Assert.notNull(annotatedElement, "AnnotatedElement must not be null"); 498 if (annotationType == null) { 499 return null; 500 } 501 502 // Do NOT store result in the findAnnotationCache since doing so could break 503 // findAnnotation(Class, Class) and findAnnotation(Method, Class). 504 A ann = findAnnotation(annotatedElement, annotationType, new HashSet<Annotation>()); 505 return synthesizeAnnotation(ann, annotatedElement); 506 } 507 508 /** 509 * Perform the search algorithm for {@link #findAnnotation(AnnotatedElement, Class)} 510 * avoiding endless recursion by tracking which annotations have already 511 * been <em>visited</em>. 512 * @param annotatedElement the {@code AnnotatedElement} on which to find the annotation 513 * @param annotationType the annotation type to look for, both locally and as a meta-annotation 514 * @param visited the set of annotations that have already been visited 515 * @return the first matching annotation, or {@code null} if not found 516 * @since 4.2 517 */ 518 @SuppressWarnings("unchecked") 519 private static <A extends Annotation> A findAnnotation( 520 AnnotatedElement annotatedElement, Class<A> annotationType, Set<Annotation> visited) { 521 try { 522 Annotation[] anns = annotatedElement.getDeclaredAnnotations(); 523 for (Annotation ann : anns) { 524 if (ann.annotationType() == annotationType) { 525 return (A) ann; 526 } 527 } 528 for (Annotation ann : anns) { 529 if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) { 530 A annotation = findAnnotation((AnnotatedElement) ann.annotationType(), annotationType, visited); 531 if (annotation != null) { 532 return annotation; 533 } 534 } 535 } 536 } 537 catch (Throwable ex) { 538 handleIntrospectionFailure(annotatedElement, ex); 539 } 540 return null; 541 } 542 543 /** 544 * Find a single {@link Annotation} of {@code annotationType} on the supplied 545 * {@link Method}, traversing its super methods (i.e. from superclasses and 546 * interfaces) if the annotation is not <em>directly present</em> on the given 547 * method itself. 548 * <p>Correctly handles bridge {@link Method Methods} generated by the compiler. 549 * <p>Meta-annotations will be searched if the annotation is not 550 * <em>directly present</em> on the method. 551 * <p>Annotations on methods are not inherited by default, so we need to handle 552 * this explicitly. 553 * @param method the method to look for annotations on 554 * @param annotationType the annotation type to look for 555 * @return the first matching annotation, or {@code null} if not found 556 * @see #getAnnotation(Method, Class) 557 */ 558 @SuppressWarnings("unchecked") 559 public static <A extends Annotation> A findAnnotation(Method method, Class<A> annotationType) { 560 Assert.notNull(method, "Method must not be null"); 561 if (annotationType == null) { 562 return null; 563 } 564 565 AnnotationCacheKey cacheKey = new AnnotationCacheKey(method, annotationType); 566 A result = (A) findAnnotationCache.get(cacheKey); 567 568 if (result == null) { 569 Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method); 570 result = findAnnotation((AnnotatedElement) resolvedMethod, annotationType); 571 if (result == null) { 572 result = searchOnInterfaces(method, annotationType, method.getDeclaringClass().getInterfaces()); 573 } 574 575 Class<?> clazz = method.getDeclaringClass(); 576 while (result == null) { 577 clazz = clazz.getSuperclass(); 578 if (clazz == null || Object.class == clazz) { 579 break; 580 } 581 try { 582 Method equivalentMethod = clazz.getDeclaredMethod(method.getName(), method.getParameterTypes()); 583 Method resolvedEquivalentMethod = BridgeMethodResolver.findBridgedMethod(equivalentMethod); 584 result = findAnnotation((AnnotatedElement) resolvedEquivalentMethod, annotationType); 585 } 586 catch (NoSuchMethodException ex) { 587 // No equivalent method found 588 } 589 if (result == null) { 590 result = searchOnInterfaces(method, annotationType, clazz.getInterfaces()); 591 } 592 } 593 594 if (result != null) { 595 result = synthesizeAnnotation(result, method); 596 findAnnotationCache.put(cacheKey, result); 597 } 598 } 599 600 return result; 601 } 602 603 private static <A extends Annotation> A searchOnInterfaces(Method method, Class<A> annotationType, Class<?>... ifcs) { 604 A annotation = null; 605 for (Class<?> ifc : ifcs) { 606 if (isInterfaceWithAnnotatedMethods(ifc)) { 607 try { 608 Method equivalentMethod = ifc.getMethod(method.getName(), method.getParameterTypes()); 609 annotation = getAnnotation(equivalentMethod, annotationType); 610 } 611 catch (NoSuchMethodException ex) { 612 // Skip this interface - it doesn't have the method... 613 } 614 if (annotation != null) { 615 break; 616 } 617 } 618 } 619 return annotation; 620 } 621 622 static boolean isInterfaceWithAnnotatedMethods(Class<?> ifc) { 623 Boolean found = annotatedInterfaceCache.get(ifc); 624 if (found != null) { 625 return found; 626 } 627 found = Boolean.FALSE; 628 for (Method ifcMethod : ifc.getMethods()) { 629 try { 630 if (ifcMethod.getAnnotations().length > 0) { 631 found = Boolean.TRUE; 632 break; 633 } 634 } 635 catch (Throwable ex) { 636 handleIntrospectionFailure(ifcMethod, ex); 637 } 638 } 639 annotatedInterfaceCache.put(ifc, found); 640 return found; 641 } 642 643 /** 644 * Find a single {@link Annotation} of {@code annotationType} on the 645 * supplied {@link Class}, traversing its interfaces, annotations, and 646 * superclasses if the annotation is not <em>directly present</em> on 647 * the given class itself. 648 * <p>This method explicitly handles class-level annotations which are not 649 * declared as {@link java.lang.annotation.Inherited inherited} <em>as well 650 * as meta-annotations and annotations on interfaces</em>. 651 * <p>The algorithm operates as follows: 652 * <ol> 653 * <li>Search for the annotation on the given class and return it if found. 654 * <li>Recursively search through all annotations that the given class declares. 655 * <li>Recursively search through all interfaces that the given class declares. 656 * <li>Recursively search through the superclass hierarchy of the given class. 657 * </ol> 658 * <p>Note: in this context, the term <em>recursively</em> means that the search 659 * process continues by returning to step #1 with the current interface, 660 * annotation, or superclass as the class to look for annotations on. 661 * @param clazz the class to look for annotations on 662 * @param annotationType the type of annotation to look for 663 * @return the first matching annotation, or {@code null} if not found 664 */ 665 public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType) { 666 return findAnnotation(clazz, annotationType, true); 667 } 668 669 /** 670 * Perform the actual work for {@link #findAnnotation(AnnotatedElement, Class)}, 671 * honoring the {@code synthesize} flag. 672 * @param clazz the class to look for annotations on 673 * @param annotationType the type of annotation to look for 674 * @param synthesize {@code true} if the result should be 675 * {@linkplain #synthesizeAnnotation(Annotation) synthesized} 676 * @return the first matching annotation, or {@code null} if not found 677 * @since 4.2.1 678 */ 679 @SuppressWarnings("unchecked") 680 private static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType, boolean synthesize) { 681 Assert.notNull(clazz, "Class must not be null"); 682 if (annotationType == null) { 683 return null; 684 } 685 686 AnnotationCacheKey cacheKey = new AnnotationCacheKey(clazz, annotationType); 687 A result = (A) findAnnotationCache.get(cacheKey); 688 if (result == null) { 689 result = findAnnotation(clazz, annotationType, new HashSet<Annotation>()); 690 if (result != null && synthesize) { 691 result = synthesizeAnnotation(result, clazz); 692 findAnnotationCache.put(cacheKey, result); 693 } 694 } 695 return result; 696 } 697 698 /** 699 * Perform the search algorithm for {@link #findAnnotation(Class, Class)}, 700 * avoiding endless recursion by tracking which annotations have already 701 * been <em>visited</em>. 702 * @param clazz the class to look for annotations on 703 * @param annotationType the type of annotation to look for 704 * @param visited the set of annotations that have already been visited 705 * @return the first matching annotation, or {@code null} if not found 706 */ 707 @SuppressWarnings("unchecked") 708 private static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType, Set<Annotation> visited) { 709 try { 710 Annotation[] anns = clazz.getDeclaredAnnotations(); 711 for (Annotation ann : anns) { 712 if (ann.annotationType() == annotationType) { 713 return (A) ann; 714 } 715 } 716 for (Annotation ann : anns) { 717 if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) { 718 A annotation = findAnnotation(ann.annotationType(), annotationType, visited); 719 if (annotation != null) { 720 return annotation; 721 } 722 } 723 } 724 } 725 catch (Throwable ex) { 726 handleIntrospectionFailure(clazz, ex); 727 return null; 728 } 729 730 for (Class<?> ifc : clazz.getInterfaces()) { 731 A annotation = findAnnotation(ifc, annotationType, visited); 732 if (annotation != null) { 733 return annotation; 734 } 735 } 736 737 Class<?> superclass = clazz.getSuperclass(); 738 if (superclass == null || Object.class == superclass) { 739 return null; 740 } 741 return findAnnotation(superclass, annotationType, visited); 742 } 743 744 /** 745 * Find the first {@link Class} in the inheritance hierarchy of the 746 * specified {@code clazz} (including the specified {@code clazz} itself) 747 * on which an annotation of the specified {@code annotationType} is 748 * <em>directly present</em>. 749 * <p>If the supplied {@code clazz} is an interface, only the interface 750 * itself will be checked; the inheritance hierarchy for interfaces will 751 * not be traversed. 752 * <p>Meta-annotations will <em>not</em> be searched. 753 * <p>The standard {@link Class} API does not provide a mechanism for 754 * determining which class in an inheritance hierarchy actually declares 755 * an {@link Annotation}, so we need to handle this explicitly. 756 * @param annotationType the annotation type to look for 757 * @param clazz the class to check for the annotation on (may be {@code null}) 758 * @return the first {@link Class} in the inheritance hierarchy that 759 * declares an annotation of the specified {@code annotationType}, or 760 * {@code null} if not found 761 * @see Class#isAnnotationPresent(Class) 762 * @see Class#getDeclaredAnnotations() 763 * @see #findAnnotationDeclaringClassForTypes(List, Class) 764 * @see #isAnnotationDeclaredLocally(Class, Class) 765 */ 766 public static Class<?> findAnnotationDeclaringClass(Class<? extends Annotation> annotationType, Class<?> clazz) { 767 Assert.notNull(annotationType, "Annotation type must not be null"); 768 if (clazz == null || Object.class == clazz) { 769 return null; 770 } 771 if (isAnnotationDeclaredLocally(annotationType, clazz)) { 772 return clazz; 773 } 774 return findAnnotationDeclaringClass(annotationType, clazz.getSuperclass()); 775 } 776 777 /** 778 * Find the first {@link Class} in the inheritance hierarchy of the 779 * specified {@code clazz} (including the specified {@code clazz} itself) 780 * on which at least one of the specified {@code annotationTypes} is 781 * <em>directly present</em>. 782 * <p>If the supplied {@code clazz} is an interface, only the interface 783 * itself will be checked; the inheritance hierarchy for interfaces will 784 * not be traversed. 785 * <p>Meta-annotations will <em>not</em> be searched. 786 * <p>The standard {@link Class} API does not provide a mechanism for 787 * determining which class in an inheritance hierarchy actually declares 788 * one of several candidate {@linkplain Annotation annotations}, so we 789 * need to handle this explicitly. 790 * @param annotationTypes the annotation types to look for 791 * @param clazz the class to check for the annotations on, or {@code null} 792 * @return the first {@link Class} in the inheritance hierarchy that 793 * declares an annotation of at least one of the specified 794 * {@code annotationTypes}, or {@code null} if not found 795 * @since 3.2.2 796 * @see Class#isAnnotationPresent(Class) 797 * @see Class#getDeclaredAnnotations() 798 * @see #findAnnotationDeclaringClass(Class, Class) 799 * @see #isAnnotationDeclaredLocally(Class, Class) 800 */ 801 public static Class<?> findAnnotationDeclaringClassForTypes(List<Class<? extends Annotation>> annotationTypes, Class<?> clazz) { 802 Assert.notEmpty(annotationTypes, "List of annotation types must not be empty"); 803 if (clazz == null || Object.class == clazz) { 804 return null; 805 } 806 for (Class<? extends Annotation> annotationType : annotationTypes) { 807 if (isAnnotationDeclaredLocally(annotationType, clazz)) { 808 return clazz; 809 } 810 } 811 return findAnnotationDeclaringClassForTypes(annotationTypes, clazz.getSuperclass()); 812 } 813 814 /** 815 * Determine whether an annotation of the specified {@code annotationType} 816 * is declared locally (i.e., <em>directly present</em>) on the supplied 817 * {@code clazz}. 818 * <p>The supplied {@link Class} may represent any type. 819 * <p>Meta-annotations will <em>not</em> be searched. 820 * <p>Note: This method does <strong>not</strong> determine if the annotation 821 * is {@linkplain java.lang.annotation.Inherited inherited}. For greater 822 * clarity regarding inherited annotations, consider using 823 * {@link #isAnnotationInherited(Class, Class)} instead. 824 * @param annotationType the annotation type to look for 825 * @param clazz the class to check for the annotation on 826 * @return {@code true} if an annotation of the specified {@code annotationType} 827 * is <em>directly present</em> 828 * @see java.lang.Class#getDeclaredAnnotations() 829 * @see java.lang.Class#getDeclaredAnnotation(Class) 830 * @see #isAnnotationInherited(Class, Class) 831 */ 832 public static boolean isAnnotationDeclaredLocally(Class<? extends Annotation> annotationType, Class<?> clazz) { 833 Assert.notNull(annotationType, "Annotation type must not be null"); 834 Assert.notNull(clazz, "Class must not be null"); 835 try { 836 for (Annotation ann : clazz.getDeclaredAnnotations()) { 837 if (ann.annotationType() == annotationType) { 838 return true; 839 } 840 } 841 } 842 catch (Throwable ex) { 843 handleIntrospectionFailure(clazz, ex); 844 } 845 return false; 846 } 847 848 /** 849 * Determine whether an annotation of the specified {@code annotationType} 850 * is <em>present</em> on the supplied {@code clazz} and is 851 * {@linkplain java.lang.annotation.Inherited inherited} (i.e., not 852 * <em>directly present</em>). 853 * <p>Meta-annotations will <em>not</em> be searched. 854 * <p>If the supplied {@code clazz} is an interface, only the interface 855 * itself will be checked. In accordance with standard meta-annotation 856 * semantics in Java, the inheritance hierarchy for interfaces will not 857 * be traversed. See the {@linkplain java.lang.annotation.Inherited javadoc} 858 * for the {@code @Inherited} meta-annotation for further details regarding 859 * annotation inheritance. 860 * @param annotationType the annotation type to look for 861 * @param clazz the class to check for the annotation on 862 * @return {@code true} if an annotation of the specified {@code annotationType} 863 * is <em>present</em> and <em>inherited</em> 864 * @see Class#isAnnotationPresent(Class) 865 * @see #isAnnotationDeclaredLocally(Class, Class) 866 */ 867 public static boolean isAnnotationInherited(Class<? extends Annotation> annotationType, Class<?> clazz) { 868 Assert.notNull(annotationType, "Annotation type must not be null"); 869 Assert.notNull(clazz, "Class must not be null"); 870 return (clazz.isAnnotationPresent(annotationType) && !isAnnotationDeclaredLocally(annotationType, clazz)); 871 } 872 873 /** 874 * Determine if an annotation of type {@code metaAnnotationType} is 875 * <em>meta-present</em> on the supplied {@code annotationType}. 876 * @param annotationType the annotation type to search on 877 * @param metaAnnotationType the type of meta-annotation to search for 878 * @return {@code true} if such an annotation is meta-present 879 * @since 4.2.1 880 */ 881 public static boolean isAnnotationMetaPresent(Class<? extends Annotation> annotationType, 882 Class<? extends Annotation> metaAnnotationType) { 883 884 Assert.notNull(annotationType, "Annotation type must not be null"); 885 if (metaAnnotationType == null) { 886 return false; 887 } 888 889 AnnotationCacheKey cacheKey = new AnnotationCacheKey(annotationType, metaAnnotationType); 890 Boolean metaPresent = metaPresentCache.get(cacheKey); 891 if (metaPresent != null) { 892 return metaPresent; 893 } 894 metaPresent = Boolean.FALSE; 895 if (findAnnotation(annotationType, metaAnnotationType, false) != null) { 896 metaPresent = Boolean.TRUE; 897 } 898 metaPresentCache.put(cacheKey, metaPresent); 899 return metaPresent; 900 } 901 902 /** 903 * Determine if the supplied {@link Annotation} is defined in the core JDK 904 * {@code java.lang.annotation} package. 905 * @param annotation the annotation to check 906 * @return {@code true} if the annotation is in the {@code java.lang.annotation} package 907 */ 908 public static boolean isInJavaLangAnnotationPackage(Annotation annotation) { 909 return (annotation != null && isInJavaLangAnnotationPackage(annotation.annotationType())); 910 } 911 912 /** 913 * Determine if the {@link Annotation} with the supplied name is defined 914 * in the core JDK {@code java.lang.annotation} package. 915 * @param annotationType the annotation type to check 916 * @return {@code true} if the annotation is in the {@code java.lang.annotation} package 917 * @since 4.3.8 918 */ 919 static boolean isInJavaLangAnnotationPackage(Class<? extends Annotation> annotationType) { 920 return (annotationType != null && isInJavaLangAnnotationPackage(annotationType.getName())); 921 } 922 923 /** 924 * Determine if the {@link Annotation} with the supplied name is defined 925 * in the core JDK {@code java.lang.annotation} package. 926 * @param annotationType the name of the annotation type to check 927 * @return {@code true} if the annotation is in the {@code java.lang.annotation} package 928 * @since 4.2 929 */ 930 public static boolean isInJavaLangAnnotationPackage(String annotationType) { 931 return (annotationType != null && annotationType.startsWith("java.lang.annotation")); 932 } 933 934 /** 935 * Check the declared attributes of the given annotation, in particular covering 936 * Google App Engine's late arrival of {@code TypeNotPresentExceptionProxy} for 937 * {@code Class} values (instead of early {@code Class.getAnnotations() failure}. 938 * <p>This method not failing indicates that {@link #getAnnotationAttributes(Annotation)} 939 * won't failure either (when attempted later on). 940 * @param annotation the annotation to validate 941 * @throws IllegalStateException if a declared {@code Class} attribute could not be read 942 * @since 4.3.15 943 * @see Class#getAnnotations() 944 * @see #getAnnotationAttributes(Annotation) 945 */ 946 public static void validateAnnotation(Annotation annotation) { 947 for (Method method : getAttributeMethods(annotation.annotationType())) { 948 Class<?> returnType = method.getReturnType(); 949 if (returnType == Class.class || returnType == Class[].class) { 950 try { 951 method.invoke(annotation); 952 } 953 catch (Throwable ex) { 954 throw new IllegalStateException("Could not obtain annotation attribute value for " + method, ex); 955 } 956 } 957 } 958 } 959 960 /** 961 * Retrieve the given annotation's attributes as a {@link Map}, preserving all 962 * attribute types. 963 * <p>Equivalent to calling {@link #getAnnotationAttributes(Annotation, boolean, boolean)} 964 * with the {@code classValuesAsString} and {@code nestedAnnotationsAsMap} parameters 965 * set to {@code false}. 966 * <p>Note: This method actually returns an {@link AnnotationAttributes} instance. 967 * However, the {@code Map} signature has been preserved for binary compatibility. 968 * @param annotation the annotation to retrieve the attributes for 969 * @return the Map of annotation attributes, with attribute names as keys and 970 * corresponding attribute values as values (never {@code null}) 971 * @see #getAnnotationAttributes(AnnotatedElement, Annotation) 972 * @see #getAnnotationAttributes(Annotation, boolean, boolean) 973 * @see #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean) 974 */ 975 public static Map<String, Object> getAnnotationAttributes(Annotation annotation) { 976 return getAnnotationAttributes(null, annotation); 977 } 978 979 /** 980 * Retrieve the given annotation's attributes as a {@link Map}. 981 * <p>Equivalent to calling {@link #getAnnotationAttributes(Annotation, boolean, boolean)} 982 * with the {@code nestedAnnotationsAsMap} parameter set to {@code false}. 983 * <p>Note: This method actually returns an {@link AnnotationAttributes} instance. 984 * However, the {@code Map} signature has been preserved for binary compatibility. 985 * @param annotation the annotation to retrieve the attributes for 986 * @param classValuesAsString whether to convert Class references into Strings (for 987 * compatibility with {@link org.springframework.core.type.AnnotationMetadata}) 988 * or to preserve them as Class references 989 * @return the Map of annotation attributes, with attribute names as keys and 990 * corresponding attribute values as values (never {@code null}) 991 * @see #getAnnotationAttributes(Annotation, boolean, boolean) 992 */ 993 public static Map<String, Object> getAnnotationAttributes(Annotation annotation, boolean classValuesAsString) { 994 return getAnnotationAttributes(annotation, classValuesAsString, false); 995 } 996 997 /** 998 * Retrieve the given annotation's attributes as an {@link AnnotationAttributes} map. 999 * <p>This method provides fully recursive annotation reading capabilities on par with 1000 * the reflection-based {@link org.springframework.core.type.StandardAnnotationMetadata}. 1001 * @param annotation the annotation to retrieve the attributes for 1002 * @param classValuesAsString whether to convert Class references into Strings (for 1003 * compatibility with {@link org.springframework.core.type.AnnotationMetadata}) 1004 * or to preserve them as Class references 1005 * @param nestedAnnotationsAsMap whether to convert nested annotations into 1006 * {@link AnnotationAttributes} maps (for compatibility with 1007 * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as 1008 * {@code Annotation} instances 1009 * @return the annotation attributes (a specialized Map) with attribute names as keys 1010 * and corresponding attribute values as values (never {@code null}) 1011 * @since 3.1.1 1012 */ 1013 public static AnnotationAttributes getAnnotationAttributes(Annotation annotation, boolean classValuesAsString, 1014 boolean nestedAnnotationsAsMap) { 1015 1016 return getAnnotationAttributes(null, annotation, classValuesAsString, nestedAnnotationsAsMap); 1017 } 1018 1019 /** 1020 * Retrieve the given annotation's attributes as an {@link AnnotationAttributes} map. 1021 * <p>Equivalent to calling {@link #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean)} 1022 * with the {@code classValuesAsString} and {@code nestedAnnotationsAsMap} parameters 1023 * set to {@code false}. 1024 * @param annotatedElement the element that is annotated with the supplied annotation; 1025 * may be {@code null} if unknown 1026 * @param annotation the annotation to retrieve the attributes for 1027 * @return the annotation attributes (a specialized Map) with attribute names as keys 1028 * and corresponding attribute values as values (never {@code null}) 1029 * @since 4.2 1030 * @see #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean) 1031 */ 1032 public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement annotatedElement, Annotation annotation) { 1033 return getAnnotationAttributes(annotatedElement, annotation, false, false); 1034 } 1035 1036 /** 1037 * Retrieve the given annotation's attributes as an {@link AnnotationAttributes} map. 1038 * <p>This method provides fully recursive annotation reading capabilities on par with 1039 * the reflection-based {@link org.springframework.core.type.StandardAnnotationMetadata}. 1040 * @param annotatedElement the element that is annotated with the supplied annotation; 1041 * may be {@code null} if unknown 1042 * @param annotation the annotation to retrieve the attributes for 1043 * @param classValuesAsString whether to convert Class references into Strings (for 1044 * compatibility with {@link org.springframework.core.type.AnnotationMetadata}) 1045 * or to preserve them as Class references 1046 * @param nestedAnnotationsAsMap whether to convert nested annotations into 1047 * {@link AnnotationAttributes} maps (for compatibility with 1048 * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as 1049 * {@code Annotation} instances 1050 * @return the annotation attributes (a specialized Map) with attribute names as keys 1051 * and corresponding attribute values as values (never {@code null}) 1052 * @since 4.2 1053 */ 1054 public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement annotatedElement, 1055 Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { 1056 1057 return getAnnotationAttributes( 1058 (Object) annotatedElement, annotation, classValuesAsString, nestedAnnotationsAsMap); 1059 } 1060 1061 private static AnnotationAttributes getAnnotationAttributes(Object annotatedElement, 1062 Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { 1063 1064 AnnotationAttributes attributes = 1065 retrieveAnnotationAttributes(annotatedElement, annotation, classValuesAsString, nestedAnnotationsAsMap); 1066 postProcessAnnotationAttributes(annotatedElement, attributes, classValuesAsString, nestedAnnotationsAsMap); 1067 return attributes; 1068 } 1069 1070 /** 1071 * Retrieve the given annotation's attributes as an {@link AnnotationAttributes} map. 1072 * <p>This method provides fully recursive annotation reading capabilities on par with 1073 * the reflection-based {@link org.springframework.core.type.StandardAnnotationMetadata}. 1074 * <p><strong>NOTE</strong>: This variant of {@code getAnnotationAttributes()} is 1075 * only intended for use within the framework. The following special rules apply: 1076 * <ol> 1077 * <li>Default values will be replaced with default value placeholders.</li> 1078 * <li>The resulting, merged annotation attributes should eventually be 1079 * {@linkplain #postProcessAnnotationAttributes post-processed} in order to 1080 * ensure that placeholders have been replaced by actual default values and 1081 * in order to enforce {@code @AliasFor} semantics.</li> 1082 * </ol> 1083 * @param annotatedElement the element that is annotated with the supplied annotation; 1084 * may be {@code null} if unknown 1085 * @param annotation the annotation to retrieve the attributes for 1086 * @param classValuesAsString whether to convert Class references into Strings (for 1087 * compatibility with {@link org.springframework.core.type.AnnotationMetadata}) 1088 * or to preserve them as Class references 1089 * @param nestedAnnotationsAsMap whether to convert nested annotations into 1090 * {@link AnnotationAttributes} maps (for compatibility with 1091 * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as 1092 * {@code Annotation} instances 1093 * @return the annotation attributes (a specialized Map) with attribute names as keys 1094 * and corresponding attribute values as values (never {@code null}) 1095 * @since 4.2 1096 * @see #postProcessAnnotationAttributes 1097 */ 1098 static AnnotationAttributes retrieveAnnotationAttributes(Object annotatedElement, Annotation annotation, 1099 boolean classValuesAsString, boolean nestedAnnotationsAsMap) { 1100 1101 Class<? extends Annotation> annotationType = annotation.annotationType(); 1102 AnnotationAttributes attributes = new AnnotationAttributes(annotationType); 1103 1104 for (Method method : getAttributeMethods(annotationType)) { 1105 try { 1106 Object attributeValue = method.invoke(annotation); 1107 Object defaultValue = method.getDefaultValue(); 1108 if (defaultValue != null && ObjectUtils.nullSafeEquals(attributeValue, defaultValue)) { 1109 attributeValue = new DefaultValueHolder(defaultValue); 1110 } 1111 attributes.put(method.getName(), 1112 adaptValue(annotatedElement, attributeValue, classValuesAsString, nestedAnnotationsAsMap)); 1113 } 1114 catch (Throwable ex) { 1115 if (ex instanceof InvocationTargetException) { 1116 Throwable targetException = ((InvocationTargetException) ex).getTargetException(); 1117 rethrowAnnotationConfigurationException(targetException); 1118 } 1119 throw new IllegalStateException("Could not obtain annotation attribute value for " + method, ex); 1120 } 1121 } 1122 1123 return attributes; 1124 } 1125 1126 /** 1127 * Adapt the given value according to the given class and nested annotation settings. 1128 * <p>Nested annotations will be 1129 * {@linkplain #synthesizeAnnotation(Annotation, AnnotatedElement) synthesized}. 1130 * @param annotatedElement the element that is annotated, used for contextual 1131 * logging; may be {@code null} if unknown 1132 * @param value the annotation attribute value 1133 * @param classValuesAsString whether to convert Class references into Strings (for 1134 * compatibility with {@link org.springframework.core.type.AnnotationMetadata}) 1135 * or to preserve them as Class references 1136 * @param nestedAnnotationsAsMap whether to convert nested annotations into 1137 * {@link AnnotationAttributes} maps (for compatibility with 1138 * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as 1139 * {@code Annotation} instances 1140 * @return the adapted value, or the original value if no adaptation is needed 1141 */ 1142 static Object adaptValue(Object annotatedElement, Object value, boolean classValuesAsString, 1143 boolean nestedAnnotationsAsMap) { 1144 1145 if (classValuesAsString) { 1146 if (value instanceof Class) { 1147 return ((Class<?>) value).getName(); 1148 } 1149 else if (value instanceof Class[]) { 1150 Class<?>[] clazzArray = (Class<?>[]) value; 1151 String[] classNames = new String[clazzArray.length]; 1152 for (int i = 0; i < clazzArray.length; i++) { 1153 classNames[i] = clazzArray[i].getName(); 1154 } 1155 return classNames; 1156 } 1157 } 1158 1159 if (value instanceof Annotation) { 1160 Annotation annotation = (Annotation) value; 1161 if (nestedAnnotationsAsMap) { 1162 return getAnnotationAttributes(annotatedElement, annotation, classValuesAsString, true); 1163 } 1164 else { 1165 return synthesizeAnnotation(annotation, annotatedElement); 1166 } 1167 } 1168 1169 if (value instanceof Annotation[]) { 1170 Annotation[] annotations = (Annotation[]) value; 1171 if (nestedAnnotationsAsMap) { 1172 AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[annotations.length]; 1173 for (int i = 0; i < annotations.length; i++) { 1174 mappedAnnotations[i] = 1175 getAnnotationAttributes(annotatedElement, annotations[i], classValuesAsString, true); 1176 } 1177 return mappedAnnotations; 1178 } 1179 else { 1180 return synthesizeAnnotationArray(annotations, annotatedElement); 1181 } 1182 } 1183 1184 // Fallback 1185 return value; 1186 } 1187 1188 /** 1189 * Register the annotation-declared default values for the given attributes, 1190 * if available. 1191 * @param attributes the annotation attributes to process 1192 * @since 4.3.2 1193 */ 1194 public static void registerDefaultValues(AnnotationAttributes attributes) { 1195 // Only do defaults scanning for public annotations; we'd run into 1196 // IllegalAccessExceptions otherwise, and we don't want to mess with 1197 // accessibility in a SecurityManager environment. 1198 Class<? extends Annotation> annotationType = attributes.annotationType(); 1199 if (annotationType != null && Modifier.isPublic(annotationType.getModifiers())) { 1200 // Check declared default values of attributes in the annotation type. 1201 for (Method annotationAttribute : getAttributeMethods(annotationType)) { 1202 String attributeName = annotationAttribute.getName(); 1203 Object defaultValue = annotationAttribute.getDefaultValue(); 1204 if (defaultValue != null && !attributes.containsKey(attributeName)) { 1205 if (defaultValue instanceof Annotation) { 1206 defaultValue = getAnnotationAttributes((Annotation) defaultValue, false, true); 1207 } 1208 else if (defaultValue instanceof Annotation[]) { 1209 Annotation[] realAnnotations = (Annotation[]) defaultValue; 1210 AnnotationAttributes[] mappedAnnotations = new AnnotationAttributes[realAnnotations.length]; 1211 for (int i = 0; i < realAnnotations.length; i++) { 1212 mappedAnnotations[i] = getAnnotationAttributes(realAnnotations[i], false, true); 1213 } 1214 defaultValue = mappedAnnotations; 1215 } 1216 attributes.put(attributeName, new DefaultValueHolder(defaultValue)); 1217 } 1218 } 1219 } 1220 } 1221 1222 /** 1223 * Post-process the supplied {@link AnnotationAttributes}, preserving nested 1224 * annotations as {@code Annotation} instances. 1225 * <p>Specifically, this method enforces <em>attribute alias</em> semantics 1226 * for annotation attributes that are annotated with {@link AliasFor @AliasFor} 1227 * and replaces default value placeholders with their original default values. 1228 * @param annotatedElement the element that is annotated with an annotation or 1229 * annotation hierarchy from which the supplied attributes were created; 1230 * may be {@code null} if unknown 1231 * @param attributes the annotation attributes to post-process 1232 * @param classValuesAsString whether to convert Class references into Strings (for 1233 * compatibility with {@link org.springframework.core.type.AnnotationMetadata}) 1234 * or to preserve them as Class references 1235 * @since 4.3.2 1236 * @see #postProcessAnnotationAttributes(Object, AnnotationAttributes, boolean, boolean) 1237 * @see #getDefaultValue(Class, String) 1238 */ 1239 public static void postProcessAnnotationAttributes(Object annotatedElement, 1240 AnnotationAttributes attributes, boolean classValuesAsString) { 1241 1242 postProcessAnnotationAttributes(annotatedElement, attributes, classValuesAsString, false); 1243 } 1244 1245 /** 1246 * Post-process the supplied {@link AnnotationAttributes}. 1247 * <p>Specifically, this method enforces <em>attribute alias</em> semantics 1248 * for annotation attributes that are annotated with {@link AliasFor @AliasFor} 1249 * and replaces default value placeholders with their original default values. 1250 * @param annotatedElement the element that is annotated with an annotation or 1251 * annotation hierarchy from which the supplied attributes were created; 1252 * may be {@code null} if unknown 1253 * @param attributes the annotation attributes to post-process 1254 * @param classValuesAsString whether to convert Class references into Strings (for 1255 * compatibility with {@link org.springframework.core.type.AnnotationMetadata}) 1256 * or to preserve them as Class references 1257 * @param nestedAnnotationsAsMap whether to convert nested annotations into 1258 * {@link AnnotationAttributes} maps (for compatibility with 1259 * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as 1260 * {@code Annotation} instances 1261 * @since 4.2 1262 * @see #retrieveAnnotationAttributes(Object, Annotation, boolean, boolean) 1263 * @see #getDefaultValue(Class, String) 1264 */ 1265 static void postProcessAnnotationAttributes(Object annotatedElement, 1266 AnnotationAttributes attributes, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { 1267 1268 // Abort? 1269 if (attributes == null) { 1270 return; 1271 } 1272 1273 Class<? extends Annotation> annotationType = attributes.annotationType(); 1274 1275 // Track which attribute values have already been replaced so that we can short 1276 // circuit the search algorithms. 1277 Set<String> valuesAlreadyReplaced = new HashSet<String>(); 1278 1279 if (!attributes.validated) { 1280 // Validate @AliasFor configuration 1281 Map<String, List<String>> aliasMap = getAttributeAliasMap(annotationType); 1282 for (String attributeName : aliasMap.keySet()) { 1283 if (valuesAlreadyReplaced.contains(attributeName)) { 1284 continue; 1285 } 1286 Object value = attributes.get(attributeName); 1287 boolean valuePresent = (value != null && !(value instanceof DefaultValueHolder)); 1288 for (String aliasedAttributeName : aliasMap.get(attributeName)) { 1289 if (valuesAlreadyReplaced.contains(aliasedAttributeName)) { 1290 continue; 1291 } 1292 Object aliasedValue = attributes.get(aliasedAttributeName); 1293 boolean aliasPresent = (aliasedValue != null && !(aliasedValue instanceof DefaultValueHolder)); 1294 // Something to validate or replace with an alias? 1295 if (valuePresent || aliasPresent) { 1296 if (valuePresent && aliasPresent) { 1297 // Since annotation attributes can be arrays, we must use ObjectUtils.nullSafeEquals(). 1298 if (!ObjectUtils.nullSafeEquals(value, aliasedValue)) { 1299 String elementAsString = 1300 (annotatedElement != null ? annotatedElement.toString() : "unknown element"); 1301 throw new AnnotationConfigurationException(String.format( 1302 "In AnnotationAttributes for annotation [%s] declared on %s, " + 1303 "attribute '%s' and its alias '%s' are declared with values of [%s] and [%s], " + 1304 "but only one is permitted.", annotationType.getName(), elementAsString, 1305 attributeName, aliasedAttributeName, ObjectUtils.nullSafeToString(value), 1306 ObjectUtils.nullSafeToString(aliasedValue))); 1307 } 1308 } 1309 else if (aliasPresent) { 1310 // Replace value with aliasedValue 1311 attributes.put(attributeName, 1312 adaptValue(annotatedElement, aliasedValue, classValuesAsString, nestedAnnotationsAsMap)); 1313 valuesAlreadyReplaced.add(attributeName); 1314 } 1315 else { 1316 // Replace aliasedValue with value 1317 attributes.put(aliasedAttributeName, 1318 adaptValue(annotatedElement, value, classValuesAsString, nestedAnnotationsAsMap)); 1319 valuesAlreadyReplaced.add(aliasedAttributeName); 1320 } 1321 } 1322 } 1323 } 1324 attributes.validated = true; 1325 } 1326 1327 // Replace any remaining placeholders with actual default values 1328 for (String attributeName : attributes.keySet()) { 1329 if (valuesAlreadyReplaced.contains(attributeName)) { 1330 continue; 1331 } 1332 Object value = attributes.get(attributeName); 1333 if (value instanceof DefaultValueHolder) { 1334 value = ((DefaultValueHolder) value).defaultValue; 1335 attributes.put(attributeName, 1336 adaptValue(annotatedElement, value, classValuesAsString, nestedAnnotationsAsMap)); 1337 } 1338 } 1339 } 1340 1341 /** 1342 * Retrieve the <em>value</em> of the {@code value} attribute of a 1343 * single-element Annotation, given an annotation instance. 1344 * @param annotation the annotation instance from which to retrieve the value 1345 * @return the attribute value, or {@code null} if not found unless the attribute 1346 * value cannot be retrieved due to an {@link AnnotationConfigurationException}, 1347 * in which case such an exception will be rethrown 1348 * @see #getValue(Annotation, String) 1349 */ 1350 public static Object getValue(Annotation annotation) { 1351 return getValue(annotation, VALUE); 1352 } 1353 1354 /** 1355 * Retrieve the <em>value</em> of a named attribute, given an annotation instance. 1356 * @param annotation the annotation instance from which to retrieve the value 1357 * @param attributeName the name of the attribute value to retrieve 1358 * @return the attribute value, or {@code null} if not found unless the attribute 1359 * value cannot be retrieved due to an {@link AnnotationConfigurationException}, 1360 * in which case such an exception will be rethrown 1361 * @see #getValue(Annotation) 1362 * @see #rethrowAnnotationConfigurationException(Throwable) 1363 */ 1364 public static Object getValue(Annotation annotation, String attributeName) { 1365 if (annotation == null || !StringUtils.hasText(attributeName)) { 1366 return null; 1367 } 1368 try { 1369 Method method = annotation.annotationType().getDeclaredMethod(attributeName); 1370 ReflectionUtils.makeAccessible(method); 1371 return method.invoke(annotation); 1372 } 1373 catch (NoSuchMethodException ex) { 1374 return null; 1375 } 1376 catch (InvocationTargetException ex) { 1377 rethrowAnnotationConfigurationException(ex.getTargetException()); 1378 throw new IllegalStateException( 1379 "Could not obtain value for annotation attribute '" + attributeName + "' in " + annotation, ex); 1380 } 1381 catch (Throwable ex) { 1382 handleIntrospectionFailure(annotation.getClass(), ex); 1383 return null; 1384 } 1385 } 1386 1387 /** 1388 * Retrieve the <em>default value</em> of the {@code value} attribute 1389 * of a single-element Annotation, given an annotation instance. 1390 * @param annotation the annotation instance from which to retrieve the default value 1391 * @return the default value, or {@code null} if not found 1392 * @see #getDefaultValue(Annotation, String) 1393 */ 1394 public static Object getDefaultValue(Annotation annotation) { 1395 return getDefaultValue(annotation, VALUE); 1396 } 1397 1398 /** 1399 * Retrieve the <em>default value</em> of a named attribute, given an annotation instance. 1400 * @param annotation the annotation instance from which to retrieve the default value 1401 * @param attributeName the name of the attribute value to retrieve 1402 * @return the default value of the named attribute, or {@code null} if not found 1403 * @see #getDefaultValue(Class, String) 1404 */ 1405 public static Object getDefaultValue(Annotation annotation, String attributeName) { 1406 if (annotation == null) { 1407 return null; 1408 } 1409 return getDefaultValue(annotation.annotationType(), attributeName); 1410 } 1411 1412 /** 1413 * Retrieve the <em>default value</em> of the {@code value} attribute 1414 * of a single-element Annotation, given the {@link Class annotation type}. 1415 * @param annotationType the <em>annotation type</em> for which the default value should be retrieved 1416 * @return the default value, or {@code null} if not found 1417 * @see #getDefaultValue(Class, String) 1418 */ 1419 public static Object getDefaultValue(Class<? extends Annotation> annotationType) { 1420 return getDefaultValue(annotationType, VALUE); 1421 } 1422 1423 /** 1424 * Retrieve the <em>default value</em> of a named attribute, given the 1425 * {@link Class annotation type}. 1426 * @param annotationType the <em>annotation type</em> for which the default value should be retrieved 1427 * @param attributeName the name of the attribute value to retrieve. 1428 * @return the default value of the named attribute, or {@code null} if not found 1429 * @see #getDefaultValue(Annotation, String) 1430 */ 1431 public static Object getDefaultValue(Class<? extends Annotation> annotationType, String attributeName) { 1432 if (annotationType == null || !StringUtils.hasText(attributeName)) { 1433 return null; 1434 } 1435 try { 1436 return annotationType.getDeclaredMethod(attributeName).getDefaultValue(); 1437 } 1438 catch (Throwable ex) { 1439 handleIntrospectionFailure(annotationType, ex); 1440 return null; 1441 } 1442 } 1443 1444 /** 1445 * <em>Synthesize</em> an annotation from the supplied {@code annotation} 1446 * by wrapping it in a dynamic proxy that transparently enforces 1447 * <em>attribute alias</em> semantics for annotation attributes that are 1448 * annotated with {@link AliasFor @AliasFor}. 1449 * @param annotation the annotation to synthesize 1450 * @return the synthesized annotation, if the supplied annotation is 1451 * <em>synthesizable</em>; {@code null} if the supplied annotation is 1452 * {@code null}; otherwise, the supplied annotation unmodified 1453 * @throws AnnotationConfigurationException if invalid configuration of 1454 * {@code @AliasFor} is detected 1455 * @since 4.2 1456 * @see #synthesizeAnnotation(Annotation, AnnotatedElement) 1457 */ 1458 static <A extends Annotation> A synthesizeAnnotation(A annotation) { 1459 return synthesizeAnnotation(annotation, null); 1460 } 1461 1462 /** 1463 * <em>Synthesize</em> an annotation from the supplied {@code annotation} 1464 * by wrapping it in a dynamic proxy that transparently enforces 1465 * <em>attribute alias</em> semantics for annotation attributes that are 1466 * annotated with {@link AliasFor @AliasFor}. 1467 * @param annotation the annotation to synthesize 1468 * @param annotatedElement the element that is annotated with the supplied 1469 * annotation; may be {@code null} if unknown 1470 * @return the synthesized annotation if the supplied annotation is 1471 * <em>synthesizable</em>; {@code null} if the supplied annotation is 1472 * {@code null}; otherwise the supplied annotation unmodified 1473 * @throws AnnotationConfigurationException if invalid configuration of 1474 * {@code @AliasFor} is detected 1475 * @since 4.2 1476 * @see #synthesizeAnnotation(Map, Class, AnnotatedElement) 1477 * @see #synthesizeAnnotation(Class) 1478 */ 1479 public static <A extends Annotation> A synthesizeAnnotation(A annotation, AnnotatedElement annotatedElement) { 1480 return synthesizeAnnotation(annotation, (Object) annotatedElement); 1481 } 1482 1483 @SuppressWarnings("unchecked") 1484 static <A extends Annotation> A synthesizeAnnotation(A annotation, Object annotatedElement) { 1485 if (annotation == null) { 1486 return null; 1487 } 1488 if (annotation instanceof SynthesizedAnnotation) { 1489 return annotation; 1490 } 1491 1492 Class<? extends Annotation> annotationType = annotation.annotationType(); 1493 if (!isSynthesizable(annotationType)) { 1494 return annotation; 1495 } 1496 1497 DefaultAnnotationAttributeExtractor attributeExtractor = 1498 new DefaultAnnotationAttributeExtractor(annotation, annotatedElement); 1499 InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(attributeExtractor); 1500 1501 // Can always expose Spring's SynthesizedAnnotation marker since we explicitly check for a 1502 // synthesizable annotation before (which needs to declare @AliasFor from the same package) 1503 Class<?>[] exposedInterfaces = new Class<?>[] {annotationType, SynthesizedAnnotation.class}; 1504 return (A) Proxy.newProxyInstance(annotation.getClass().getClassLoader(), exposedInterfaces, handler); 1505 } 1506 1507 /** 1508 * <em>Synthesize</em> an annotation from the supplied map of annotation 1509 * attributes by wrapping the map in a dynamic proxy that implements an 1510 * annotation of the specified {@code annotationType} and transparently 1511 * enforces <em>attribute alias</em> semantics for annotation attributes 1512 * that are annotated with {@link AliasFor @AliasFor}. 1513 * <p>The supplied map must contain a key-value pair for every attribute 1514 * defined in the supplied {@code annotationType} that is not aliased or 1515 * does not have a default value. Nested maps and nested arrays of maps 1516 * will be recursively synthesized into nested annotations or nested 1517 * arrays of annotations, respectively. 1518 * <p>Note that {@link AnnotationAttributes} is a specialized type of 1519 * {@link Map} that is an ideal candidate for this method's 1520 * {@code attributes} argument. 1521 * @param attributes the map of annotation attributes to synthesize 1522 * @param annotationType the type of annotation to synthesize 1523 * @param annotatedElement the element that is annotated with the annotation 1524 * corresponding to the supplied attributes; may be {@code null} if unknown 1525 * @return the synthesized annotation, or {@code null} if the supplied attributes 1526 * map is {@code null} 1527 * @throws IllegalArgumentException if a required attribute is missing or if an 1528 * attribute is not of the correct type 1529 * @throws AnnotationConfigurationException if invalid configuration of 1530 * {@code @AliasFor} is detected 1531 * @since 4.2 1532 * @see #synthesizeAnnotation(Annotation, AnnotatedElement) 1533 * @see #synthesizeAnnotation(Class) 1534 * @see #getAnnotationAttributes(AnnotatedElement, Annotation) 1535 * @see #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean) 1536 */ 1537 @SuppressWarnings("unchecked") 1538 public static <A extends Annotation> A synthesizeAnnotation(Map<String, Object> attributes, 1539 Class<A> annotationType, AnnotatedElement annotatedElement) { 1540 1541 Assert.notNull(annotationType, "'annotationType' must not be null"); 1542 if (attributes == null) { 1543 return null; 1544 } 1545 1546 MapAnnotationAttributeExtractor attributeExtractor = 1547 new MapAnnotationAttributeExtractor(attributes, annotationType, annotatedElement); 1548 InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(attributeExtractor); 1549 Class<?>[] exposedInterfaces = (canExposeSynthesizedMarker(annotationType) ? 1550 new Class<?>[] {annotationType, SynthesizedAnnotation.class} : new Class<?>[] {annotationType}); 1551 return (A) Proxy.newProxyInstance(annotationType.getClassLoader(), exposedInterfaces, handler); 1552 } 1553 1554 /** 1555 * <em>Synthesize</em> an annotation from its default attributes values. 1556 * <p>This method simply delegates to 1557 * {@link #synthesizeAnnotation(Map, Class, AnnotatedElement)}, 1558 * supplying an empty map for the source attribute values and {@code null} 1559 * for the {@link AnnotatedElement}. 1560 * @param annotationType the type of annotation to synthesize 1561 * @return the synthesized annotation 1562 * @throws IllegalArgumentException if a required attribute is missing 1563 * @throws AnnotationConfigurationException if invalid configuration of 1564 * {@code @AliasFor} is detected 1565 * @since 4.2 1566 * @see #synthesizeAnnotation(Map, Class, AnnotatedElement) 1567 * @see #synthesizeAnnotation(Annotation, AnnotatedElement) 1568 */ 1569 public static <A extends Annotation> A synthesizeAnnotation(Class<A> annotationType) { 1570 return synthesizeAnnotation(Collections.<String, Object> emptyMap(), annotationType, null); 1571 } 1572 1573 /** 1574 * <em>Synthesize</em> an array of annotations from the supplied array 1575 * of {@code annotations} by creating a new array of the same size and 1576 * type and populating it with {@linkplain #synthesizeAnnotation(Annotation) 1577 * synthesized} versions of the annotations from the input array. 1578 * @param annotations the array of annotations to synthesize 1579 * @param annotatedElement the element that is annotated with the supplied 1580 * array of annotations; may be {@code null} if unknown 1581 * @return a new array of synthesized annotations, or {@code null} if 1582 * the supplied array is {@code null} 1583 * @throws AnnotationConfigurationException if invalid configuration of 1584 * {@code @AliasFor} is detected 1585 * @since 4.2 1586 * @see #synthesizeAnnotation(Annotation, AnnotatedElement) 1587 * @see #synthesizeAnnotation(Map, Class, AnnotatedElement) 1588 */ 1589 static Annotation[] synthesizeAnnotationArray(Annotation[] annotations, Object annotatedElement) { 1590 if (annotations == null) { 1591 return null; 1592 } 1593 1594 Annotation[] synthesized = (Annotation[]) Array.newInstance( 1595 annotations.getClass().getComponentType(), annotations.length); 1596 for (int i = 0; i < annotations.length; i++) { 1597 synthesized[i] = synthesizeAnnotation(annotations[i], annotatedElement); 1598 } 1599 return synthesized; 1600 } 1601 1602 /** 1603 * <em>Synthesize</em> an array of annotations from the supplied array 1604 * of {@code maps} of annotation attributes by creating a new array of 1605 * {@code annotationType} with the same size and populating it with 1606 * {@linkplain #synthesizeAnnotation(Map, Class, AnnotatedElement) 1607 * synthesized} versions of the maps from the input array. 1608 * @param maps the array of maps of annotation attributes to synthesize 1609 * @param annotationType the type of annotations to synthesize 1610 * (never {@code null}) 1611 * @return a new array of synthesized annotations, or {@code null} if 1612 * the supplied array is {@code null} 1613 * @throws AnnotationConfigurationException if invalid configuration of 1614 * {@code @AliasFor} is detected 1615 * @since 4.2.1 1616 * @see #synthesizeAnnotation(Map, Class, AnnotatedElement) 1617 * @see #synthesizeAnnotationArray(Annotation[], Object) 1618 */ 1619 @SuppressWarnings("unchecked") 1620 static <A extends Annotation> A[] synthesizeAnnotationArray(Map<String, Object>[] maps, Class<A> annotationType) { 1621 Assert.notNull(annotationType, "'annotationType' must not be null"); 1622 if (maps == null) { 1623 return null; 1624 } 1625 1626 A[] synthesized = (A[]) Array.newInstance(annotationType, maps.length); 1627 for (int i = 0; i < maps.length; i++) { 1628 synthesized[i] = synthesizeAnnotation(maps[i], annotationType, null); 1629 } 1630 return synthesized; 1631 } 1632 1633 /** 1634 * Get a map of all attribute aliases declared via {@code @AliasFor} 1635 * in the supplied annotation type. 1636 * <p>The map is keyed by attribute name with each value representing 1637 * a list of names of aliased attributes. 1638 * <p>For <em>explicit</em> alias pairs such as x and y (i.e., where x 1639 * is an {@code @AliasFor("y")} and y is an {@code @AliasFor("x")}, there 1640 * will be two entries in the map: {@code x -> (y)} and {@code y -> (x)}. 1641 * <p>For <em>implicit</em> aliases (i.e., attributes that are declared 1642 * as attribute overrides for the same attribute in the same meta-annotation), 1643 * there will be n entries in the map. For example, if x, y, and z are 1644 * implicit aliases, the map will contain the following entries: 1645 * {@code x -> (y, z)}, {@code y -> (x, z)}, {@code z -> (x, y)}. 1646 * <p>An empty return value implies that the annotation does not declare 1647 * any attribute aliases. 1648 * @param annotationType the annotation type to find attribute aliases in 1649 * @return a map containing attribute aliases (never {@code null}) 1650 * @since 4.2 1651 */ 1652 static Map<String, List<String>> getAttributeAliasMap(Class<? extends Annotation> annotationType) { 1653 if (annotationType == null) { 1654 return Collections.emptyMap(); 1655 } 1656 1657 Map<String, List<String>> map = attributeAliasesCache.get(annotationType); 1658 if (map != null) { 1659 return map; 1660 } 1661 1662 map = new LinkedHashMap<String, List<String>>(); 1663 for (Method attribute : getAttributeMethods(annotationType)) { 1664 List<String> aliasNames = getAttributeAliasNames(attribute); 1665 if (!aliasNames.isEmpty()) { 1666 map.put(attribute.getName(), aliasNames); 1667 } 1668 } 1669 1670 attributeAliasesCache.put(annotationType, map); 1671 return map; 1672 } 1673 1674 /** 1675 * Check whether we can expose our {@link SynthesizedAnnotation} marker for the given annotation type. 1676 * @param annotationType the annotation type that we are about to create a synthesized proxy for 1677 */ 1678 private static boolean canExposeSynthesizedMarker(Class<? extends Annotation> annotationType) { 1679 try { 1680 return (Class.forName(SynthesizedAnnotation.class.getName(), false, annotationType.getClassLoader()) == 1681 SynthesizedAnnotation.class); 1682 } 1683 catch (ClassNotFoundException ex) { 1684 return false; 1685 } 1686 } 1687 1688 /** 1689 * Determine if annotations of the supplied {@code annotationType} are 1690 * <em>synthesizable</em> (i.e., in need of being wrapped in a dynamic 1691 * proxy that provides functionality above that of a standard JDK 1692 * annotation). 1693 * <p>Specifically, an annotation is <em>synthesizable</em> if it declares 1694 * any attributes that are configured as <em>aliased pairs</em> via 1695 * {@link AliasFor @AliasFor} or if any nested annotations used by the 1696 * annotation declare such <em>aliased pairs</em>. 1697 * @since 4.2 1698 * @see SynthesizedAnnotation 1699 * @see SynthesizedAnnotationInvocationHandler 1700 */ 1701 @SuppressWarnings("unchecked") 1702 private static boolean isSynthesizable(Class<? extends Annotation> annotationType) { 1703 Boolean synthesizable = synthesizableCache.get(annotationType); 1704 if (synthesizable != null) { 1705 return synthesizable; 1706 } 1707 1708 synthesizable = Boolean.FALSE; 1709 for (Method attribute : getAttributeMethods(annotationType)) { 1710 if (!getAttributeAliasNames(attribute).isEmpty()) { 1711 synthesizable = Boolean.TRUE; 1712 break; 1713 } 1714 Class<?> returnType = attribute.getReturnType(); 1715 if (Annotation[].class.isAssignableFrom(returnType)) { 1716 Class<? extends Annotation> nestedAnnotationType = 1717 (Class<? extends Annotation>) returnType.getComponentType(); 1718 if (isSynthesizable(nestedAnnotationType)) { 1719 synthesizable = Boolean.TRUE; 1720 break; 1721 } 1722 } 1723 else if (Annotation.class.isAssignableFrom(returnType)) { 1724 Class<? extends Annotation> nestedAnnotationType = (Class<? extends Annotation>) returnType; 1725 if (isSynthesizable(nestedAnnotationType)) { 1726 synthesizable = Boolean.TRUE; 1727 break; 1728 } 1729 } 1730 } 1731 1732 synthesizableCache.put(annotationType, synthesizable); 1733 return synthesizable; 1734 } 1735 1736 /** 1737 * Get the names of the aliased attributes configured via 1738 * {@link AliasFor @AliasFor} for the supplied annotation {@code attribute}. 1739 * @param attribute the attribute to find aliases for 1740 * @return the names of the aliased attributes (never {@code null}, though 1741 * potentially <em>empty</em>) 1742 * @throws IllegalArgumentException if the supplied attribute method is 1743 * {@code null} or not from an annotation 1744 * @throws AnnotationConfigurationException if invalid configuration of 1745 * {@code @AliasFor} is detected 1746 * @since 4.2 1747 * @see #getAttributeOverrideName(Method, Class) 1748 */ 1749 static List<String> getAttributeAliasNames(Method attribute) { 1750 Assert.notNull(attribute, "attribute must not be null"); 1751 AliasDescriptor descriptor = AliasDescriptor.from(attribute); 1752 return (descriptor != null ? descriptor.getAttributeAliasNames() : Collections.<String> emptyList()); 1753 } 1754 1755 /** 1756 * Get the name of the overridden attribute configured via 1757 * {@link AliasFor @AliasFor} for the supplied annotation {@code attribute}. 1758 * @param attribute the attribute from which to retrieve the override 1759 * (never {@code null}) 1760 * @param metaAnnotationType the type of meta-annotation in which the 1761 * overridden attribute is allowed to be declared 1762 * @return the name of the overridden attribute, or {@code null} if not 1763 * found or not applicable for the specified meta-annotation type 1764 * @throws IllegalArgumentException if the supplied attribute method is 1765 * {@code null} or not from an annotation, or if the supplied meta-annotation 1766 * type is {@code null} or {@link Annotation} 1767 * @throws AnnotationConfigurationException if invalid configuration of 1768 * {@code @AliasFor} is detected 1769 * @since 4.2 1770 */ 1771 static String getAttributeOverrideName(Method attribute, Class<? extends Annotation> metaAnnotationType) { 1772 Assert.notNull(attribute, "attribute must not be null"); 1773 Assert.notNull(metaAnnotationType, "metaAnnotationType must not be null"); 1774 Assert.isTrue(Annotation.class != metaAnnotationType, 1775 "metaAnnotationType must not be [java.lang.annotation.Annotation]"); 1776 1777 AliasDescriptor descriptor = AliasDescriptor.from(attribute); 1778 return (descriptor != null ? descriptor.getAttributeOverrideName(metaAnnotationType) : null); 1779 } 1780 1781 /** 1782 * Get all methods declared in the supplied {@code annotationType} that 1783 * match Java's requirements for annotation <em>attributes</em>. 1784 * <p>All methods in the returned list will be 1785 * {@linkplain ReflectionUtils#makeAccessible(Method) made accessible}. 1786 * @param annotationType the type in which to search for attribute methods 1787 * (never {@code null}) 1788 * @return all annotation attribute methods in the specified annotation 1789 * type (never {@code null}, though potentially <em>empty</em>) 1790 * @since 4.2 1791 */ 1792 static List<Method> getAttributeMethods(Class<? extends Annotation> annotationType) { 1793 List<Method> methods = attributeMethodsCache.get(annotationType); 1794 if (methods != null) { 1795 return methods; 1796 } 1797 1798 methods = new ArrayList<Method>(); 1799 for (Method method : annotationType.getDeclaredMethods()) { 1800 if (isAttributeMethod(method)) { 1801 ReflectionUtils.makeAccessible(method); 1802 methods.add(method); 1803 } 1804 } 1805 1806 attributeMethodsCache.put(annotationType, methods); 1807 return methods; 1808 } 1809 1810 /** 1811 * Get the annotation with the supplied {@code annotationName} on the 1812 * supplied {@code element}. 1813 * @param element the element to search on 1814 * @param annotationName the fully qualified class name of the annotation 1815 * type to find 1816 * @return the annotation if found; {@code null} otherwise 1817 * @since 4.2 1818 */ 1819 static Annotation getAnnotation(AnnotatedElement element, String annotationName) { 1820 for (Annotation annotation : element.getAnnotations()) { 1821 if (annotation.annotationType().getName().equals(annotationName)) { 1822 return annotation; 1823 } 1824 } 1825 return null; 1826 } 1827 1828 /** 1829 * Determine if the supplied {@code method} is an annotation attribute method. 1830 * @param method the method to check 1831 * @return {@code true} if the method is an attribute method 1832 * @since 4.2 1833 */ 1834 static boolean isAttributeMethod(Method method) { 1835 return (method != null && method.getParameterTypes().length == 0 && method.getReturnType() != void.class); 1836 } 1837 1838 /** 1839 * Determine if the supplied method is an "annotationType" method. 1840 * @return {@code true} if the method is an "annotationType" method 1841 * @see Annotation#annotationType() 1842 * @since 4.2 1843 */ 1844 static boolean isAnnotationTypeMethod(Method method) { 1845 return (method != null && method.getName().equals("annotationType") && method.getParameterTypes().length == 0); 1846 } 1847 1848 /** 1849 * Resolve the container type for the supplied repeatable {@code annotationType}. 1850 * <p>Automatically detects a <em>container annotation</em> declared via 1851 * {@link java.lang.annotation.Repeatable}. If the supplied annotation type 1852 * is not annotated with {@code @Repeatable}, this method simply returns 1853 * {@code null}. 1854 * @since 4.2 1855 */ 1856 @SuppressWarnings("unchecked") 1857 static Class<? extends Annotation> resolveContainerAnnotationType(Class<? extends Annotation> annotationType) { 1858 try { 1859 Annotation repeatable = getAnnotation(annotationType, REPEATABLE_CLASS_NAME); 1860 if (repeatable != null) { 1861 Object value = getValue(repeatable); 1862 return (Class<? extends Annotation>) value; 1863 } 1864 } 1865 catch (Exception ex) { 1866 handleIntrospectionFailure(annotationType, ex); 1867 } 1868 return null; 1869 } 1870 1871 /** 1872 * If the supplied throwable is an {@link AnnotationConfigurationException}, 1873 * it will be cast to an {@code AnnotationConfigurationException} and thrown, 1874 * allowing it to propagate to the caller. 1875 * <p>Otherwise, this method does nothing. 1876 * @param ex the throwable to inspect 1877 * @since 4.2 1878 */ 1879 static void rethrowAnnotationConfigurationException(Throwable ex) { 1880 if (ex instanceof AnnotationConfigurationException) { 1881 throw (AnnotationConfigurationException) ex; 1882 } 1883 } 1884 1885 /** 1886 * Handle the supplied annotation introspection exception. 1887 * <p>If the supplied exception is an {@link AnnotationConfigurationException}, 1888 * it will simply be thrown, allowing it to propagate to the caller, and 1889 * nothing will be logged. 1890 * <p>Otherwise, this method logs an introspection failure (in particular 1891 * {@code TypeNotPresentExceptions}) before moving on, assuming nested 1892 * Class values were not resolvable within annotation attributes and 1893 * thereby effectively pretending there were no annotations on the specified 1894 * element. 1895 * @param element the element that we tried to introspect annotations on 1896 * @param ex the exception that we encountered 1897 * @see #rethrowAnnotationConfigurationException 1898 */ 1899 static void handleIntrospectionFailure(AnnotatedElement element, Throwable ex) { 1900 rethrowAnnotationConfigurationException(ex); 1901 1902 Log loggerToUse = logger; 1903 if (loggerToUse == null) { 1904 loggerToUse = LogFactory.getLog(AnnotationUtils.class); 1905 logger = loggerToUse; 1906 } 1907 if (element instanceof Class && Annotation.class.isAssignableFrom((Class<?>) element)) { 1908 // Meta-annotation or (default) value lookup on an annotation type 1909 if (loggerToUse.isDebugEnabled()) { 1910 loggerToUse.debug("Failed to meta-introspect annotation " + element + ": " + ex); 1911 } 1912 } 1913 else { 1914 // Direct annotation lookup on regular Class, Method, Field 1915 if (loggerToUse.isInfoEnabled()) { 1916 loggerToUse.info("Failed to introspect annotations on " + element + ": " + ex); 1917 } 1918 } 1919 } 1920 1921 /** 1922 * Clear the internal annotation metadata cache. 1923 * @since 4.3.15 1924 */ 1925 public static void clearCache() { 1926 findAnnotationCache.clear(); 1927 metaPresentCache.clear(); 1928 annotatedInterfaceCache.clear(); 1929 synthesizableCache.clear(); 1930 attributeAliasesCache.clear(); 1931 attributeMethodsCache.clear(); 1932 aliasDescriptorCache.clear(); 1933 } 1934 1935 1936 /** 1937 * Cache key for the AnnotatedElement cache. 1938 */ 1939 private static final class AnnotationCacheKey implements Comparable<AnnotationCacheKey> { 1940 1941 private final AnnotatedElement element; 1942 1943 private final Class<? extends Annotation> annotationType; 1944 1945 public AnnotationCacheKey(AnnotatedElement element, Class<? extends Annotation> annotationType) { 1946 this.element = element; 1947 this.annotationType = annotationType; 1948 } 1949 1950 @Override 1951 public boolean equals(Object other) { 1952 if (this == other) { 1953 return true; 1954 } 1955 if (!(other instanceof AnnotationCacheKey)) { 1956 return false; 1957 } 1958 AnnotationCacheKey otherKey = (AnnotationCacheKey) other; 1959 return (this.element.equals(otherKey.element) && this.annotationType.equals(otherKey.annotationType)); 1960 } 1961 1962 @Override 1963 public int hashCode() { 1964 return (this.element.hashCode() * 29 + this.annotationType.hashCode()); 1965 } 1966 1967 @Override 1968 public String toString() { 1969 return "@" + this.annotationType + " on " + this.element; 1970 } 1971 1972 @Override 1973 public int compareTo(AnnotationCacheKey other) { 1974 int result = this.element.toString().compareTo(other.element.toString()); 1975 if (result == 0) { 1976 result = this.annotationType.getName().compareTo(other.annotationType.getName()); 1977 } 1978 return result; 1979 } 1980 } 1981 1982 1983 private static class AnnotationCollector<A extends Annotation> { 1984 1985 private final Class<A> annotationType; 1986 1987 private final Class<? extends Annotation> containerAnnotationType; 1988 1989 private final boolean declaredMode; 1990 1991 private final Set<AnnotatedElement> visited = new HashSet<AnnotatedElement>(); 1992 1993 private final Set<A> result = new LinkedHashSet<A>(); 1994 1995 AnnotationCollector(Class<A> annotationType, Class<? extends Annotation> containerAnnotationType, boolean declaredMode) { 1996 this.annotationType = annotationType; 1997 this.containerAnnotationType = (containerAnnotationType != null ? containerAnnotationType : 1998 resolveContainerAnnotationType(annotationType)); 1999 this.declaredMode = declaredMode; 2000 } 2001 2002 Set<A> getResult(AnnotatedElement element) { 2003 process(element); 2004 return Collections.unmodifiableSet(this.result); 2005 } 2006 2007 @SuppressWarnings("unchecked") 2008 private void process(AnnotatedElement element) { 2009 if (this.visited.add(element)) { 2010 try { 2011 Annotation[] annotations = (this.declaredMode ? element.getDeclaredAnnotations() : element.getAnnotations()); 2012 for (Annotation ann : annotations) { 2013 Class<? extends Annotation> currentAnnotationType = ann.annotationType(); 2014 if (ObjectUtils.nullSafeEquals(this.annotationType, currentAnnotationType)) { 2015 this.result.add(synthesizeAnnotation((A) ann, element)); 2016 } 2017 else if (ObjectUtils.nullSafeEquals(this.containerAnnotationType, currentAnnotationType)) { 2018 this.result.addAll(getValue(element, ann)); 2019 } 2020 else if (!isInJavaLangAnnotationPackage(currentAnnotationType)) { 2021 process(currentAnnotationType); 2022 } 2023 } 2024 } 2025 catch (Throwable ex) { 2026 handleIntrospectionFailure(element, ex); 2027 } 2028 } 2029 } 2030 2031 @SuppressWarnings("unchecked") 2032 private List<A> getValue(AnnotatedElement element, Annotation annotation) { 2033 try { 2034 List<A> synthesizedAnnotations = new ArrayList<A>(); 2035 for (A anno : (A[]) AnnotationUtils.getValue(annotation)) { 2036 synthesizedAnnotations.add(synthesizeAnnotation(anno, element)); 2037 } 2038 return synthesizedAnnotations; 2039 } 2040 catch (Throwable ex) { 2041 handleIntrospectionFailure(element, ex); 2042 } 2043 // Unable to read value from repeating annotation container -> ignore it. 2044 return Collections.emptyList(); 2045 } 2046 } 2047 2048 2049 /** 2050 * {@code AliasDescriptor} encapsulates the declaration of {@code @AliasFor} 2051 * on a given annotation attribute and includes support for validating 2052 * the configuration of aliases (both explicit and implicit). 2053 * @since 4.2.1 2054 * @see #from 2055 * @see #getAttributeAliasNames 2056 * @see #getAttributeOverrideName 2057 */ 2058 private static class AliasDescriptor { 2059 2060 private final Method sourceAttribute; 2061 2062 private final Class<? extends Annotation> sourceAnnotationType; 2063 2064 private final String sourceAttributeName; 2065 2066 private final Method aliasedAttribute; 2067 2068 private final Class<? extends Annotation> aliasedAnnotationType; 2069 2070 private final String aliasedAttributeName; 2071 2072 private final boolean isAliasPair; 2073 2074 /** 2075 * Create an {@code AliasDescriptor} <em>from</em> the declaration 2076 * of {@code @AliasFor} on the supplied annotation attribute and 2077 * validate the configuration of {@code @AliasFor}. 2078 * @param attribute the annotation attribute that is annotated with 2079 * {@code @AliasFor} 2080 * @return an alias descriptor, or {@code null} if the attribute 2081 * is not annotated with {@code @AliasFor} 2082 * @see #validateAgainst 2083 */ 2084 public static AliasDescriptor from(Method attribute) { 2085 AliasDescriptor descriptor = aliasDescriptorCache.get(attribute); 2086 if (descriptor != null) { 2087 return descriptor; 2088 } 2089 2090 AliasFor aliasFor = attribute.getAnnotation(AliasFor.class); 2091 if (aliasFor == null) { 2092 return null; 2093 } 2094 2095 descriptor = new AliasDescriptor(attribute, aliasFor); 2096 descriptor.validate(); 2097 aliasDescriptorCache.put(attribute, descriptor); 2098 return descriptor; 2099 } 2100 2101 @SuppressWarnings("unchecked") 2102 private AliasDescriptor(Method sourceAttribute, AliasFor aliasFor) { 2103 Class<?> declaringClass = sourceAttribute.getDeclaringClass(); 2104 Assert.isTrue(declaringClass.isAnnotation(), "sourceAttribute must be from an annotation"); 2105 2106 this.sourceAttribute = sourceAttribute; 2107 this.sourceAnnotationType = (Class<? extends Annotation>) declaringClass; 2108 this.sourceAttributeName = sourceAttribute.getName(); 2109 2110 this.aliasedAnnotationType = (Annotation.class == aliasFor.annotation() ? 2111 this.sourceAnnotationType : aliasFor.annotation()); 2112 this.aliasedAttributeName = getAliasedAttributeName(aliasFor, sourceAttribute); 2113 if (this.aliasedAnnotationType == this.sourceAnnotationType && 2114 this.aliasedAttributeName.equals(this.sourceAttributeName)) { 2115 String msg = String.format("@AliasFor declaration on attribute '%s' in annotation [%s] points to " + 2116 "itself. Specify 'annotation' to point to a same-named attribute on a meta-annotation.", 2117 sourceAttribute.getName(), declaringClass.getName()); 2118 throw new AnnotationConfigurationException(msg); 2119 } 2120 try { 2121 this.aliasedAttribute = this.aliasedAnnotationType.getDeclaredMethod(this.aliasedAttributeName); 2122 } 2123 catch (NoSuchMethodException ex) { 2124 String msg = String.format( 2125 "Attribute '%s' in annotation [%s] is declared as an @AliasFor nonexistent attribute '%s' in annotation [%s].", 2126 this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName, 2127 this.aliasedAnnotationType.getName()); 2128 throw new AnnotationConfigurationException(msg, ex); 2129 } 2130 2131 this.isAliasPair = (this.sourceAnnotationType == this.aliasedAnnotationType); 2132 } 2133 2134 private void validate() { 2135 // Target annotation is not meta-present? 2136 if (!this.isAliasPair && !isAnnotationMetaPresent(this.sourceAnnotationType, this.aliasedAnnotationType)) { 2137 String msg = String.format("@AliasFor declaration on attribute '%s' in annotation [%s] declares " + 2138 "an alias for attribute '%s' in meta-annotation [%s] which is not meta-present.", 2139 this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName, 2140 this.aliasedAnnotationType.getName()); 2141 throw new AnnotationConfigurationException(msg); 2142 } 2143 2144 if (this.isAliasPair) { 2145 AliasFor mirrorAliasFor = this.aliasedAttribute.getAnnotation(AliasFor.class); 2146 if (mirrorAliasFor == null) { 2147 String msg = String.format("Attribute '%s' in annotation [%s] must be declared as an @AliasFor [%s].", 2148 this.aliasedAttributeName, this.sourceAnnotationType.getName(), this.sourceAttributeName); 2149 throw new AnnotationConfigurationException(msg); 2150 } 2151 2152 String mirrorAliasedAttributeName = getAliasedAttributeName(mirrorAliasFor, this.aliasedAttribute); 2153 if (!this.sourceAttributeName.equals(mirrorAliasedAttributeName)) { 2154 String msg = String.format("Attribute '%s' in annotation [%s] must be declared as an @AliasFor [%s], not [%s].", 2155 this.aliasedAttributeName, this.sourceAnnotationType.getName(), this.sourceAttributeName, 2156 mirrorAliasedAttributeName); 2157 throw new AnnotationConfigurationException(msg); 2158 } 2159 } 2160 2161 Class<?> returnType = this.sourceAttribute.getReturnType(); 2162 Class<?> aliasedReturnType = this.aliasedAttribute.getReturnType(); 2163 if (returnType != aliasedReturnType && 2164 (!aliasedReturnType.isArray() || returnType != aliasedReturnType.getComponentType())) { 2165 String msg = String.format("Misconfigured aliases: attribute '%s' in annotation [%s] " + 2166 "and attribute '%s' in annotation [%s] must declare the same return type.", 2167 this.sourceAttributeName, this.sourceAnnotationType.getName(), this.aliasedAttributeName, 2168 this.aliasedAnnotationType.getName()); 2169 throw new AnnotationConfigurationException(msg); 2170 } 2171 2172 if (this.isAliasPair) { 2173 validateDefaultValueConfiguration(this.aliasedAttribute); 2174 } 2175 } 2176 2177 private void validateDefaultValueConfiguration(Method aliasedAttribute) { 2178 Assert.notNull(aliasedAttribute, "aliasedAttribute must not be null"); 2179 Object defaultValue = this.sourceAttribute.getDefaultValue(); 2180 Object aliasedDefaultValue = aliasedAttribute.getDefaultValue(); 2181 2182 if (defaultValue == null || aliasedDefaultValue == null) { 2183 String msg = String.format("Misconfigured aliases: attribute '%s' in annotation [%s] " + 2184 "and attribute '%s' in annotation [%s] must declare default values.", 2185 this.sourceAttributeName, this.sourceAnnotationType.getName(), aliasedAttribute.getName(), 2186 aliasedAttribute.getDeclaringClass().getName()); 2187 throw new AnnotationConfigurationException(msg); 2188 } 2189 2190 if (!ObjectUtils.nullSafeEquals(defaultValue, aliasedDefaultValue)) { 2191 String msg = String.format("Misconfigured aliases: attribute '%s' in annotation [%s] " + 2192 "and attribute '%s' in annotation [%s] must declare the same default value.", 2193 this.sourceAttributeName, this.sourceAnnotationType.getName(), aliasedAttribute.getName(), 2194 aliasedAttribute.getDeclaringClass().getName()); 2195 throw new AnnotationConfigurationException(msg); 2196 } 2197 } 2198 2199 /** 2200 * Validate this descriptor against the supplied descriptor. 2201 * <p>This method only validates the configuration of default values 2202 * for the two descriptors, since other aspects of the descriptors 2203 * are validated when they are created. 2204 */ 2205 private void validateAgainst(AliasDescriptor otherDescriptor) { 2206 validateDefaultValueConfiguration(otherDescriptor.sourceAttribute); 2207 } 2208 2209 /** 2210 * Determine if this descriptor represents an explicit override for 2211 * an attribute in the supplied {@code metaAnnotationType}. 2212 * @see #isAliasFor 2213 */ 2214 private boolean isOverrideFor(Class<? extends Annotation> metaAnnotationType) { 2215 return (this.aliasedAnnotationType == metaAnnotationType); 2216 } 2217 2218 /** 2219 * Determine if this descriptor and the supplied descriptor both 2220 * effectively represent aliases for the same attribute in the same 2221 * target annotation, either explicitly or implicitly. 2222 * <p>This method searches the attribute override hierarchy, beginning 2223 * with this descriptor, in order to detect implicit and transitively 2224 * implicit aliases. 2225 * @return {@code true} if this descriptor and the supplied descriptor 2226 * effectively alias the same annotation attribute 2227 * @see #isOverrideFor 2228 */ 2229 private boolean isAliasFor(AliasDescriptor otherDescriptor) { 2230 for (AliasDescriptor lhs = this; lhs != null; lhs = lhs.getAttributeOverrideDescriptor()) { 2231 for (AliasDescriptor rhs = otherDescriptor; rhs != null; rhs = rhs.getAttributeOverrideDescriptor()) { 2232 if (lhs.aliasedAttribute.equals(rhs.aliasedAttribute)) { 2233 return true; 2234 } 2235 } 2236 } 2237 return false; 2238 } 2239 2240 public List<String> getAttributeAliasNames() { 2241 // Explicit alias pair? 2242 if (this.isAliasPair) { 2243 return Collections.singletonList(this.aliasedAttributeName); 2244 } 2245 2246 // Else: search for implicit aliases 2247 List<String> aliases = new ArrayList<String>(); 2248 for (AliasDescriptor otherDescriptor : getOtherDescriptors()) { 2249 if (this.isAliasFor(otherDescriptor)) { 2250 this.validateAgainst(otherDescriptor); 2251 aliases.add(otherDescriptor.sourceAttributeName); 2252 } 2253 } 2254 return aliases; 2255 } 2256 2257 private List<AliasDescriptor> getOtherDescriptors() { 2258 List<AliasDescriptor> otherDescriptors = new ArrayList<AliasDescriptor>(); 2259 for (Method currentAttribute : getAttributeMethods(this.sourceAnnotationType)) { 2260 if (!this.sourceAttribute.equals(currentAttribute)) { 2261 AliasDescriptor otherDescriptor = AliasDescriptor.from(currentAttribute); 2262 if (otherDescriptor != null) { 2263 otherDescriptors.add(otherDescriptor); 2264 } 2265 } 2266 } 2267 return otherDescriptors; 2268 } 2269 2270 public String getAttributeOverrideName(Class<? extends Annotation> metaAnnotationType) { 2271 Assert.notNull(metaAnnotationType, "metaAnnotationType must not be null"); 2272 Assert.isTrue(Annotation.class != metaAnnotationType, 2273 "metaAnnotationType must not be [java.lang.annotation.Annotation]"); 2274 2275 // Search the attribute override hierarchy, starting with the current attribute 2276 for (AliasDescriptor desc = this; desc != null; desc = desc.getAttributeOverrideDescriptor()) { 2277 if (desc.isOverrideFor(metaAnnotationType)) { 2278 return desc.aliasedAttributeName; 2279 } 2280 } 2281 2282 // Else: explicit attribute override for a different meta-annotation 2283 return null; 2284 } 2285 2286 private AliasDescriptor getAttributeOverrideDescriptor() { 2287 if (this.isAliasPair) { 2288 return null; 2289 } 2290 return AliasDescriptor.from(this.aliasedAttribute); 2291 } 2292 2293 /** 2294 * Get the name of the aliased attribute configured via the supplied 2295 * {@link AliasFor @AliasFor} annotation on the supplied {@code attribute}, 2296 * or the original attribute if no aliased one specified (indicating that 2297 * the reference goes to a same-named attribute on a meta-annotation). 2298 * <p>This method returns the value of either the {@code attribute} 2299 * or {@code value} attribute of {@code @AliasFor}, ensuring that only 2300 * one of the attributes has been declared while simultaneously ensuring 2301 * that at least one of the attributes has been declared. 2302 * @param aliasFor the {@code @AliasFor} annotation from which to retrieve 2303 * the aliased attribute name 2304 * @param attribute the attribute that is annotated with {@code @AliasFor} 2305 * @return the name of the aliased attribute (never {@code null} or empty) 2306 * @throws AnnotationConfigurationException if invalid configuration of 2307 * {@code @AliasFor} is detected 2308 */ 2309 private String getAliasedAttributeName(AliasFor aliasFor, Method attribute) { 2310 String attributeName = aliasFor.attribute(); 2311 String value = aliasFor.value(); 2312 boolean attributeDeclared = StringUtils.hasText(attributeName); 2313 boolean valueDeclared = StringUtils.hasText(value); 2314 2315 // Ensure user did not declare both 'value' and 'attribute' in @AliasFor 2316 if (attributeDeclared && valueDeclared) { 2317 String msg = String.format("In @AliasFor declared on attribute '%s' in annotation [%s], attribute 'attribute' " + 2318 "and its alias 'value' are present with values of [%s] and [%s], but only one is permitted.", 2319 attribute.getName(), attribute.getDeclaringClass().getName(), attributeName, value); 2320 throw new AnnotationConfigurationException(msg); 2321 } 2322 2323 // Either explicit attribute name or pointing to same-named attribute by default 2324 attributeName = (attributeDeclared ? attributeName : value); 2325 return (StringUtils.hasText(attributeName) ? attributeName.trim() : attribute.getName()); 2326 } 2327 2328 @Override 2329 public String toString() { 2330 return String.format("%s: @%s(%s) is an alias for @%s(%s)", getClass().getSimpleName(), 2331 this.sourceAnnotationType.getSimpleName(), this.sourceAttributeName, 2332 this.aliasedAnnotationType.getSimpleName(), this.aliasedAttributeName); 2333 } 2334 } 2335 2336 2337 private static class DefaultValueHolder { 2338 2339 final Object defaultValue; 2340 2341 public DefaultValueHolder(Object defaultValue) { 2342 this.defaultValue = defaultValue; 2343 } 2344 } 2345 2346}