001/* 002 * Copyright 2002-2020 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.InvocationTargetException; 023import java.lang.reflect.Method; 024import java.lang.reflect.Modifier; 025import java.util.Collection; 026import java.util.Collections; 027import java.util.LinkedHashMap; 028import java.util.List; 029import java.util.Map; 030import java.util.NoSuchElementException; 031import java.util.Set; 032 033import org.springframework.core.BridgeMethodResolver; 034import org.springframework.core.annotation.AnnotationTypeMapping.MirrorSets.MirrorSet; 035import org.springframework.core.annotation.MergedAnnotation.Adapt; 036import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; 037import org.springframework.lang.Nullable; 038import org.springframework.util.ConcurrentReferenceHashMap; 039import org.springframework.util.ReflectionUtils; 040import org.springframework.util.StringUtils; 041 042/** 043 * General utility methods for working with annotations, handling meta-annotations, 044 * bridge methods (which the compiler generates for generic declarations) as well 045 * as super methods (for optional <em>annotation inheritance</em>). 046 * 047 * <p>Note that most of the features of this class are not provided by the 048 * JDK's introspection facilities themselves. 049 * 050 * <p>As a general rule for runtime-retained application annotations (e.g. for 051 * transaction control, authorization, or service exposure), always use the 052 * lookup methods on this class (e.g. {@link #findAnnotation(Method, Class)} or 053 * {@link #getAnnotation(Method, Class)}) instead of the plain annotation lookup 054 * methods in the JDK. You can still explicitly choose between a <em>get</em> 055 * lookup on the given class level only ({@link #getAnnotation(Method, Class)}) 056 * and a <em>find</em> lookup in the entire inheritance hierarchy of the given 057 * method ({@link #findAnnotation(Method, Class)}). 058 * 059 * <h3>Terminology</h3> 060 * The terms <em>directly present</em>, <em>indirectly present</em>, and 061 * <em>present</em> have the same meanings as defined in the class-level 062 * javadoc for {@link AnnotatedElement} (in Java 8). 063 * 064 * <p>An annotation is <em>meta-present</em> on an element if the annotation 065 * is declared as a meta-annotation on some other annotation which is 066 * <em>present</em> on the element. Annotation {@code A} is <em>meta-present</em> 067 * on another annotation if {@code A} is either <em>directly present</em> or 068 * <em>meta-present</em> on the other annotation. 069 * 070 * <h3>Meta-annotation Support</h3> 071 * <p>Most {@code find*()} methods and some {@code get*()} methods in this class 072 * provide support for finding annotations used as meta-annotations. Consult the 073 * javadoc for each method in this class for details. For fine-grained support for 074 * meta-annotations with <em>attribute overrides</em> in <em>composed annotations</em>, 075 * consider using {@link AnnotatedElementUtils}'s more specific methods instead. 076 * 077 * <h3>Attribute Aliases</h3> 078 * <p>All public methods in this class that return annotations, arrays of 079 * annotations, or {@link AnnotationAttributes} transparently support attribute 080 * aliases configured via {@link AliasFor @AliasFor}. Consult the various 081 * {@code synthesizeAnnotation*(..)} methods for details. 082 * 083 * <h3>Search Scope</h3> 084 * <p>The search algorithms used by methods in this class stop searching for 085 * an annotation once the first annotation of the specified type has been 086 * found. As a consequence, additional annotations of the specified type will 087 * be silently ignored. 088 * 089 * @author Rob Harrop 090 * @author Juergen Hoeller 091 * @author Sam Brannen 092 * @author Mark Fisher 093 * @author Chris Beams 094 * @author Phillip Webb 095 * @author Oleg Zhurakousky 096 * @since 2.0 097 * @see AliasFor 098 * @see AnnotationAttributes 099 * @see AnnotatedElementUtils 100 * @see BridgeMethodResolver 101 * @see java.lang.reflect.AnnotatedElement#getAnnotations() 102 * @see java.lang.reflect.AnnotatedElement#getAnnotation(Class) 103 * @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotations() 104 */ 105public abstract class AnnotationUtils { 106 107 /** 108 * The attribute name for annotations with a single element. 109 */ 110 public static final String VALUE = MergedAnnotation.VALUE; 111 112 private static final AnnotationFilter JAVA_LANG_ANNOTATION_FILTER = 113 AnnotationFilter.packages("java.lang.annotation"); 114 115 private static final Map<Class<? extends Annotation>, Map<String, DefaultValueHolder>> defaultValuesCache = 116 new ConcurrentReferenceHashMap<>(); 117 118 119 /** 120 * Determine whether the given class is a candidate for carrying one of the specified 121 * annotations (at type, method or field level). 122 * @param clazz the class to introspect 123 * @param annotationTypes the searchable annotation types 124 * @return {@code false} if the class is known to have no such annotations at any level; 125 * {@code true} otherwise. Callers will usually perform full method/field introspection 126 * if {@code true} is being returned here. 127 * @since 5.2 128 * @see #isCandidateClass(Class, Class) 129 * @see #isCandidateClass(Class, String) 130 */ 131 public static boolean isCandidateClass(Class<?> clazz, Collection<Class<? extends Annotation>> annotationTypes) { 132 for (Class<? extends Annotation> annotationType : annotationTypes) { 133 if (isCandidateClass(clazz, annotationType)) { 134 return true; 135 } 136 } 137 return false; 138 } 139 140 /** 141 * Determine whether the given class is a candidate for carrying the specified annotation 142 * (at type, method or field level). 143 * @param clazz the class to introspect 144 * @param annotationType the searchable annotation type 145 * @return {@code false} if the class is known to have no such annotations at any level; 146 * {@code true} otherwise. Callers will usually perform full method/field introspection 147 * if {@code true} is being returned here. 148 * @since 5.2 149 * @see #isCandidateClass(Class, String) 150 */ 151 public static boolean isCandidateClass(Class<?> clazz, Class<? extends Annotation> annotationType) { 152 return isCandidateClass(clazz, annotationType.getName()); 153 } 154 155 /** 156 * Determine whether the given class is a candidate for carrying the specified annotation 157 * (at type, method or field level). 158 * @param clazz the class to introspect 159 * @param annotationName the fully-qualified name of the searchable annotation type 160 * @return {@code false} if the class is known to have no such annotations at any level; 161 * {@code true} otherwise. Callers will usually perform full method/field introspection 162 * if {@code true} is being returned here. 163 * @since 5.2 164 * @see #isCandidateClass(Class, Class) 165 */ 166 public static boolean isCandidateClass(Class<?> clazz, String annotationName) { 167 if (annotationName.startsWith("java.")) { 168 return true; 169 } 170 if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(clazz)) { 171 return false; 172 } 173 return true; 174 } 175 176 /** 177 * Get a single {@link Annotation} of {@code annotationType} from the supplied 178 * annotation: either the given annotation itself or a direct meta-annotation 179 * thereof. 180 * <p>Note that this method supports only a single level of meta-annotations. 181 * For support for arbitrary levels of meta-annotations, use one of the 182 * {@code find*()} methods instead. 183 * @param annotation the Annotation to check 184 * @param annotationType the annotation type to look for, both locally and as a meta-annotation 185 * @return the first matching annotation, or {@code null} if not found 186 * @since 4.0 187 */ 188 @SuppressWarnings("unchecked") 189 @Nullable 190 public static <A extends Annotation> A getAnnotation(Annotation annotation, Class<A> annotationType) { 191 // Shortcut: directly present on the element, with no merging needed? 192 if (annotationType.isInstance(annotation)) { 193 return synthesizeAnnotation((A) annotation, annotationType); 194 } 195 // Shortcut: no searchable annotations to be found on plain Java classes and core Spring types... 196 if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(annotation)) { 197 return null; 198 } 199 // Exhaustive retrieval of merged annotations... 200 return MergedAnnotations.from(annotation, new Annotation[] {annotation}, RepeatableContainers.none()) 201 .get(annotationType).withNonMergedAttributes() 202 .synthesize(AnnotationUtils::isSingleLevelPresent).orElse(null); 203 } 204 205 /** 206 * Get a single {@link Annotation} of {@code annotationType} from the supplied 207 * {@link AnnotatedElement}, where the annotation is either <em>present</em> or 208 * <em>meta-present</em> on the {@code AnnotatedElement}. 209 * <p>Note that this method supports only a single level of meta-annotations. 210 * For support for arbitrary levels of meta-annotations, use 211 * {@link #findAnnotation(AnnotatedElement, Class)} instead. 212 * @param annotatedElement the {@code AnnotatedElement} from which to get the annotation 213 * @param annotationType the annotation type to look for, both locally and as a meta-annotation 214 * @return the first matching annotation, or {@code null} if not found 215 * @since 3.1 216 */ 217 @Nullable 218 public static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) { 219 // Shortcut: directly present on the element, with no merging needed? 220 if (AnnotationFilter.PLAIN.matches(annotationType) || 221 AnnotationsScanner.hasPlainJavaAnnotationsOnly(annotatedElement)) { 222 return annotatedElement.getAnnotation(annotationType); 223 } 224 // Exhaustive retrieval of merged annotations... 225 return MergedAnnotations.from(annotatedElement, SearchStrategy.INHERITED_ANNOTATIONS, RepeatableContainers.none()) 226 .get(annotationType).withNonMergedAttributes() 227 .synthesize(AnnotationUtils::isSingleLevelPresent).orElse(null); 228 } 229 230 private static <A extends Annotation> boolean isSingleLevelPresent(MergedAnnotation<A> mergedAnnotation) { 231 int distance = mergedAnnotation.getDistance(); 232 return (distance == 0 || distance == 1); 233 } 234 235 /** 236 * Get a single {@link Annotation} of {@code annotationType} from the 237 * supplied {@link Method}, where the annotation is either <em>present</em> 238 * or <em>meta-present</em> on the method. 239 * <p>Correctly handles bridge {@link Method Methods} generated by the compiler. 240 * <p>Note that this method supports only a single level of meta-annotations. 241 * For support for arbitrary levels of meta-annotations, use 242 * {@link #findAnnotation(Method, Class)} instead. 243 * @param method the method to look for annotations on 244 * @param annotationType the annotation type to look for 245 * @return the first matching annotation, or {@code null} if not found 246 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method) 247 * @see #getAnnotation(AnnotatedElement, Class) 248 */ 249 @Nullable 250 public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType) { 251 Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method); 252 return getAnnotation((AnnotatedElement) resolvedMethod, annotationType); 253 } 254 255 /** 256 * Get all {@link Annotation Annotations} that are <em>present</em> on the 257 * supplied {@link AnnotatedElement}. 258 * <p>Meta-annotations will <em>not</em> be searched. 259 * @param annotatedElement the Method, Constructor or Field to retrieve annotations from 260 * @return the annotations found, an empty array, or {@code null} if not 261 * resolvable (e.g. because nested Class values in annotation attributes 262 * failed to resolve at runtime) 263 * @since 4.0.8 264 * @see AnnotatedElement#getAnnotations() 265 * @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API 266 */ 267 @Deprecated 268 @Nullable 269 public static Annotation[] getAnnotations(AnnotatedElement annotatedElement) { 270 try { 271 return synthesizeAnnotationArray(annotatedElement.getAnnotations(), annotatedElement); 272 } 273 catch (Throwable ex) { 274 handleIntrospectionFailure(annotatedElement, ex); 275 return null; 276 } 277 } 278 279 /** 280 * Get all {@link Annotation Annotations} that are <em>present</em> on the 281 * supplied {@link Method}. 282 * <p>Correctly handles bridge {@link Method Methods} generated by the compiler. 283 * <p>Meta-annotations will <em>not</em> be searched. 284 * @param method the Method to retrieve annotations from 285 * @return the annotations found, an empty array, or {@code null} if not 286 * resolvable (e.g. because nested Class values in annotation attributes 287 * failed to resolve at runtime) 288 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method) 289 * @see AnnotatedElement#getAnnotations() 290 * @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API 291 */ 292 @Deprecated 293 @Nullable 294 public static Annotation[] getAnnotations(Method method) { 295 try { 296 return synthesizeAnnotationArray(BridgeMethodResolver.findBridgedMethod(method).getAnnotations(), method); 297 } 298 catch (Throwable ex) { 299 handleIntrospectionFailure(method, ex); 300 return null; 301 } 302 } 303 304 /** 305 * Get the <em>repeatable</em> {@linkplain Annotation annotations} of 306 * {@code annotationType} from the supplied {@link AnnotatedElement}, where 307 * such annotations are either <em>present</em>, <em>indirectly present</em>, 308 * or <em>meta-present</em> on the element. 309 * <p>This method mimics the functionality of Java 8's 310 * {@link java.lang.reflect.AnnotatedElement#getAnnotationsByType(Class)} 311 * with support for automatic detection of a <em>container annotation</em> 312 * declared via @{@link java.lang.annotation.Repeatable} (when running on 313 * Java 8 or higher) and with additional support for meta-annotations. 314 * <p>Handles both single annotations and annotations nested within a 315 * <em>container annotation</em>. 316 * <p>Correctly handles <em>bridge methods</em> generated by the 317 * compiler if the supplied element is a {@link Method}. 318 * <p>Meta-annotations will be searched if the annotation is not 319 * <em>present</em> on the supplied element. 320 * @param annotatedElement the element to look for annotations on 321 * @param annotationType the annotation type to look for 322 * @return the annotations found or an empty set (never {@code null}) 323 * @since 4.2 324 * @see #getRepeatableAnnotations(AnnotatedElement, Class, Class) 325 * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class) 326 * @see AnnotatedElementUtils#getMergedRepeatableAnnotations(AnnotatedElement, Class) 327 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod 328 * @see java.lang.annotation.Repeatable 329 * @see java.lang.reflect.AnnotatedElement#getAnnotationsByType 330 * @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API 331 */ 332 @Deprecated 333 public static <A extends Annotation> Set<A> getRepeatableAnnotations(AnnotatedElement annotatedElement, 334 Class<A> annotationType) { 335 336 return getRepeatableAnnotations(annotatedElement, annotationType, null); 337 } 338 339 /** 340 * Get the <em>repeatable</em> {@linkplain Annotation annotations} of 341 * {@code annotationType} from the supplied {@link AnnotatedElement}, where 342 * such annotations are either <em>present</em>, <em>indirectly present</em>, 343 * or <em>meta-present</em> on the element. 344 * <p>This method mimics the functionality of Java 8's 345 * {@link java.lang.reflect.AnnotatedElement#getAnnotationsByType(Class)} 346 * with additional support for meta-annotations. 347 * <p>Handles both single annotations and annotations nested within a 348 * <em>container annotation</em>. 349 * <p>Correctly handles <em>bridge methods</em> generated by the 350 * compiler if the supplied element is a {@link Method}. 351 * <p>Meta-annotations will be searched if the annotation is not 352 * <em>present</em> on the supplied element. 353 * @param annotatedElement the element to look for annotations on 354 * @param annotationType the annotation type to look for 355 * @param containerAnnotationType the type of the container that holds 356 * the annotations; may be {@code null} if a container is not supported 357 * or if it should be looked up via @{@link java.lang.annotation.Repeatable} 358 * when running on Java 8 or higher 359 * @return the annotations found or an empty set (never {@code null}) 360 * @since 4.2 361 * @see #getRepeatableAnnotations(AnnotatedElement, Class) 362 * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class) 363 * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class) 364 * @see AnnotatedElementUtils#getMergedRepeatableAnnotations(AnnotatedElement, Class, Class) 365 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod 366 * @see java.lang.annotation.Repeatable 367 * @see java.lang.reflect.AnnotatedElement#getAnnotationsByType 368 * @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API 369 */ 370 @Deprecated 371 public static <A extends Annotation> Set<A> getRepeatableAnnotations(AnnotatedElement annotatedElement, 372 Class<A> annotationType, @Nullable Class<? extends Annotation> containerAnnotationType) { 373 374 RepeatableContainers repeatableContainers = (containerAnnotationType != null ? 375 RepeatableContainers.of(annotationType, containerAnnotationType) : 376 RepeatableContainers.standardRepeatables()); 377 378 return MergedAnnotations.from(annotatedElement, SearchStrategy.SUPERCLASS, repeatableContainers) 379 .stream(annotationType) 380 .filter(MergedAnnotationPredicates.firstRunOf(MergedAnnotation::getAggregateIndex)) 381 .map(MergedAnnotation::withNonMergedAttributes) 382 .collect(MergedAnnotationCollectors.toAnnotationSet()); 383 } 384 385 /** 386 * Get the declared <em>repeatable</em> {@linkplain Annotation annotations} 387 * of {@code annotationType} from the supplied {@link AnnotatedElement}, 388 * where such annotations are either <em>directly present</em>, 389 * <em>indirectly present</em>, or <em>meta-present</em> on the element. 390 * <p>This method mimics the functionality of Java 8's 391 * {@link java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType(Class)} 392 * with support for automatic detection of a <em>container annotation</em> 393 * declared via @{@link java.lang.annotation.Repeatable} (when running on 394 * Java 8 or higher) and with additional support for meta-annotations. 395 * <p>Handles both single annotations and annotations nested within a 396 * <em>container annotation</em>. 397 * <p>Correctly handles <em>bridge methods</em> generated by the 398 * compiler if the supplied element is a {@link Method}. 399 * <p>Meta-annotations will be searched if the annotation is not 400 * <em>present</em> on the supplied element. 401 * @param annotatedElement the element to look for annotations on 402 * @param annotationType the annotation type to look for 403 * @return the annotations found or an empty set (never {@code null}) 404 * @since 4.2 405 * @see #getRepeatableAnnotations(AnnotatedElement, Class) 406 * @see #getRepeatableAnnotations(AnnotatedElement, Class, Class) 407 * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class) 408 * @see AnnotatedElementUtils#getMergedRepeatableAnnotations(AnnotatedElement, Class) 409 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod 410 * @see java.lang.annotation.Repeatable 411 * @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType 412 * @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API 413 */ 414 @Deprecated 415 public static <A extends Annotation> Set<A> getDeclaredRepeatableAnnotations(AnnotatedElement annotatedElement, 416 Class<A> annotationType) { 417 418 return getDeclaredRepeatableAnnotations(annotatedElement, annotationType, null); 419 } 420 421 /** 422 * Get the declared <em>repeatable</em> {@linkplain Annotation annotations} 423 * of {@code annotationType} from the supplied {@link AnnotatedElement}, 424 * where such annotations are either <em>directly present</em>, 425 * <em>indirectly present</em>, or <em>meta-present</em> on the element. 426 * <p>This method mimics the functionality of Java 8's 427 * {@link java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType(Class)} 428 * with additional support for meta-annotations. 429 * <p>Handles both single annotations and annotations nested within a 430 * <em>container annotation</em>. 431 * <p>Correctly handles <em>bridge methods</em> generated by the 432 * compiler if the supplied element is a {@link Method}. 433 * <p>Meta-annotations will be searched if the annotation is not 434 * <em>present</em> on the supplied element. 435 * @param annotatedElement the element to look for annotations on 436 * @param annotationType the annotation type to look for 437 * @param containerAnnotationType the type of the container that holds 438 * the annotations; may be {@code null} if a container is not supported 439 * or if it should be looked up via @{@link java.lang.annotation.Repeatable} 440 * when running on Java 8 or higher 441 * @return the annotations found or an empty set (never {@code null}) 442 * @since 4.2 443 * @see #getRepeatableAnnotations(AnnotatedElement, Class) 444 * @see #getRepeatableAnnotations(AnnotatedElement, Class, Class) 445 * @see #getDeclaredRepeatableAnnotations(AnnotatedElement, Class) 446 * @see AnnotatedElementUtils#getMergedRepeatableAnnotations(AnnotatedElement, Class, Class) 447 * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod 448 * @see java.lang.annotation.Repeatable 449 * @see java.lang.reflect.AnnotatedElement#getDeclaredAnnotationsByType 450 * @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API 451 */ 452 @Deprecated 453 public static <A extends Annotation> Set<A> getDeclaredRepeatableAnnotations(AnnotatedElement annotatedElement, 454 Class<A> annotationType, @Nullable Class<? extends Annotation> containerAnnotationType) { 455 456 RepeatableContainers repeatableContainers = containerAnnotationType != null ? 457 RepeatableContainers.of(annotationType, containerAnnotationType) : 458 RepeatableContainers.standardRepeatables(); 459 460 return MergedAnnotations.from(annotatedElement, SearchStrategy.DIRECT, repeatableContainers) 461 .stream(annotationType) 462 .map(MergedAnnotation::withNonMergedAttributes) 463 .collect(MergedAnnotationCollectors.toAnnotationSet()); 464 } 465 466 /** 467 * Find a single {@link Annotation} of {@code annotationType} on the 468 * supplied {@link AnnotatedElement}. 469 * <p>Meta-annotations will be searched if the annotation is not 470 * <em>directly present</em> on the supplied element. 471 * <p><strong>Warning</strong>: this method operates generically on 472 * annotated elements. In other words, this method does not execute 473 * specialized search algorithms for classes or methods. If you require 474 * the more specific semantics of {@link #findAnnotation(Class, Class)} 475 * or {@link #findAnnotation(Method, Class)}, invoke one of those methods 476 * instead. 477 * @param annotatedElement the {@code AnnotatedElement} on which to find the annotation 478 * @param annotationType the annotation type to look for, both locally and as a meta-annotation 479 * @return the first matching annotation, or {@code null} if not found 480 * @since 4.2 481 */ 482 @Nullable 483 public static <A extends Annotation> A findAnnotation( 484 AnnotatedElement annotatedElement, @Nullable Class<A> annotationType) { 485 486 if (annotationType == null) { 487 return null; 488 } 489 490 // Shortcut: directly present on the element, with no merging needed? 491 if (AnnotationFilter.PLAIN.matches(annotationType) || 492 AnnotationsScanner.hasPlainJavaAnnotationsOnly(annotatedElement)) { 493 return annotatedElement.getDeclaredAnnotation(annotationType); 494 } 495 496 // Exhaustive retrieval of merged annotations... 497 return MergedAnnotations.from(annotatedElement, SearchStrategy.INHERITED_ANNOTATIONS, RepeatableContainers.none()) 498 .get(annotationType).withNonMergedAttributes() 499 .synthesize(MergedAnnotation::isPresent).orElse(null); 500 } 501 502 /** 503 * Find a single {@link Annotation} of {@code annotationType} on the supplied 504 * {@link Method}, traversing its super methods (i.e. from superclasses and 505 * interfaces) if the annotation is not <em>directly present</em> on the given 506 * method itself. 507 * <p>Correctly handles bridge {@link Method Methods} generated by the compiler. 508 * <p>Meta-annotations will be searched if the annotation is not 509 * <em>directly present</em> on the method. 510 * <p>Annotations on methods are not inherited by default, so we need to handle 511 * this explicitly. 512 * @param method the method to look for annotations on 513 * @param annotationType the annotation type to look for 514 * @return the first matching annotation, or {@code null} if not found 515 * @see #getAnnotation(Method, Class) 516 */ 517 @Nullable 518 public static <A extends Annotation> A findAnnotation(Method method, @Nullable Class<A> annotationType) { 519 if (annotationType == null) { 520 return null; 521 } 522 523 // Shortcut: directly present on the element, with no merging needed? 524 if (AnnotationFilter.PLAIN.matches(annotationType) || 525 AnnotationsScanner.hasPlainJavaAnnotationsOnly(method)) { 526 return method.getDeclaredAnnotation(annotationType); 527 } 528 529 // Exhaustive retrieval of merged annotations... 530 return MergedAnnotations.from(method, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.none()) 531 .get(annotationType).withNonMergedAttributes() 532 .synthesize(MergedAnnotation::isPresent).orElse(null); 533 } 534 535 /** 536 * Find a single {@link Annotation} of {@code annotationType} on the 537 * supplied {@link Class}, traversing its interfaces, annotations, and 538 * superclasses if the annotation is not <em>directly present</em> on 539 * the given class itself. 540 * <p>This method explicitly handles class-level annotations which are not 541 * declared as {@link java.lang.annotation.Inherited inherited} <em>as well 542 * as meta-annotations and annotations on interfaces</em>. 543 * <p>The algorithm operates as follows: 544 * <ol> 545 * <li>Search for the annotation on the given class and return it if found. 546 * <li>Recursively search through all annotations that the given class declares. 547 * <li>Recursively search through all interfaces that the given class declares. 548 * <li>Recursively search through the superclass hierarchy of the given class. 549 * </ol> 550 * <p>Note: in this context, the term <em>recursively</em> means that the search 551 * process continues by returning to step #1 with the current interface, 552 * annotation, or superclass as the class to look for annotations on. 553 * @param clazz the class to look for annotations on 554 * @param annotationType the type of annotation to look for 555 * @return the first matching annotation, or {@code null} if not found 556 */ 557 @Nullable 558 public static <A extends Annotation> A findAnnotation(Class<?> clazz, @Nullable Class<A> annotationType) { 559 if (annotationType == null) { 560 return null; 561 } 562 563 // Shortcut: directly present on the element, with no merging needed? 564 if (AnnotationFilter.PLAIN.matches(annotationType) || 565 AnnotationsScanner.hasPlainJavaAnnotationsOnly(clazz)) { 566 A annotation = clazz.getDeclaredAnnotation(annotationType); 567 if (annotation != null) { 568 return annotation; 569 } 570 // For backwards compatibility, perform a superclass search with plain annotations 571 // even if not marked as @Inherited: e.g. a findAnnotation search for @Deprecated 572 Class<?> superclass = clazz.getSuperclass(); 573 if (superclass == null || superclass == Object.class) { 574 return null; 575 } 576 return findAnnotation(superclass, annotationType); 577 } 578 579 // Exhaustive retrieval of merged annotations... 580 return MergedAnnotations.from(clazz, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.none()) 581 .get(annotationType).withNonMergedAttributes() 582 .synthesize(MergedAnnotation::isPresent).orElse(null); 583 } 584 585 /** 586 * Find the first {@link Class} in the inheritance hierarchy of the 587 * specified {@code clazz} (including the specified {@code clazz} itself) 588 * on which an annotation of the specified {@code annotationType} is 589 * <em>directly present</em>. 590 * <p>If the supplied {@code clazz} is an interface, only the interface 591 * itself will be checked; the inheritance hierarchy for interfaces will 592 * not be traversed. 593 * <p>Meta-annotations will <em>not</em> be searched. 594 * <p>The standard {@link Class} API does not provide a mechanism for 595 * determining which class in an inheritance hierarchy actually declares 596 * an {@link Annotation}, so we need to handle this explicitly. 597 * @param annotationType the annotation type to look for 598 * @param clazz the class to check for the annotation on (may be {@code null}) 599 * @return the first {@link Class} in the inheritance hierarchy that 600 * declares an annotation of the specified {@code annotationType}, 601 * or {@code null} if not found 602 * @see Class#isAnnotationPresent(Class) 603 * @see Class#getDeclaredAnnotations() 604 * @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API 605 */ 606 @Deprecated 607 @Nullable 608 public static Class<?> findAnnotationDeclaringClass( 609 Class<? extends Annotation> annotationType, @Nullable Class<?> clazz) { 610 611 if (clazz == null) { 612 return null; 613 } 614 615 return (Class<?>) MergedAnnotations.from(clazz, SearchStrategy.SUPERCLASS) 616 .get(annotationType, MergedAnnotation::isDirectlyPresent) 617 .getSource(); 618 } 619 620 /** 621 * Find the first {@link Class} in the inheritance hierarchy of the 622 * specified {@code clazz} (including the specified {@code clazz} itself) 623 * on which at least one of the specified {@code annotationTypes} is 624 * <em>directly present</em>. 625 * <p>If the supplied {@code clazz} is an interface, only the interface 626 * itself will be checked; the inheritance hierarchy for interfaces will 627 * not be traversed. 628 * <p>Meta-annotations will <em>not</em> be searched. 629 * <p>The standard {@link Class} API does not provide a mechanism for 630 * determining which class in an inheritance hierarchy actually declares 631 * one of several candidate {@linkplain Annotation annotations}, so we 632 * need to handle this explicitly. 633 * @param annotationTypes the annotation types to look for 634 * @param clazz the class to check for the annotation on (may be {@code null}) 635 * @return the first {@link Class} in the inheritance hierarchy that 636 * declares an annotation of at least one of the specified 637 * {@code annotationTypes}, or {@code null} if not found 638 * @since 3.2.2 639 * @see Class#isAnnotationPresent(Class) 640 * @see Class#getDeclaredAnnotations() 641 * @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API 642 */ 643 @Deprecated 644 @Nullable 645 public static Class<?> findAnnotationDeclaringClassForTypes( 646 List<Class<? extends Annotation>> annotationTypes, @Nullable Class<?> clazz) { 647 648 if (clazz == null) { 649 return null; 650 } 651 652 return (Class<?>) MergedAnnotations.from(clazz, SearchStrategy.SUPERCLASS) 653 .stream() 654 .filter(MergedAnnotationPredicates.typeIn(annotationTypes).and(MergedAnnotation::isDirectlyPresent)) 655 .map(MergedAnnotation::getSource) 656 .findFirst().orElse(null); 657 } 658 659 /** 660 * Determine whether an annotation of the specified {@code annotationType} 661 * is declared locally (i.e. <em>directly present</em>) on the supplied 662 * {@code clazz}. 663 * <p>The supplied {@link Class} may represent any type. 664 * <p>Meta-annotations will <em>not</em> be searched. 665 * <p>Note: This method does <strong>not</strong> determine if the annotation 666 * is {@linkplain java.lang.annotation.Inherited inherited}. 667 * @param annotationType the annotation type to look for 668 * @param clazz the class to check for the annotation on 669 * @return {@code true} if an annotation of the specified {@code annotationType} 670 * is <em>directly present</em> 671 * @see java.lang.Class#getDeclaredAnnotations() 672 * @see java.lang.Class#getDeclaredAnnotation(Class) 673 */ 674 public static boolean isAnnotationDeclaredLocally(Class<? extends Annotation> annotationType, Class<?> clazz) { 675 return MergedAnnotations.from(clazz).get(annotationType).isDirectlyPresent(); 676 } 677 678 /** 679 * Determine whether an annotation of the specified {@code annotationType} 680 * is <em>present</em> on the supplied {@code clazz} and is 681 * {@linkplain java.lang.annotation.Inherited inherited} 682 * (i.e. not <em>directly present</em>). 683 * <p>Meta-annotations will <em>not</em> be searched. 684 * <p>If the supplied {@code clazz} is an interface, only the interface 685 * itself will be checked. In accordance with standard meta-annotation 686 * semantics in Java, the inheritance hierarchy for interfaces will not 687 * be traversed. See the {@linkplain java.lang.annotation.Inherited javadoc} 688 * for the {@code @Inherited} meta-annotation for further details regarding 689 * annotation inheritance. 690 * @param annotationType the annotation type to look for 691 * @param clazz the class to check for the annotation on 692 * @return {@code true} if an annotation of the specified {@code annotationType} 693 * is <em>present</em> and <em>inherited</em> 694 * @see Class#isAnnotationPresent(Class) 695 * @see #isAnnotationDeclaredLocally(Class, Class) 696 * @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API 697 */ 698 @Deprecated 699 public static boolean isAnnotationInherited(Class<? extends Annotation> annotationType, Class<?> clazz) { 700 return MergedAnnotations.from(clazz, SearchStrategy.INHERITED_ANNOTATIONS) 701 .stream(annotationType) 702 .filter(MergedAnnotation::isDirectlyPresent) 703 .findFirst().orElseGet(MergedAnnotation::missing) 704 .getAggregateIndex() > 0; 705 } 706 707 /** 708 * Determine if an annotation of type {@code metaAnnotationType} is 709 * <em>meta-present</em> on the supplied {@code annotationType}. 710 * @param annotationType the annotation type to search on 711 * @param metaAnnotationType the type of meta-annotation to search for 712 * @return {@code true} if such an annotation is meta-present 713 * @since 4.2.1 714 * @deprecated as of 5.2 since it is superseded by the {@link MergedAnnotations} API 715 */ 716 @Deprecated 717 public static boolean isAnnotationMetaPresent(Class<? extends Annotation> annotationType, 718 @Nullable Class<? extends Annotation> metaAnnotationType) { 719 720 if (metaAnnotationType == null) { 721 return false; 722 } 723 // Shortcut: directly present on the element, with no merging needed? 724 if (AnnotationFilter.PLAIN.matches(metaAnnotationType) || 725 AnnotationsScanner.hasPlainJavaAnnotationsOnly(annotationType)) { 726 return annotationType.isAnnotationPresent(metaAnnotationType); 727 } 728 // Exhaustive retrieval of merged annotations... 729 return MergedAnnotations.from(annotationType, SearchStrategy.INHERITED_ANNOTATIONS, 730 RepeatableContainers.none()).isPresent(metaAnnotationType); 731 } 732 733 /** 734 * Determine if the supplied {@link Annotation} is defined in the core JDK 735 * {@code java.lang.annotation} package. 736 * @param annotation the annotation to check 737 * @return {@code true} if the annotation is in the {@code java.lang.annotation} package 738 */ 739 public static boolean isInJavaLangAnnotationPackage(@Nullable Annotation annotation) { 740 return (annotation != null && JAVA_LANG_ANNOTATION_FILTER.matches(annotation)); 741 } 742 743 /** 744 * Determine if the {@link Annotation} with the supplied name is defined 745 * in the core JDK {@code java.lang.annotation} package. 746 * @param annotationType the name of the annotation type to check 747 * @return {@code true} if the annotation is in the {@code java.lang.annotation} package 748 * @since 4.2 749 */ 750 public static boolean isInJavaLangAnnotationPackage(@Nullable String annotationType) { 751 return (annotationType != null && JAVA_LANG_ANNOTATION_FILTER.matches(annotationType)); 752 } 753 754 /** 755 * Check the declared attributes of the given annotation, in particular covering 756 * Google App Engine's late arrival of {@code TypeNotPresentExceptionProxy} for 757 * {@code Class} values (instead of early {@code Class.getAnnotations() failure}. 758 * <p>This method not failing indicates that {@link #getAnnotationAttributes(Annotation)} 759 * won't failure either (when attempted later on). 760 * @param annotation the annotation to validate 761 * @throws IllegalStateException if a declared {@code Class} attribute could not be read 762 * @since 4.3.15 763 * @see Class#getAnnotations() 764 * @see #getAnnotationAttributes(Annotation) 765 */ 766 public static void validateAnnotation(Annotation annotation) { 767 AttributeMethods.forAnnotationType(annotation.annotationType()).validate(annotation); 768 } 769 770 /** 771 * Retrieve the given annotation's attributes as a {@link Map}, preserving all 772 * attribute types. 773 * <p>Equivalent to calling {@link #getAnnotationAttributes(Annotation, boolean, boolean)} 774 * with the {@code classValuesAsString} and {@code nestedAnnotationsAsMap} parameters 775 * set to {@code false}. 776 * <p>Note: This method actually returns an {@link AnnotationAttributes} instance. 777 * However, the {@code Map} signature has been preserved for binary compatibility. 778 * @param annotation the annotation to retrieve the attributes for 779 * @return the Map of annotation attributes, with attribute names as keys and 780 * corresponding attribute values as values (never {@code null}) 781 * @see #getAnnotationAttributes(AnnotatedElement, Annotation) 782 * @see #getAnnotationAttributes(Annotation, boolean, boolean) 783 * @see #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean) 784 */ 785 public static Map<String, Object> getAnnotationAttributes(Annotation annotation) { 786 return getAnnotationAttributes(null, annotation); 787 } 788 789 /** 790 * Retrieve the given annotation's attributes as a {@link Map}. 791 * <p>Equivalent to calling {@link #getAnnotationAttributes(Annotation, boolean, boolean)} 792 * with the {@code nestedAnnotationsAsMap} parameter set to {@code false}. 793 * <p>Note: This method actually returns an {@link AnnotationAttributes} instance. 794 * However, the {@code Map} signature has been preserved for binary compatibility. 795 * @param annotation the annotation to retrieve the attributes for 796 * @param classValuesAsString whether to convert Class references into Strings (for 797 * compatibility with {@link org.springframework.core.type.AnnotationMetadata}) 798 * or to preserve them as Class references 799 * @return the Map of annotation attributes, with attribute names as keys and 800 * corresponding attribute values as values (never {@code null}) 801 * @see #getAnnotationAttributes(Annotation, boolean, boolean) 802 */ 803 public static Map<String, Object> getAnnotationAttributes( 804 Annotation annotation, boolean classValuesAsString) { 805 806 return getAnnotationAttributes(annotation, classValuesAsString, false); 807 } 808 809 /** 810 * Retrieve the given annotation's attributes as an {@link AnnotationAttributes} map. 811 * <p>This method provides fully recursive annotation reading capabilities on par with 812 * the reflection-based {@link org.springframework.core.type.StandardAnnotationMetadata}. 813 * @param annotation the annotation to retrieve the attributes for 814 * @param classValuesAsString whether to convert Class references into Strings (for 815 * compatibility with {@link org.springframework.core.type.AnnotationMetadata}) 816 * or to preserve them as Class references 817 * @param nestedAnnotationsAsMap whether to convert nested annotations into 818 * {@link AnnotationAttributes} maps (for compatibility with 819 * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as 820 * {@code Annotation} instances 821 * @return the annotation attributes (a specialized Map) with attribute names as keys 822 * and corresponding attribute values as values (never {@code null}) 823 * @since 3.1.1 824 */ 825 public static AnnotationAttributes getAnnotationAttributes( 826 Annotation annotation, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { 827 828 return getAnnotationAttributes(null, annotation, classValuesAsString, nestedAnnotationsAsMap); 829 } 830 831 /** 832 * Retrieve the given annotation's attributes as an {@link AnnotationAttributes} map. 833 * <p>Equivalent to calling {@link #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean)} 834 * with the {@code classValuesAsString} and {@code nestedAnnotationsAsMap} parameters 835 * set to {@code false}. 836 * @param annotatedElement the element that is annotated with the supplied annotation; 837 * may be {@code null} if unknown 838 * @param annotation the annotation to retrieve the attributes for 839 * @return the annotation attributes (a specialized Map) with attribute names as keys 840 * and corresponding attribute values as values (never {@code null}) 841 * @since 4.2 842 * @see #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean) 843 */ 844 public static AnnotationAttributes getAnnotationAttributes( 845 @Nullable AnnotatedElement annotatedElement, Annotation annotation) { 846 847 return getAnnotationAttributes(annotatedElement, annotation, false, false); 848 } 849 850 /** 851 * Retrieve the given annotation's attributes as an {@link AnnotationAttributes} map. 852 * <p>This method provides fully recursive annotation reading capabilities on par with 853 * the reflection-based {@link org.springframework.core.type.StandardAnnotationMetadata}. 854 * @param annotatedElement the element that is annotated with the supplied annotation; 855 * may be {@code null} if unknown 856 * @param annotation the annotation to retrieve the attributes for 857 * @param classValuesAsString whether to convert Class references into Strings (for 858 * compatibility with {@link org.springframework.core.type.AnnotationMetadata}) 859 * or to preserve them as Class references 860 * @param nestedAnnotationsAsMap whether to convert nested annotations into 861 * {@link AnnotationAttributes} maps (for compatibility with 862 * {@link org.springframework.core.type.AnnotationMetadata}) or to preserve them as 863 * {@code Annotation} instances 864 * @return the annotation attributes (a specialized Map) with attribute names as keys 865 * and corresponding attribute values as values (never {@code null}) 866 * @since 4.2 867 */ 868 public static AnnotationAttributes getAnnotationAttributes( 869 @Nullable AnnotatedElement annotatedElement, Annotation annotation, 870 boolean classValuesAsString, boolean nestedAnnotationsAsMap) { 871 872 Adapt[] adaptations = Adapt.values(classValuesAsString, nestedAnnotationsAsMap); 873 return MergedAnnotation.from(annotatedElement, annotation) 874 .withNonMergedAttributes() 875 .asMap(mergedAnnotation -> 876 new AnnotationAttributes(mergedAnnotation.getType(), true), adaptations); 877 } 878 879 /** 880 * Register the annotation-declared default values for the given attributes, 881 * if available. 882 * @param attributes the annotation attributes to process 883 * @since 4.3.2 884 */ 885 public static void registerDefaultValues(AnnotationAttributes attributes) { 886 Class<? extends Annotation> annotationType = attributes.annotationType(); 887 if (annotationType != null && Modifier.isPublic(annotationType.getModifiers()) && 888 !AnnotationFilter.PLAIN.matches(annotationType)) { 889 Map<String, DefaultValueHolder> defaultValues = getDefaultValues(annotationType); 890 defaultValues.forEach(attributes::putIfAbsent); 891 } 892 } 893 894 private static Map<String, DefaultValueHolder> getDefaultValues( 895 Class<? extends Annotation> annotationType) { 896 897 return defaultValuesCache.computeIfAbsent(annotationType, 898 AnnotationUtils::computeDefaultValues); 899 } 900 901 private static Map<String, DefaultValueHolder> computeDefaultValues( 902 Class<? extends Annotation> annotationType) { 903 904 AttributeMethods methods = AttributeMethods.forAnnotationType(annotationType); 905 if (!methods.hasDefaultValueMethod()) { 906 return Collections.emptyMap(); 907 } 908 Map<String, DefaultValueHolder> result = new LinkedHashMap<>(methods.size()); 909 if (!methods.hasNestedAnnotation()) { 910 // Use simpler method if there are no nested annotations 911 for (int i = 0; i < methods.size(); i++) { 912 Method method = methods.get(i); 913 Object defaultValue = method.getDefaultValue(); 914 if (defaultValue != null) { 915 result.put(method.getName(), new DefaultValueHolder(defaultValue)); 916 } 917 } 918 } 919 else { 920 // If we have nested annotations, we need them as nested maps 921 AnnotationAttributes attributes = MergedAnnotation.of(annotationType) 922 .asMap(annotation -> 923 new AnnotationAttributes(annotation.getType(), true), Adapt.ANNOTATION_TO_MAP); 924 for (Map.Entry<String, Object> element : attributes.entrySet()) { 925 result.put(element.getKey(), new DefaultValueHolder(element.getValue())); 926 } 927 } 928 return result; 929 } 930 931 /** 932 * Post-process the supplied {@link AnnotationAttributes}, preserving nested 933 * annotations as {@code Annotation} instances. 934 * <p>Specifically, this method enforces <em>attribute alias</em> semantics 935 * for annotation attributes that are annotated with {@link AliasFor @AliasFor} 936 * and replaces default value placeholders with their original default values. 937 * @param annotatedElement the element that is annotated with an annotation or 938 * annotation hierarchy from which the supplied attributes were created; 939 * may be {@code null} if unknown 940 * @param attributes the annotation attributes to post-process 941 * @param classValuesAsString whether to convert Class references into Strings (for 942 * compatibility with {@link org.springframework.core.type.AnnotationMetadata}) 943 * or to preserve them as Class references 944 * @since 4.3.2 945 * @see #getDefaultValue(Class, String) 946 */ 947 public static void postProcessAnnotationAttributes(@Nullable Object annotatedElement, 948 @Nullable AnnotationAttributes attributes, boolean classValuesAsString) { 949 950 if (attributes == null) { 951 return; 952 } 953 if (!attributes.validated) { 954 Class<? extends Annotation> annotationType = attributes.annotationType(); 955 if (annotationType == null) { 956 return; 957 } 958 AnnotationTypeMapping mapping = AnnotationTypeMappings.forAnnotationType(annotationType).get(0); 959 for (int i = 0; i < mapping.getMirrorSets().size(); i++) { 960 MirrorSet mirrorSet = mapping.getMirrorSets().get(i); 961 int resolved = mirrorSet.resolve(attributes.displayName, attributes, 962 AnnotationUtils::getAttributeValueForMirrorResolution); 963 if (resolved != -1) { 964 Method attribute = mapping.getAttributes().get(resolved); 965 Object value = attributes.get(attribute.getName()); 966 for (int j = 0; j < mirrorSet.size(); j++) { 967 Method mirror = mirrorSet.get(j); 968 if (mirror != attribute) { 969 attributes.put(mirror.getName(), 970 adaptValue(annotatedElement, value, classValuesAsString)); 971 } 972 } 973 } 974 } 975 } 976 for (Map.Entry<String, Object> attributeEntry : attributes.entrySet()) { 977 String attributeName = attributeEntry.getKey(); 978 Object value = attributeEntry.getValue(); 979 if (value instanceof DefaultValueHolder) { 980 value = ((DefaultValueHolder) value).defaultValue; 981 attributes.put(attributeName, 982 adaptValue(annotatedElement, value, classValuesAsString)); 983 } 984 } 985 } 986 987 private static Object getAttributeValueForMirrorResolution(Method attribute, Object attributes) { 988 Object result = ((AnnotationAttributes) attributes).get(attribute.getName()); 989 return (result instanceof DefaultValueHolder ? ((DefaultValueHolder) result).defaultValue : result); 990 } 991 992 @Nullable 993 private static Object adaptValue( 994 @Nullable Object annotatedElement, @Nullable Object value, boolean classValuesAsString) { 995 996 if (classValuesAsString) { 997 if (value instanceof Class) { 998 return ((Class<?>) value).getName(); 999 } 1000 if (value instanceof Class[]) { 1001 Class<?>[] classes = (Class<?>[]) value; 1002 String[] names = new String[classes.length]; 1003 for (int i = 0; i < classes.length; i++) { 1004 names[i] = classes[i].getName(); 1005 } 1006 return names; 1007 } 1008 } 1009 if (value instanceof Annotation) { 1010 Annotation annotation = (Annotation) value; 1011 return MergedAnnotation.from(annotatedElement, annotation).synthesize(); 1012 } 1013 if (value instanceof Annotation[]) { 1014 Annotation[] annotations = (Annotation[]) value; 1015 Annotation[] synthesized = (Annotation[]) Array.newInstance( 1016 annotations.getClass().getComponentType(), annotations.length); 1017 for (int i = 0; i < annotations.length; i++) { 1018 synthesized[i] = MergedAnnotation.from(annotatedElement, annotations[i]).synthesize(); 1019 } 1020 return synthesized; 1021 } 1022 return value; 1023 } 1024 1025 /** 1026 * Retrieve the <em>value</em> of the {@code value} attribute of a 1027 * single-element Annotation, given an annotation instance. 1028 * @param annotation the annotation instance from which to retrieve the value 1029 * @return the attribute value, or {@code null} if not found unless the attribute 1030 * value cannot be retrieved due to an {@link AnnotationConfigurationException}, 1031 * in which case such an exception will be rethrown 1032 * @see #getValue(Annotation, String) 1033 */ 1034 @Nullable 1035 public static Object getValue(Annotation annotation) { 1036 return getValue(annotation, VALUE); 1037 } 1038 1039 /** 1040 * Retrieve the <em>value</em> of a named attribute, given an annotation instance. 1041 * @param annotation the annotation instance from which to retrieve the value 1042 * @param attributeName the name of the attribute value to retrieve 1043 * @return the attribute value, or {@code null} if not found unless the attribute 1044 * value cannot be retrieved due to an {@link AnnotationConfigurationException}, 1045 * in which case such an exception will be rethrown 1046 * @see #getValue(Annotation) 1047 */ 1048 @Nullable 1049 public static Object getValue(@Nullable Annotation annotation, @Nullable String attributeName) { 1050 if (annotation == null || !StringUtils.hasText(attributeName)) { 1051 return null; 1052 } 1053 try { 1054 Method method = annotation.annotationType().getDeclaredMethod(attributeName); 1055 ReflectionUtils.makeAccessible(method); 1056 return method.invoke(annotation); 1057 } 1058 catch (NoSuchMethodException ex) { 1059 return null; 1060 } 1061 catch (InvocationTargetException ex) { 1062 rethrowAnnotationConfigurationException(ex.getTargetException()); 1063 throw new IllegalStateException("Could not obtain value for annotation attribute '" + 1064 attributeName + "' in " + annotation, ex); 1065 } 1066 catch (Throwable ex) { 1067 handleIntrospectionFailure(annotation.getClass(), ex); 1068 return null; 1069 } 1070 } 1071 1072 /** 1073 * If the supplied throwable is an {@link AnnotationConfigurationException}, 1074 * it will be cast to an {@code AnnotationConfigurationException} and thrown, 1075 * allowing it to propagate to the caller. 1076 * <p>Otherwise, this method does nothing. 1077 * @param ex the throwable to inspect 1078 */ 1079 static void rethrowAnnotationConfigurationException(Throwable ex) { 1080 if (ex instanceof AnnotationConfigurationException) { 1081 throw (AnnotationConfigurationException) ex; 1082 } 1083 } 1084 1085 /** 1086 * Handle the supplied annotation introspection exception. 1087 * <p>If the supplied exception is an {@link AnnotationConfigurationException}, 1088 * it will simply be thrown, allowing it to propagate to the caller, and 1089 * nothing will be logged. 1090 * <p>Otherwise, this method logs an introspection failure (in particular for 1091 * a {@link TypeNotPresentException}) before moving on, assuming nested 1092 * {@code Class} values were not resolvable within annotation attributes and 1093 * thereby effectively pretending there were no annotations on the specified 1094 * element. 1095 * @param element the element that we tried to introspect annotations on 1096 * @param ex the exception that we encountered 1097 * @see #rethrowAnnotationConfigurationException 1098 * @see IntrospectionFailureLogger 1099 */ 1100 static void handleIntrospectionFailure(@Nullable AnnotatedElement element, Throwable ex) { 1101 rethrowAnnotationConfigurationException(ex); 1102 IntrospectionFailureLogger logger = IntrospectionFailureLogger.INFO; 1103 boolean meta = false; 1104 if (element instanceof Class && Annotation.class.isAssignableFrom((Class<?>) element)) { 1105 // Meta-annotation or (default) value lookup on an annotation type 1106 logger = IntrospectionFailureLogger.DEBUG; 1107 meta = true; 1108 } 1109 if (logger.isEnabled()) { 1110 String message = meta ? 1111 "Failed to meta-introspect annotation " : 1112 "Failed to introspect annotations on "; 1113 logger.log(message + element + ": " + ex); 1114 } 1115 } 1116 1117 /** 1118 * Retrieve the <em>default value</em> of the {@code value} attribute 1119 * of a single-element Annotation, given an annotation instance. 1120 * @param annotation the annotation instance from which to retrieve the default value 1121 * @return the default value, or {@code null} if not found 1122 * @see #getDefaultValue(Annotation, String) 1123 */ 1124 @Nullable 1125 public static Object getDefaultValue(Annotation annotation) { 1126 return getDefaultValue(annotation, VALUE); 1127 } 1128 1129 /** 1130 * Retrieve the <em>default value</em> of a named attribute, given an annotation instance. 1131 * @param annotation the annotation instance from which to retrieve the default value 1132 * @param attributeName the name of the attribute value to retrieve 1133 * @return the default value of the named attribute, or {@code null} if not found 1134 * @see #getDefaultValue(Class, String) 1135 */ 1136 @Nullable 1137 public static Object getDefaultValue(@Nullable Annotation annotation, @Nullable String attributeName) { 1138 return (annotation != null ? getDefaultValue(annotation.annotationType(), attributeName) : null); 1139 } 1140 1141 /** 1142 * Retrieve the <em>default value</em> of the {@code value} attribute 1143 * of a single-element Annotation, given the {@link Class annotation type}. 1144 * @param annotationType the <em>annotation type</em> for which the default value should be retrieved 1145 * @return the default value, or {@code null} if not found 1146 * @see #getDefaultValue(Class, String) 1147 */ 1148 @Nullable 1149 public static Object getDefaultValue(Class<? extends Annotation> annotationType) { 1150 return getDefaultValue(annotationType, VALUE); 1151 } 1152 1153 /** 1154 * Retrieve the <em>default value</em> of a named attribute, given the 1155 * {@link Class annotation type}. 1156 * @param annotationType the <em>annotation type</em> for which the default value should be retrieved 1157 * @param attributeName the name of the attribute value to retrieve. 1158 * @return the default value of the named attribute, or {@code null} if not found 1159 * @see #getDefaultValue(Annotation, String) 1160 */ 1161 @Nullable 1162 public static Object getDefaultValue( 1163 @Nullable Class<? extends Annotation> annotationType, @Nullable String attributeName) { 1164 1165 if (annotationType == null || !StringUtils.hasText(attributeName)) { 1166 return null; 1167 } 1168 return MergedAnnotation.of(annotationType).getDefaultValue(attributeName).orElse(null); 1169 } 1170 1171 /** 1172 * <em>Synthesize</em> an annotation from the supplied {@code annotation} 1173 * by wrapping it in a dynamic proxy that transparently enforces 1174 * <em>attribute alias</em> semantics for annotation attributes that are 1175 * annotated with {@link AliasFor @AliasFor}. 1176 * @param annotation the annotation to synthesize 1177 * @param annotatedElement the element that is annotated with the supplied 1178 * annotation; may be {@code null} if unknown 1179 * @return the synthesized annotation if the supplied annotation is 1180 * <em>synthesizable</em>; {@code null} if the supplied annotation is 1181 * {@code null}; otherwise the supplied annotation unmodified 1182 * @throws AnnotationConfigurationException if invalid configuration of 1183 * {@code @AliasFor} is detected 1184 * @since 4.2 1185 * @see #synthesizeAnnotation(Map, Class, AnnotatedElement) 1186 * @see #synthesizeAnnotation(Class) 1187 */ 1188 public static <A extends Annotation> A synthesizeAnnotation( 1189 A annotation, @Nullable AnnotatedElement annotatedElement) { 1190 1191 if (annotation instanceof SynthesizedAnnotation || AnnotationFilter.PLAIN.matches(annotation)) { 1192 return annotation; 1193 } 1194 return MergedAnnotation.from(annotatedElement, annotation).synthesize(); 1195 } 1196 1197 /** 1198 * <em>Synthesize</em> an annotation from its default attributes values. 1199 * <p>This method simply delegates to 1200 * {@link #synthesizeAnnotation(Map, Class, AnnotatedElement)}, 1201 * supplying an empty map for the source attribute values and {@code null} 1202 * for the {@link AnnotatedElement}. 1203 * @param annotationType the type of annotation to synthesize 1204 * @return the synthesized annotation 1205 * @throws IllegalArgumentException if a required attribute is missing 1206 * @throws AnnotationConfigurationException if invalid configuration of 1207 * {@code @AliasFor} is detected 1208 * @since 4.2 1209 * @see #synthesizeAnnotation(Map, Class, AnnotatedElement) 1210 * @see #synthesizeAnnotation(Annotation, AnnotatedElement) 1211 */ 1212 public static <A extends Annotation> A synthesizeAnnotation(Class<A> annotationType) { 1213 return synthesizeAnnotation(Collections.emptyMap(), annotationType, null); 1214 } 1215 1216 /** 1217 * <em>Synthesize</em> an annotation from the supplied map of annotation 1218 * attributes by wrapping the map in a dynamic proxy that implements an 1219 * annotation of the specified {@code annotationType} and transparently 1220 * enforces <em>attribute alias</em> semantics for annotation attributes 1221 * that are annotated with {@link AliasFor @AliasFor}. 1222 * <p>The supplied map must contain a key-value pair for every attribute 1223 * defined in the supplied {@code annotationType} that is not aliased or 1224 * does not have a default value. Nested maps and nested arrays of maps 1225 * will be recursively synthesized into nested annotations or nested 1226 * arrays of annotations, respectively. 1227 * <p>Note that {@link AnnotationAttributes} is a specialized type of 1228 * {@link Map} that is an ideal candidate for this method's 1229 * {@code attributes} argument. 1230 * @param attributes the map of annotation attributes to synthesize 1231 * @param annotationType the type of annotation to synthesize 1232 * @param annotatedElement the element that is annotated with the annotation 1233 * corresponding to the supplied attributes; may be {@code null} if unknown 1234 * @return the synthesized annotation 1235 * @throws IllegalArgumentException if a required attribute is missing or if an 1236 * attribute is not of the correct type 1237 * @throws AnnotationConfigurationException if invalid configuration of 1238 * {@code @AliasFor} is detected 1239 * @since 4.2 1240 * @see #synthesizeAnnotation(Annotation, AnnotatedElement) 1241 * @see #synthesizeAnnotation(Class) 1242 * @see #getAnnotationAttributes(AnnotatedElement, Annotation) 1243 * @see #getAnnotationAttributes(AnnotatedElement, Annotation, boolean, boolean) 1244 */ 1245 public static <A extends Annotation> A synthesizeAnnotation(Map<String, Object> attributes, 1246 Class<A> annotationType, @Nullable AnnotatedElement annotatedElement) { 1247 1248 try { 1249 return MergedAnnotation.of(annotatedElement, annotationType, attributes).synthesize(); 1250 } 1251 catch (NoSuchElementException | IllegalStateException ex) { 1252 throw new IllegalArgumentException(ex); 1253 } 1254 } 1255 1256 /** 1257 * <em>Synthesize</em> an array of annotations from the supplied array 1258 * of {@code annotations} by creating a new array of the same size and 1259 * type and populating it with {@linkplain #synthesizeAnnotation(Annotation, 1260 * AnnotatedElement) synthesized} versions of the annotations from the input 1261 * array. 1262 * @param annotations the array of annotations to synthesize 1263 * @param annotatedElement the element that is annotated with the supplied 1264 * array of annotations; may be {@code null} if unknown 1265 * @return a new array of synthesized annotations, or {@code null} if 1266 * the supplied array is {@code null} 1267 * @throws AnnotationConfigurationException if invalid configuration of 1268 * {@code @AliasFor} is detected 1269 * @since 4.2 1270 * @see #synthesizeAnnotation(Annotation, AnnotatedElement) 1271 * @see #synthesizeAnnotation(Map, Class, AnnotatedElement) 1272 */ 1273 static Annotation[] synthesizeAnnotationArray(Annotation[] annotations, AnnotatedElement annotatedElement) { 1274 if (AnnotationsScanner.hasPlainJavaAnnotationsOnly(annotatedElement)) { 1275 return annotations; 1276 } 1277 Annotation[] synthesized = (Annotation[]) Array.newInstance( 1278 annotations.getClass().getComponentType(), annotations.length); 1279 for (int i = 0; i < annotations.length; i++) { 1280 synthesized[i] = synthesizeAnnotation(annotations[i], annotatedElement); 1281 } 1282 return synthesized; 1283 } 1284 1285 /** 1286 * Clear the internal annotation metadata cache. 1287 * @since 4.3.15 1288 */ 1289 public static void clearCache() { 1290 AnnotationTypeMappings.clearCache(); 1291 AnnotationsScanner.clearCache(); 1292 } 1293 1294 1295 /** 1296 * Internal holder used to wrap default values. 1297 */ 1298 private static class DefaultValueHolder { 1299 1300 final Object defaultValue; 1301 1302 public DefaultValueHolder(Object defaultValue) { 1303 this.defaultValue = defaultValue; 1304 } 1305 1306 @Override 1307 public String toString() { 1308 return "*" + this.defaultValue; 1309 } 1310 } 1311 1312}