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.util.Collections; 022import java.util.Comparator; 023import java.util.LinkedHashSet; 024import java.util.Set; 025import java.util.stream.Collectors; 026 027import org.springframework.core.BridgeMethodResolver; 028import org.springframework.core.annotation.MergedAnnotation.Adapt; 029import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; 030import org.springframework.lang.Nullable; 031import org.springframework.util.MultiValueMap; 032 033/** 034 * General utility methods for finding annotations, meta-annotations, and 035 * repeatable annotations on {@link AnnotatedElement AnnotatedElements}. 036 * 037 * <p>{@code AnnotatedElementUtils} defines the public API for Spring's 038 * meta-annotation programming model with support for <em>annotation attribute 039 * overrides</em>. If you do not need support for annotation attribute 040 * overrides, consider using {@link AnnotationUtils} instead. 041 * 042 * <p>Note that the features of this class are not provided by the JDK's 043 * introspection facilities themselves. 044 * 045 * <h3>Annotation Attribute Overrides</h3> 046 * <p>Support for meta-annotations with <em>attribute overrides</em> in 047 * <em>composed annotations</em> is provided by all variants of the 048 * {@code getMergedAnnotationAttributes()}, {@code getMergedAnnotation()}, 049 * {@code getAllMergedAnnotations()}, {@code getMergedRepeatableAnnotations()}, 050 * {@code findMergedAnnotationAttributes()}, {@code findMergedAnnotation()}, 051 * {@code findAllMergedAnnotations()}, and {@code findMergedRepeatableAnnotations()} 052 * methods. 053 * 054 * <h3>Find vs. Get Semantics</h3> 055 * <p>The search algorithms used by methods in this class follow either 056 * <em>find</em> or <em>get</em> semantics. Consult the javadocs for each 057 * individual method for details on which search algorithm is used. 058 * 059 * <p><strong>Get semantics</strong> are limited to searching for annotations 060 * that are either <em>present</em> on an {@code AnnotatedElement} (i.e. declared 061 * locally or {@linkplain java.lang.annotation.Inherited inherited}) or declared 062 * within the annotation hierarchy <em>above</em> the {@code AnnotatedElement}. 063 * 064 * <p><strong>Find semantics</strong> are much more exhaustive, providing 065 * <em>get semantics</em> plus support for the following: 066 * 067 * <ul> 068 * <li>Searching on interfaces, if the annotated element is a class 069 * <li>Searching on superclasses, if the annotated element is a class 070 * <li>Resolving bridged methods, if the annotated element is a method 071 * <li>Searching on methods in interfaces, if the annotated element is a method 072 * <li>Searching on methods in superclasses, if the annotated element is a method 073 * </ul> 074 * 075 * <h3>Support for {@code @Inherited}</h3> 076 * <p>Methods following <em>get semantics</em> will honor the contract of Java's 077 * {@link java.lang.annotation.Inherited @Inherited} annotation except that locally 078 * declared annotations (including custom composed annotations) will be favored over 079 * inherited annotations. In contrast, methods following <em>find semantics</em> 080 * will completely ignore the presence of {@code @Inherited} since the <em>find</em> 081 * search algorithm manually traverses type and method hierarchies and thereby 082 * implicitly supports annotation inheritance without a need for {@code @Inherited}. 083 * 084 * @author Phillip Webb 085 * @author Juergen Hoeller 086 * @author Sam Brannen 087 * @since 4.0 088 * @see AliasFor 089 * @see AnnotationAttributes 090 * @see AnnotationUtils 091 * @see BridgeMethodResolver 092 */ 093public abstract class AnnotatedElementUtils { 094 095 /** 096 * Build an adapted {@link AnnotatedElement} for the given annotations, 097 * typically for use with other methods on {@link AnnotatedElementUtils}. 098 * @param annotations the annotations to expose through the {@code AnnotatedElement} 099 * @since 4.3 100 */ 101 public static AnnotatedElement forAnnotations(Annotation... annotations) { 102 return new AnnotatedElementForAnnotations(annotations); 103 } 104 105 /** 106 * Get the fully qualified class names of all meta-annotation types 107 * <em>present</em> on the annotation (of the specified {@code annotationType}) 108 * on the supplied {@link AnnotatedElement}. 109 * <p>This method follows <em>get semantics</em> as described in the 110 * {@linkplain AnnotatedElementUtils class-level javadoc}. 111 * @param element the annotated element 112 * @param annotationType the annotation type on which to find meta-annotations 113 * @return the names of all meta-annotations present on the annotation, 114 * or an empty set if not found 115 * @since 4.2 116 * @see #getMetaAnnotationTypes(AnnotatedElement, String) 117 * @see #hasMetaAnnotationTypes 118 */ 119 public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, 120 Class<? extends Annotation> annotationType) { 121 122 return getMetaAnnotationTypes(element, element.getAnnotation(annotationType)); 123 } 124 125 /** 126 * Get the fully qualified class names of all meta-annotation 127 * types <em>present</em> on the annotation (of the specified 128 * {@code annotationName}) on the supplied {@link AnnotatedElement}. 129 * <p>This method follows <em>get semantics</em> as described in the 130 * {@linkplain AnnotatedElementUtils class-level javadoc}. 131 * @param element the annotated element 132 * @param annotationName the fully qualified class name of the annotation 133 * type on which to find meta-annotations 134 * @return the names of all meta-annotations present on the annotation, 135 * or an empty set if none found 136 * @see #getMetaAnnotationTypes(AnnotatedElement, Class) 137 * @see #hasMetaAnnotationTypes 138 */ 139 public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, String annotationName) { 140 for (Annotation annotation : element.getAnnotations()) { 141 if (annotation.annotationType().getName().equals(annotationName)) { 142 return getMetaAnnotationTypes(element, annotation); 143 } 144 } 145 return Collections.emptySet(); 146 } 147 148 private static Set<String> getMetaAnnotationTypes(AnnotatedElement element, @Nullable Annotation annotation) { 149 if (annotation == null) { 150 return Collections.emptySet(); 151 } 152 return getAnnotations(annotation.annotationType()).stream() 153 .map(mergedAnnotation -> mergedAnnotation.getType().getName()) 154 .collect(Collectors.toCollection(LinkedHashSet::new)); 155 } 156 157 /** 158 * Determine if the supplied {@link AnnotatedElement} is annotated with 159 * a <em>composed annotation</em> that is meta-annotated with an 160 * annotation of the specified {@code annotationType}. 161 * <p>This method follows <em>get semantics</em> as described in the 162 * {@linkplain AnnotatedElementUtils class-level javadoc}. 163 * @param element the annotated element 164 * @param annotationType the meta-annotation type to find 165 * @return {@code true} if a matching meta-annotation is present 166 * @since 4.2.3 167 * @see #getMetaAnnotationTypes 168 */ 169 public static boolean hasMetaAnnotationTypes(AnnotatedElement element, Class<? extends Annotation> annotationType) { 170 return getAnnotations(element).stream(annotationType).anyMatch(MergedAnnotation::isMetaPresent); 171 } 172 173 /** 174 * Determine if the supplied {@link AnnotatedElement} is annotated with a 175 * <em>composed annotation</em> that is meta-annotated with an annotation 176 * of the specified {@code annotationName}. 177 * <p>This method follows <em>get semantics</em> as described in the 178 * {@linkplain AnnotatedElementUtils class-level javadoc}. 179 * @param element the annotated element 180 * @param annotationName the fully qualified class name of the 181 * meta-annotation type to find 182 * @return {@code true} if a matching meta-annotation is present 183 * @see #getMetaAnnotationTypes 184 */ 185 public static boolean hasMetaAnnotationTypes(AnnotatedElement element, String annotationName) { 186 return getAnnotations(element).stream(annotationName).anyMatch(MergedAnnotation::isMetaPresent); 187 } 188 189 /** 190 * Determine if an annotation of the specified {@code annotationType} 191 * is <em>present</em> on the supplied {@link AnnotatedElement} or 192 * within the annotation hierarchy <em>above</em> the specified element. 193 * <p>If this method returns {@code true}, then {@link #getMergedAnnotationAttributes} 194 * will return a non-null value. 195 * <p>This method follows <em>get semantics</em> as described in the 196 * {@linkplain AnnotatedElementUtils class-level javadoc}. 197 * @param element the annotated element 198 * @param annotationType the annotation type to find 199 * @return {@code true} if a matching annotation is present 200 * @since 4.2.3 201 * @see #hasAnnotation(AnnotatedElement, Class) 202 */ 203 public static boolean isAnnotated(AnnotatedElement element, Class<? extends Annotation> annotationType) { 204 // Shortcut: directly present on the element, with no merging needed? 205 if (AnnotationFilter.PLAIN.matches(annotationType) || 206 AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) { 207 return element.isAnnotationPresent(annotationType); 208 } 209 // Exhaustive retrieval of merged annotations... 210 return getAnnotations(element).isPresent(annotationType); 211 } 212 213 /** 214 * Determine if an annotation of the specified {@code annotationName} is 215 * <em>present</em> on the supplied {@link AnnotatedElement} or within the 216 * annotation hierarchy <em>above</em> the specified element. 217 * <p>If this method returns {@code true}, then {@link #getMergedAnnotationAttributes} 218 * will return a non-null value. 219 * <p>This method follows <em>get semantics</em> as described in the 220 * {@linkplain AnnotatedElementUtils class-level javadoc}. 221 * @param element the annotated element 222 * @param annotationName the fully qualified class name of the annotation type to find 223 * @return {@code true} if a matching annotation is present 224 */ 225 public static boolean isAnnotated(AnnotatedElement element, String annotationName) { 226 return getAnnotations(element).isPresent(annotationName); 227 } 228 229 /** 230 * Get the first annotation of the specified {@code annotationType} within 231 * the annotation hierarchy <em>above</em> the supplied {@code element} and 232 * merge that annotation's attributes with <em>matching</em> attributes from 233 * annotations in lower levels of the annotation hierarchy. 234 * <p>{@link AliasFor @AliasFor} semantics are fully supported, both 235 * within a single annotation and within the annotation hierarchy. 236 * <p>This method delegates to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)}. 237 * @param element the annotated element 238 * @param annotationType the annotation type to find 239 * @return the merged {@code AnnotationAttributes}, or {@code null} if not found 240 * @since 4.2 241 * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) 242 * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) 243 * @see #getMergedAnnotation(AnnotatedElement, Class) 244 * @see #findMergedAnnotation(AnnotatedElement, Class) 245 */ 246 @Nullable 247 public static AnnotationAttributes getMergedAnnotationAttributes( 248 AnnotatedElement element, Class<? extends Annotation> annotationType) { 249 250 MergedAnnotation<?> mergedAnnotation = getAnnotations(element) 251 .get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared()); 252 return getAnnotationAttributes(mergedAnnotation, false, false); 253 } 254 255 /** 256 * Get the first annotation of the specified {@code annotationName} within 257 * the annotation hierarchy <em>above</em> the supplied {@code element} and 258 * merge that annotation's attributes with <em>matching</em> attributes from 259 * annotations in lower levels of the annotation hierarchy. 260 * <p>{@link AliasFor @AliasFor} semantics are fully supported, both 261 * within a single annotation and within the annotation hierarchy. 262 * <p>This method delegates to {@link #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)}, 263 * supplying {@code false} for {@code classValuesAsString} and {@code nestedAnnotationsAsMap}. 264 * @param element the annotated element 265 * @param annotationName the fully qualified class name of the annotation type to find 266 * @return the merged {@code AnnotationAttributes}, or {@code null} if not found 267 * @since 4.2 268 * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) 269 * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) 270 * @see #findMergedAnnotation(AnnotatedElement, Class) 271 * @see #getAllAnnotationAttributes(AnnotatedElement, String) 272 */ 273 @Nullable 274 public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element, 275 String annotationName) { 276 277 return getMergedAnnotationAttributes(element, annotationName, false, false); 278 } 279 280 /** 281 * Get the first annotation of the specified {@code annotationName} within 282 * the annotation hierarchy <em>above</em> the supplied {@code element} and 283 * merge that annotation's attributes with <em>matching</em> attributes from 284 * annotations in lower levels of the annotation hierarchy. 285 * <p>Attributes from lower levels in the annotation hierarchy override attributes 286 * of the same name from higher levels, and {@link AliasFor @AliasFor} semantics are 287 * fully supported, both within a single annotation and within the annotation hierarchy. 288 * <p>In contrast to {@link #getAllAnnotationAttributes}, the search algorithm used by 289 * this method will stop searching the annotation hierarchy once the first annotation 290 * of the specified {@code annotationName} has been found. As a consequence, 291 * additional annotations of the specified {@code annotationName} will be ignored. 292 * <p>This method follows <em>get semantics</em> as described in the 293 * {@linkplain AnnotatedElementUtils class-level javadoc}. 294 * @param element the annotated element 295 * @param annotationName the fully qualified class name of the annotation type to find 296 * @param classValuesAsString whether to convert Class references into Strings or to 297 * preserve them as Class references 298 * @param nestedAnnotationsAsMap whether to convert nested Annotation instances 299 * into {@code AnnotationAttributes} maps or to preserve them as Annotation instances 300 * @return the merged {@code AnnotationAttributes}, or {@code null} if not found 301 * @since 4.2 302 * @see #findMergedAnnotation(AnnotatedElement, Class) 303 * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) 304 * @see #getAllAnnotationAttributes(AnnotatedElement, String, boolean, boolean) 305 */ 306 @Nullable 307 public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element, 308 String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { 309 310 MergedAnnotation<?> mergedAnnotation = getAnnotations(element) 311 .get(annotationName, null, MergedAnnotationSelectors.firstDirectlyDeclared()); 312 return getAnnotationAttributes(mergedAnnotation, classValuesAsString, nestedAnnotationsAsMap); 313 } 314 315 /** 316 * Get the first annotation of the specified {@code annotationType} within 317 * the annotation hierarchy <em>above</em> the supplied {@code element}, 318 * merge that annotation's attributes with <em>matching</em> attributes from 319 * annotations in lower levels of the annotation hierarchy, and synthesize 320 * the result back into an annotation of the specified {@code annotationType}. 321 * <p>{@link AliasFor @AliasFor} semantics are fully supported, both 322 * within a single annotation and within the annotation hierarchy. 323 * @param element the annotated element 324 * @param annotationType the annotation type to find 325 * @return the merged, synthesized {@code Annotation}, or {@code null} if not found 326 * @since 4.2 327 * @see #findMergedAnnotation(AnnotatedElement, Class) 328 */ 329 @Nullable 330 public static <A extends Annotation> A getMergedAnnotation(AnnotatedElement element, Class<A> annotationType) { 331 // Shortcut: directly present on the element, with no merging needed? 332 if (AnnotationFilter.PLAIN.matches(annotationType) || 333 AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) { 334 return element.getDeclaredAnnotation(annotationType); 335 } 336 // Exhaustive retrieval of merged annotations... 337 return getAnnotations(element) 338 .get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared()) 339 .synthesize(MergedAnnotation::isPresent).orElse(null); 340 } 341 342 /** 343 * Get <strong>all</strong> annotations of the specified {@code annotationType} 344 * within the annotation hierarchy <em>above</em> the supplied {@code element}; 345 * and for each annotation found, merge that annotation's attributes with 346 * <em>matching</em> attributes from annotations in lower levels of the annotation 347 * hierarchy and synthesize the results back into an annotation of the specified 348 * {@code annotationType}. 349 * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a 350 * single annotation and within annotation hierarchies. 351 * <p>This method follows <em>get semantics</em> as described in the 352 * {@linkplain AnnotatedElementUtils class-level javadoc}. 353 * @param element the annotated element (never {@code null}) 354 * @param annotationType the annotation type to find (never {@code null}) 355 * @return the set of all merged, synthesized {@code Annotations} found, 356 * or an empty set if none were found 357 * @since 4.3 358 * @see #getMergedAnnotation(AnnotatedElement, Class) 359 * @see #getAllAnnotationAttributes(AnnotatedElement, String) 360 * @see #findAllMergedAnnotations(AnnotatedElement, Class) 361 */ 362 public static <A extends Annotation> Set<A> getAllMergedAnnotations( 363 AnnotatedElement element, Class<A> annotationType) { 364 365 return getAnnotations(element).stream(annotationType) 366 .collect(MergedAnnotationCollectors.toAnnotationSet()); 367 } 368 369 /** 370 * Get <strong>all</strong> annotations of the specified {@code annotationTypes} 371 * within the annotation hierarchy <em>above</em> the supplied {@code element}; 372 * and for each annotation found, merge that annotation's attributes with 373 * <em>matching</em> attributes from annotations in lower levels of the 374 * annotation hierarchy and synthesize the results back into an annotation 375 * of the corresponding {@code annotationType}. 376 * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a 377 * single annotation and within annotation hierarchies. 378 * <p>This method follows <em>get semantics</em> as described in the 379 * {@linkplain AnnotatedElementUtils class-level javadoc}. 380 * @param element the annotated element (never {@code null}) 381 * @param annotationTypes the annotation types to find 382 * @return the set of all merged, synthesized {@code Annotations} found, 383 * or an empty set if none were found 384 * @since 5.1 385 * @see #getAllMergedAnnotations(AnnotatedElement, Class) 386 */ 387 public static Set<Annotation> getAllMergedAnnotations(AnnotatedElement element, 388 Set<Class<? extends Annotation>> annotationTypes) { 389 390 return getAnnotations(element).stream() 391 .filter(MergedAnnotationPredicates.typeIn(annotationTypes)) 392 .collect(MergedAnnotationCollectors.toAnnotationSet()); 393 } 394 395 /** 396 * Get all <em>repeatable annotations</em> of the specified {@code annotationType} 397 * within the annotation hierarchy <em>above</em> the supplied {@code element}; 398 * and for each annotation found, merge that annotation's attributes with 399 * <em>matching</em> attributes from annotations in lower levels of the annotation 400 * hierarchy and synthesize the results back into an annotation of the specified 401 * {@code annotationType}. 402 * <p>The container type that holds the repeatable annotations will be looked up 403 * via {@link java.lang.annotation.Repeatable}. 404 * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a 405 * single annotation and within annotation hierarchies. 406 * <p>This method follows <em>get semantics</em> as described in the 407 * {@linkplain AnnotatedElementUtils class-level javadoc}. 408 * @param element the annotated element (never {@code null}) 409 * @param annotationType the annotation type to find (never {@code null}) 410 * @return the set of all merged repeatable {@code Annotations} found, 411 * or an empty set if none were found 412 * @throws IllegalArgumentException if the {@code element} or {@code annotationType} 413 * is {@code null}, or if the container type cannot be resolved 414 * @since 4.3 415 * @see #getMergedAnnotation(AnnotatedElement, Class) 416 * @see #getAllMergedAnnotations(AnnotatedElement, Class) 417 * @see #getMergedRepeatableAnnotations(AnnotatedElement, Class, Class) 418 */ 419 public static <A extends Annotation> Set<A> getMergedRepeatableAnnotations( 420 AnnotatedElement element, Class<A> annotationType) { 421 422 return getMergedRepeatableAnnotations(element, annotationType, null); 423 } 424 425 /** 426 * Get all <em>repeatable annotations</em> of the specified {@code annotationType} 427 * within the annotation hierarchy <em>above</em> the supplied {@code element}; 428 * and for each annotation found, merge that annotation's attributes with 429 * <em>matching</em> attributes from annotations in lower levels of the annotation 430 * hierarchy and synthesize the results back into an annotation of the specified 431 * {@code annotationType}. 432 * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a 433 * single annotation and within annotation hierarchies. 434 * <p>This method follows <em>get semantics</em> as described in the 435 * {@linkplain AnnotatedElementUtils class-level javadoc}. 436 * @param element the annotated element (never {@code null}) 437 * @param annotationType the annotation type to find (never {@code null}) 438 * @param containerType the type of the container that holds the annotations; 439 * may be {@code null} if the container type should be looked up via 440 * {@link java.lang.annotation.Repeatable} 441 * @return the set of all merged repeatable {@code Annotations} found, 442 * or an empty set if none were found 443 * @throws IllegalArgumentException if the {@code element} or {@code annotationType} 444 * is {@code null}, or if the container type cannot be resolved 445 * @throws AnnotationConfigurationException if the supplied {@code containerType} 446 * is not a valid container annotation for the supplied {@code annotationType} 447 * @since 4.3 448 * @see #getMergedAnnotation(AnnotatedElement, Class) 449 * @see #getAllMergedAnnotations(AnnotatedElement, Class) 450 */ 451 public static <A extends Annotation> Set<A> getMergedRepeatableAnnotations( 452 AnnotatedElement element, Class<A> annotationType, 453 @Nullable Class<? extends Annotation> containerType) { 454 455 return getRepeatableAnnotations(element, containerType, annotationType) 456 .stream(annotationType) 457 .collect(MergedAnnotationCollectors.toAnnotationSet()); 458 } 459 460 /** 461 * Get the annotation attributes of <strong>all</strong> annotations of the specified 462 * {@code annotationName} in the annotation hierarchy above the supplied 463 * {@link AnnotatedElement} and store the results in a {@link MultiValueMap}. 464 * <p>Note: in contrast to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)}, 465 * this method does <em>not</em> support attribute overrides. 466 * <p>This method follows <em>get semantics</em> as described in the 467 * {@linkplain AnnotatedElementUtils class-level javadoc}. 468 * @param element the annotated element 469 * @param annotationName the fully qualified class name of the annotation type to find 470 * @return a {@link MultiValueMap} keyed by attribute name, containing the annotation 471 * attributes from all annotations found, or {@code null} if not found 472 * @see #getAllAnnotationAttributes(AnnotatedElement, String, boolean, boolean) 473 */ 474 @Nullable 475 public static MultiValueMap<String, Object> getAllAnnotationAttributes( 476 AnnotatedElement element, String annotationName) { 477 478 return getAllAnnotationAttributes(element, annotationName, false, false); 479 } 480 481 /** 482 * Get the annotation attributes of <strong>all</strong> annotations of 483 * the specified {@code annotationName} in the annotation hierarchy above 484 * the supplied {@link AnnotatedElement} and store the results in a 485 * {@link MultiValueMap}. 486 * <p>Note: in contrast to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)}, 487 * this method does <em>not</em> support attribute overrides. 488 * <p>This method follows <em>get semantics</em> as described in the 489 * {@linkplain AnnotatedElementUtils class-level javadoc}. 490 * @param element the annotated element 491 * @param annotationName the fully qualified class name of the annotation type to find 492 * @param classValuesAsString whether to convert Class references into Strings or to 493 * preserve them as Class references 494 * @param nestedAnnotationsAsMap whether to convert nested Annotation instances into 495 * {@code AnnotationAttributes} maps or to preserve them as Annotation instances 496 * @return a {@link MultiValueMap} keyed by attribute name, containing the annotation 497 * attributes from all annotations found, or {@code null} if not found 498 */ 499 @Nullable 500 public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element, 501 String annotationName, final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) { 502 503 Adapt[] adaptations = Adapt.values(classValuesAsString, nestedAnnotationsAsMap); 504 return getAnnotations(element).stream(annotationName) 505 .filter(MergedAnnotationPredicates.unique(MergedAnnotation::getMetaTypes)) 506 .map(MergedAnnotation::withNonMergedAttributes) 507 .collect(MergedAnnotationCollectors.toMultiValueMap(AnnotatedElementUtils::nullIfEmpty, adaptations)); 508 } 509 510 /** 511 * Determine if an annotation of the specified {@code annotationType} 512 * is <em>available</em> on the supplied {@link AnnotatedElement} or 513 * within the annotation hierarchy <em>above</em> the specified element. 514 * <p>If this method returns {@code true}, then {@link #findMergedAnnotationAttributes} 515 * will return a non-null value. 516 * <p>This method follows <em>find semantics</em> as described in the 517 * {@linkplain AnnotatedElementUtils class-level javadoc}. 518 * @param element the annotated element 519 * @param annotationType the annotation type to find 520 * @return {@code true} if a matching annotation is present 521 * @since 4.3 522 * @see #isAnnotated(AnnotatedElement, Class) 523 */ 524 public static boolean hasAnnotation(AnnotatedElement element, Class<? extends Annotation> annotationType) { 525 // Shortcut: directly present on the element, with no merging needed? 526 if (AnnotationFilter.PLAIN.matches(annotationType) || 527 AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) { 528 return element.isAnnotationPresent(annotationType); 529 } 530 // Exhaustive retrieval of merged annotations... 531 return findAnnotations(element).isPresent(annotationType); 532 } 533 534 /** 535 * Find the first annotation of the specified {@code annotationType} within 536 * the annotation hierarchy <em>above</em> the supplied {@code element} and 537 * merge that annotation's attributes with <em>matching</em> attributes from 538 * annotations in lower levels of the annotation hierarchy. 539 * <p>Attributes from lower levels in the annotation hierarchy override 540 * attributes of the same name from higher levels, and 541 * {@link AliasFor @AliasFor} semantics are fully supported, both 542 * within a single annotation and within the annotation hierarchy. 543 * <p>In contrast to {@link #getAllAnnotationAttributes}, the search algorithm 544 * used by this method will stop searching the annotation hierarchy once the 545 * first annotation of the specified {@code annotationType} has been found. 546 * As a consequence, additional annotations of the specified 547 * {@code annotationType} will be ignored. 548 * <p>This method follows <em>find semantics</em> as described in the 549 * {@linkplain AnnotatedElementUtils class-level javadoc}. 550 * @param element the annotated element 551 * @param annotationType the annotation type to find 552 * @param classValuesAsString whether to convert Class references into 553 * Strings or to preserve them as Class references 554 * @param nestedAnnotationsAsMap whether to convert nested Annotation instances into 555 * {@code AnnotationAttributes} maps or to preserve them as Annotation instances 556 * @return the merged {@code AnnotationAttributes}, or {@code null} if not found 557 * @since 4.2 558 * @see #findMergedAnnotation(AnnotatedElement, Class) 559 * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) 560 */ 561 @Nullable 562 public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element, 563 Class<? extends Annotation> annotationType, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { 564 565 MergedAnnotation<?> mergedAnnotation = findAnnotations(element) 566 .get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared()); 567 return getAnnotationAttributes(mergedAnnotation, classValuesAsString, nestedAnnotationsAsMap); 568 } 569 570 /** 571 * Find the first annotation of the specified {@code annotationName} within 572 * the annotation hierarchy <em>above</em> the supplied {@code element} and 573 * merge that annotation's attributes with <em>matching</em> attributes from 574 * annotations in lower levels of the annotation hierarchy. 575 * <p>Attributes from lower levels in the annotation hierarchy override 576 * attributes of the same name from higher levels, and 577 * {@link AliasFor @AliasFor} semantics are fully supported, both 578 * within a single annotation and within the annotation hierarchy. 579 * <p>In contrast to {@link #getAllAnnotationAttributes}, the search 580 * algorithm used by this method will stop searching the annotation 581 * hierarchy once the first annotation of the specified 582 * {@code annotationName} has been found. As a consequence, additional 583 * annotations of the specified {@code annotationName} will be ignored. 584 * <p>This method follows <em>find semantics</em> as described in the 585 * {@linkplain AnnotatedElementUtils class-level javadoc}. 586 * @param element the annotated element 587 * @param annotationName the fully qualified class name of the annotation type to find 588 * @param classValuesAsString whether to convert Class references into Strings or to 589 * preserve them as Class references 590 * @param nestedAnnotationsAsMap whether to convert nested Annotation instances into 591 * {@code AnnotationAttributes} maps or to preserve them as Annotation instances 592 * @return the merged {@code AnnotationAttributes}, or {@code null} if not found 593 * @since 4.2 594 * @see #findMergedAnnotation(AnnotatedElement, Class) 595 * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) 596 */ 597 @Nullable 598 public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element, 599 String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { 600 601 MergedAnnotation<?> mergedAnnotation = findAnnotations(element) 602 .get(annotationName, null, MergedAnnotationSelectors.firstDirectlyDeclared()); 603 return getAnnotationAttributes(mergedAnnotation, classValuesAsString, nestedAnnotationsAsMap); 604 } 605 606 /** 607 * Find the first annotation of the specified {@code annotationType} within 608 * the annotation hierarchy <em>above</em> the supplied {@code element}, 609 * merge that annotation's attributes with <em>matching</em> attributes from 610 * annotations in lower levels of the annotation hierarchy, and synthesize 611 * the result back into an annotation of the specified {@code annotationType}. 612 * <p>{@link AliasFor @AliasFor} semantics are fully supported, both 613 * within a single annotation and within the annotation hierarchy. 614 * <p>This method follows <em>find semantics</em> as described in the 615 * {@linkplain AnnotatedElementUtils class-level javadoc}. 616 * @param element the annotated element 617 * @param annotationType the annotation type to find 618 * @return the merged, synthesized {@code Annotation}, or {@code null} if not found 619 * @since 4.2 620 * @see #findAllMergedAnnotations(AnnotatedElement, Class) 621 * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) 622 * @see #getMergedAnnotationAttributes(AnnotatedElement, Class) 623 */ 624 @Nullable 625 public static <A extends Annotation> A findMergedAnnotation(AnnotatedElement element, Class<A> annotationType) { 626 // Shortcut: directly present on the element, with no merging needed? 627 if (AnnotationFilter.PLAIN.matches(annotationType) || 628 AnnotationsScanner.hasPlainJavaAnnotationsOnly(element)) { 629 return element.getDeclaredAnnotation(annotationType); 630 } 631 // Exhaustive retrieval of merged annotations... 632 return findAnnotations(element) 633 .get(annotationType, null, MergedAnnotationSelectors.firstDirectlyDeclared()) 634 .synthesize(MergedAnnotation::isPresent).orElse(null); 635 } 636 637 /** 638 * Find <strong>all</strong> annotations of the specified {@code annotationType} 639 * within the annotation hierarchy <em>above</em> the supplied {@code element}; 640 * and for each annotation found, merge that annotation's attributes with 641 * <em>matching</em> attributes from annotations in lower levels of the annotation 642 * hierarchy and synthesize the results back into an annotation of the specified 643 * {@code annotationType}. 644 * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a 645 * single annotation and within annotation hierarchies. 646 * <p>This method follows <em>find semantics</em> as described in the 647 * {@linkplain AnnotatedElementUtils class-level javadoc}. 648 * @param element the annotated element (never {@code null}) 649 * @param annotationType the annotation type to find (never {@code null}) 650 * @return the set of all merged, synthesized {@code Annotations} found, 651 * or an empty set if none were found 652 * @since 4.3 653 * @see #findMergedAnnotation(AnnotatedElement, Class) 654 * @see #getAllMergedAnnotations(AnnotatedElement, Class) 655 */ 656 public static <A extends Annotation> Set<A> findAllMergedAnnotations(AnnotatedElement element, Class<A> annotationType) { 657 return findAnnotations(element).stream(annotationType) 658 .sorted(highAggregateIndexesFirst()) 659 .collect(MergedAnnotationCollectors.toAnnotationSet()); 660 } 661 662 /** 663 * Find <strong>all</strong> annotations of the specified {@code annotationTypes} 664 * within the annotation hierarchy <em>above</em> the supplied {@code element}; 665 * and for each annotation found, merge that annotation's attributes with 666 * <em>matching</em> attributes from annotations in lower levels of the 667 * annotation hierarchy and synthesize the results back into an annotation 668 * of the corresponding {@code annotationType}. 669 * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a 670 * single annotation and within annotation hierarchies. 671 * <p>This method follows <em>find semantics</em> as described in the 672 * {@linkplain AnnotatedElementUtils class-level javadoc}. 673 * @param element the annotated element (never {@code null}) 674 * @param annotationTypes the annotation types to find 675 * @return the set of all merged, synthesized {@code Annotations} found, 676 * or an empty set if none were found 677 * @since 5.1 678 * @see #findAllMergedAnnotations(AnnotatedElement, Class) 679 */ 680 public static Set<Annotation> findAllMergedAnnotations(AnnotatedElement element, Set<Class<? extends Annotation>> annotationTypes) { 681 return findAnnotations(element).stream() 682 .filter(MergedAnnotationPredicates.typeIn(annotationTypes)) 683 .sorted(highAggregateIndexesFirst()) 684 .collect(MergedAnnotationCollectors.toAnnotationSet()); 685 } 686 687 /** 688 * Find all <em>repeatable annotations</em> of the specified {@code annotationType} 689 * within the annotation hierarchy <em>above</em> the supplied {@code element}; 690 * and for each annotation found, merge that annotation's attributes with 691 * <em>matching</em> attributes from annotations in lower levels of the annotation 692 * hierarchy and synthesize the results back into an annotation of the specified 693 * {@code annotationType}. 694 * <p>The container type that holds the repeatable annotations will be looked up 695 * via {@link java.lang.annotation.Repeatable}. 696 * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a 697 * single annotation and within annotation hierarchies. 698 * <p>This method follows <em>find semantics</em> as described in the 699 * {@linkplain AnnotatedElementUtils class-level javadoc}. 700 * @param element the annotated element (never {@code null}) 701 * @param annotationType the annotation type to find (never {@code null}) 702 * @return the set of all merged repeatable {@code Annotations} found, 703 * or an empty set if none were found 704 * @throws IllegalArgumentException if the {@code element} or {@code annotationType} 705 * is {@code null}, or if the container type cannot be resolved 706 * @since 4.3 707 * @see #findMergedAnnotation(AnnotatedElement, Class) 708 * @see #findAllMergedAnnotations(AnnotatedElement, Class) 709 * @see #findMergedRepeatableAnnotations(AnnotatedElement, Class, Class) 710 */ 711 public static <A extends Annotation> Set<A> findMergedRepeatableAnnotations(AnnotatedElement element, 712 Class<A> annotationType) { 713 714 return findMergedRepeatableAnnotations(element, annotationType, null); 715 } 716 717 /** 718 * Find all <em>repeatable annotations</em> of the specified {@code annotationType} 719 * within the annotation hierarchy <em>above</em> the supplied {@code element}; 720 * and for each annotation found, merge that annotation's attributes with 721 * <em>matching</em> attributes from annotations in lower levels of the annotation 722 * hierarchy and synthesize the results back into an annotation of the specified 723 * {@code annotationType}. 724 * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a 725 * single annotation and within annotation hierarchies. 726 * <p>This method follows <em>find semantics</em> as described in the 727 * {@linkplain AnnotatedElementUtils class-level javadoc}. 728 * @param element the annotated element (never {@code null}) 729 * @param annotationType the annotation type to find (never {@code null}) 730 * @param containerType the type of the container that holds the annotations; 731 * may be {@code null} if the container type should be looked up via 732 * {@link java.lang.annotation.Repeatable} 733 * @return the set of all merged repeatable {@code Annotations} found, 734 * or an empty set if none were found 735 * @throws IllegalArgumentException if the {@code element} or {@code annotationType} 736 * is {@code null}, or if the container type cannot be resolved 737 * @throws AnnotationConfigurationException if the supplied {@code containerType} 738 * is not a valid container annotation for the supplied {@code annotationType} 739 * @since 4.3 740 * @see #findMergedAnnotation(AnnotatedElement, Class) 741 * @see #findAllMergedAnnotations(AnnotatedElement, Class) 742 */ 743 public static <A extends Annotation> Set<A> findMergedRepeatableAnnotations(AnnotatedElement element, 744 Class<A> annotationType, @Nullable Class<? extends Annotation> containerType) { 745 746 return findRepeatableAnnotations(element, containerType, annotationType) 747 .stream(annotationType) 748 .sorted(highAggregateIndexesFirst()) 749 .collect(MergedAnnotationCollectors.toAnnotationSet()); 750 } 751 752 private static MergedAnnotations getAnnotations(AnnotatedElement element) { 753 return MergedAnnotations.from(element, SearchStrategy.INHERITED_ANNOTATIONS, RepeatableContainers.none()); 754 } 755 756 private static MergedAnnotations getRepeatableAnnotations(AnnotatedElement element, 757 @Nullable Class<? extends Annotation> containerType, Class<? extends Annotation> annotationType) { 758 759 RepeatableContainers repeatableContainers = RepeatableContainers.of(annotationType, containerType); 760 return MergedAnnotations.from(element, SearchStrategy.INHERITED_ANNOTATIONS, repeatableContainers); 761 } 762 763 private static MergedAnnotations findAnnotations(AnnotatedElement element) { 764 return MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.none()); 765 } 766 767 private static MergedAnnotations findRepeatableAnnotations(AnnotatedElement element, 768 @Nullable Class<? extends Annotation> containerType, Class<? extends Annotation> annotationType) { 769 770 RepeatableContainers repeatableContainers = RepeatableContainers.of(annotationType, containerType); 771 return MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY, repeatableContainers); 772 } 773 774 @Nullable 775 private static MultiValueMap<String, Object> nullIfEmpty(MultiValueMap<String, Object> map) { 776 return (map.isEmpty() ? null : map); 777 } 778 779 private static <A extends Annotation> Comparator<MergedAnnotation<A>> highAggregateIndexesFirst() { 780 return Comparator.<MergedAnnotation<A>> comparingInt( 781 MergedAnnotation::getAggregateIndex).reversed(); 782 } 783 784 @Nullable 785 private static AnnotationAttributes getAnnotationAttributes(MergedAnnotation<?> annotation, 786 boolean classValuesAsString, boolean nestedAnnotationsAsMap) { 787 788 if (!annotation.isPresent()) { 789 return null; 790 } 791 return annotation.asAnnotationAttributes( 792 Adapt.values(classValuesAsString, nestedAnnotationsAsMap)); 793 } 794 795 796 /** 797 * Adapted {@link AnnotatedElement} that hold specific annotations. 798 */ 799 private static class AnnotatedElementForAnnotations implements AnnotatedElement { 800 801 private final Annotation[] annotations; 802 803 AnnotatedElementForAnnotations(Annotation... annotations) { 804 this.annotations = annotations; 805 } 806 807 @Override 808 @SuppressWarnings("unchecked") 809 @Nullable 810 public <T extends Annotation> T getAnnotation(Class<T> annotationClass) { 811 for (Annotation annotation : this.annotations) { 812 if (annotation.annotationType() == annotationClass) { 813 return (T) annotation; 814 } 815 } 816 return null; 817 } 818 819 @Override 820 public Annotation[] getAnnotations() { 821 return this.annotations.clone(); 822 } 823 824 @Override 825 public Annotation[] getDeclaredAnnotations() { 826 return this.annotations.clone(); 827 } 828 829 } 830 831}