001/* 002 * Copyright 2002-2018 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.Method; 022import java.util.ArrayList; 023import java.util.Arrays; 024import java.util.HashSet; 025import java.util.LinkedHashSet; 026import java.util.List; 027import java.util.Map; 028import java.util.Set; 029 030import org.springframework.core.BridgeMethodResolver; 031import org.springframework.util.Assert; 032import org.springframework.util.CollectionUtils; 033import org.springframework.util.LinkedMultiValueMap; 034import org.springframework.util.MultiValueMap; 035 036/** 037 * General utility methods for finding annotations, meta-annotations, and 038 * repeatable annotations on {@link AnnotatedElement AnnotatedElements}. 039 * 040 * <p>{@code AnnotatedElementUtils} defines the public API for Spring's 041 * meta-annotation programming model with support for <em>annotation attribute 042 * overrides</em>. If you do not need support for annotation attribute 043 * overrides, consider using {@link AnnotationUtils} instead. 044 * 045 * <p>Note that the features of this class are not provided by the JDK's 046 * introspection facilities themselves. 047 * 048 * <h3>Annotation Attribute Overrides</h3> 049 * <p>Support for meta-annotations with <em>attribute overrides</em> in 050 * <em>composed annotations</em> is provided by all variants of the 051 * {@code getMergedAnnotationAttributes()}, {@code getMergedAnnotation()}, 052 * {@code getAllMergedAnnotations()}, {@code getMergedRepeatableAnnotations()}, 053 * {@code findMergedAnnotationAttributes()}, {@code findMergedAnnotation()}, 054 * {@code findAllMergedAnnotations()}, and {@code findMergedRepeatableAnnotations()} 055 * methods. 056 * 057 * <h3>Find vs. Get Semantics</h3> 058 * <p>The search algorithms used by methods in this class follow either 059 * <em>find</em> or <em>get</em> semantics. Consult the javadocs for each 060 * individual method for details on which search algorithm is used. 061 * 062 * <p><strong>Get semantics</strong> are limited to searching for annotations 063 * that are either <em>present</em> on an {@code AnnotatedElement} (i.e. declared 064 * locally or {@linkplain java.lang.annotation.Inherited inherited}) or declared 065 * within the annotation hierarchy <em>above</em> the {@code AnnotatedElement}. 066 * 067 * <p><strong>Find semantics</strong> are much more exhaustive, providing 068 * <em>get semantics</em> plus support for the following: 069 * 070 * <ul> 071 * <li>Searching on interfaces, if the annotated element is a class 072 * <li>Searching on superclasses, if the annotated element is a class 073 * <li>Resolving bridged methods, if the annotated element is a method 074 * <li>Searching on methods in interfaces, if the annotated element is a method 075 * <li>Searching on methods in superclasses, if the annotated element is a method 076 * </ul> 077 * 078 * <h3>Support for {@code @Inherited}</h3> 079 * <p>Methods following <em>get semantics</em> will honor the contract of Java's 080 * {@link java.lang.annotation.Inherited @Inherited} annotation except that locally 081 * declared annotations (including custom composed annotations) will be favored over 082 * inherited annotations. In contrast, methods following <em>find semantics</em> 083 * will completely ignore the presence of {@code @Inherited} since the <em>find</em> 084 * search algorithm manually traverses type and method hierarchies and thereby 085 * implicitly supports annotation inheritance without a need for {@code @Inherited}. 086 * 087 * @author Phillip Webb 088 * @author Juergen Hoeller 089 * @author Sam Brannen 090 * @since 4.0 091 * @see AliasFor 092 * @see AnnotationAttributes 093 * @see AnnotationUtils 094 * @see BridgeMethodResolver 095 */ 096public class AnnotatedElementUtils { 097 098 /** 099 * {@code null} constant used to denote that the search algorithm should continue. 100 */ 101 private static final Boolean CONTINUE = null; 102 103 private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0]; 104 105 private static final Processor<Boolean> alwaysTrueAnnotationProcessor = new AlwaysTrueBooleanAnnotationProcessor(); 106 107 108 /** 109 * Build an adapted {@link AnnotatedElement} for the given annotations, 110 * typically for use with other methods on {@link AnnotatedElementUtils}. 111 * @param annotations the annotations to expose through the {@code AnnotatedElement} 112 * @since 4.3 113 */ 114 public static AnnotatedElement forAnnotations(final Annotation... annotations) { 115 return new AnnotatedElement() { 116 @Override 117 @SuppressWarnings("unchecked") 118 public <T extends Annotation> T getAnnotation(Class<T> annotationClass) { 119 for (Annotation ann : annotations) { 120 if (ann.annotationType() == annotationClass) { 121 return (T) ann; 122 } 123 } 124 return null; 125 } 126 @Override 127 public Annotation[] getAnnotations() { 128 return annotations; 129 } 130 @Override 131 public Annotation[] getDeclaredAnnotations() { 132 return annotations; 133 } 134 }; 135 } 136 137 /** 138 * Get the fully qualified class names of all meta-annotation types 139 * <em>present</em> on the annotation (of the specified {@code annotationType}) 140 * on the supplied {@link AnnotatedElement}. 141 * <p>This method follows <em>get semantics</em> as described in the 142 * {@linkplain AnnotatedElementUtils class-level javadoc}. 143 * @param element the annotated element 144 * @param annotationType the annotation type on which to find meta-annotations 145 * @return the names of all meta-annotations present on the annotation, 146 * or {@code null} if not found 147 * @since 4.2 148 * @see #getMetaAnnotationTypes(AnnotatedElement, String) 149 * @see #hasMetaAnnotationTypes 150 */ 151 public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, Class<? extends Annotation> annotationType) { 152 Assert.notNull(element, "AnnotatedElement must not be null"); 153 Assert.notNull(annotationType, "'annotationType' must not be null"); 154 155 return getMetaAnnotationTypes(element, element.getAnnotation(annotationType)); 156 } 157 158 /** 159 * Get the fully qualified class names of all meta-annotation 160 * types <em>present</em> on the annotation (of the specified 161 * {@code annotationName}) on the supplied {@link AnnotatedElement}. 162 * <p>This method follows <em>get semantics</em> as described in the 163 * {@linkplain AnnotatedElementUtils class-level javadoc}. 164 * @param element the annotated element 165 * @param annotationName the fully qualified class name of the annotation 166 * type on which to find meta-annotations 167 * @return the names of all meta-annotations present on the annotation, 168 * or {@code null} if not found 169 * @see #getMetaAnnotationTypes(AnnotatedElement, Class) 170 * @see #hasMetaAnnotationTypes 171 */ 172 public static Set<String> getMetaAnnotationTypes(AnnotatedElement element, String annotationName) { 173 Assert.notNull(element, "AnnotatedElement must not be null"); 174 Assert.hasLength(annotationName, "'annotationName' must not be null or empty"); 175 176 return getMetaAnnotationTypes(element, AnnotationUtils.getAnnotation(element, annotationName)); 177 } 178 179 private static Set<String> getMetaAnnotationTypes(AnnotatedElement element, Annotation composed) { 180 if (composed == null) { 181 return null; 182 } 183 184 try { 185 final Set<String> types = new LinkedHashSet<String>(); 186 searchWithGetSemantics(composed.annotationType(), null, null, null, new SimpleAnnotationProcessor<Object>(true) { 187 @Override 188 public Object process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { 189 types.add(annotation.annotationType().getName()); 190 return CONTINUE; 191 } 192 }, new HashSet<AnnotatedElement>(), 1); 193 return (!types.isEmpty() ? types : null); 194 } 195 catch (Throwable ex) { 196 AnnotationUtils.rethrowAnnotationConfigurationException(ex); 197 throw new IllegalStateException("Failed to introspect annotations on " + element, ex); 198 } 199 } 200 201 /** 202 * Determine if the supplied {@link AnnotatedElement} is annotated with 203 * a <em>composed annotation</em> that is meta-annotated with an 204 * annotation of the specified {@code annotationType}. 205 * <p>This method follows <em>get semantics</em> as described in the 206 * {@linkplain AnnotatedElementUtils class-level javadoc}. 207 * @param element the annotated element 208 * @param annotationType the meta-annotation type to find 209 * @return {@code true} if a matching meta-annotation is present 210 * @since 4.2.3 211 * @see #getMetaAnnotationTypes 212 */ 213 public static boolean hasMetaAnnotationTypes(AnnotatedElement element, Class<? extends Annotation> annotationType) { 214 Assert.notNull(element, "AnnotatedElement must not be null"); 215 Assert.notNull(annotationType, "'annotationType' must not be null"); 216 217 return hasMetaAnnotationTypes(element, annotationType, null); 218 } 219 220 /** 221 * Determine if the supplied {@link AnnotatedElement} is annotated with a 222 * <em>composed annotation</em> that is meta-annotated with an annotation 223 * of the specified {@code annotationName}. 224 * <p>This method follows <em>get semantics</em> as described in the 225 * {@linkplain AnnotatedElementUtils class-level javadoc}. 226 * @param element the annotated element 227 * @param annotationName the fully qualified class name of the 228 * meta-annotation type to find 229 * @return {@code true} if a matching meta-annotation is present 230 * @see #getMetaAnnotationTypes 231 */ 232 public static boolean hasMetaAnnotationTypes(AnnotatedElement element, String annotationName) { 233 Assert.notNull(element, "AnnotatedElement must not be null"); 234 Assert.hasLength(annotationName, "'annotationName' must not be null or empty"); 235 236 return hasMetaAnnotationTypes(element, null, annotationName); 237 } 238 239 private static boolean hasMetaAnnotationTypes(AnnotatedElement element, Class<? extends Annotation> annotationType, 240 String annotationName) { 241 242 return Boolean.TRUE.equals( 243 searchWithGetSemantics(element, annotationType, annotationName, new SimpleAnnotationProcessor<Boolean>() { 244 @Override 245 public Boolean process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { 246 return (metaDepth > 0 ? Boolean.TRUE : CONTINUE); 247 } 248 })); 249 } 250 251 /** 252 * Determine if an annotation of the specified {@code annotationType} 253 * is <em>present</em> on the supplied {@link AnnotatedElement} or 254 * within the annotation hierarchy <em>above</em> the specified element. 255 * <p>If this method returns {@code true}, then {@link #getMergedAnnotationAttributes} 256 * will return a non-null value. 257 * <p>This method follows <em>get semantics</em> as described in the 258 * {@linkplain AnnotatedElementUtils class-level javadoc}. 259 * @param element the annotated element 260 * @param annotationType the annotation type to find 261 * @return {@code true} if a matching annotation is present 262 * @since 4.2.3 263 * @see #hasAnnotation(AnnotatedElement, Class) 264 */ 265 public static boolean isAnnotated(AnnotatedElement element, Class<? extends Annotation> annotationType) { 266 Assert.notNull(element, "AnnotatedElement must not be null"); 267 Assert.notNull(annotationType, "'annotationType' must not be null"); 268 269 // Shortcut: directly present on the element, with no processing needed? 270 if (element.isAnnotationPresent(annotationType)) { 271 return true; 272 } 273 return Boolean.TRUE.equals(searchWithGetSemantics(element, annotationType, null, alwaysTrueAnnotationProcessor)); 274 } 275 276 /** 277 * Determine if an annotation of the specified {@code annotationName} is 278 * <em>present</em> on the supplied {@link AnnotatedElement} or within the 279 * annotation hierarchy <em>above</em> the specified element. 280 * <p>If this method returns {@code true}, then {@link #getMergedAnnotationAttributes} 281 * will return a non-null value. 282 * <p>This method follows <em>get semantics</em> as described in the 283 * {@linkplain AnnotatedElementUtils class-level javadoc}. 284 * @param element the annotated element 285 * @param annotationName the fully qualified class name of the annotation type to find 286 * @return {@code true} if a matching annotation is present 287 */ 288 public static boolean isAnnotated(AnnotatedElement element, String annotationName) { 289 Assert.notNull(element, "AnnotatedElement must not be null"); 290 Assert.hasLength(annotationName, "'annotationName' must not be null or empty"); 291 292 return Boolean.TRUE.equals(searchWithGetSemantics(element, null, annotationName, alwaysTrueAnnotationProcessor)); 293 } 294 295 /** 296 * @deprecated As of Spring Framework 4.2, use {@link #getMergedAnnotationAttributes(AnnotatedElement, String)} instead. 297 */ 298 @Deprecated 299 public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement element, String annotationName) { 300 return getMergedAnnotationAttributes(element, annotationName); 301 } 302 303 /** 304 * @deprecated As of Spring Framework 4.2, use {@link #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)} instead. 305 */ 306 @Deprecated 307 public static AnnotationAttributes getAnnotationAttributes(AnnotatedElement element, String annotationName, 308 boolean classValuesAsString, boolean nestedAnnotationsAsMap) { 309 310 return getMergedAnnotationAttributes(element, annotationName, classValuesAsString, nestedAnnotationsAsMap); 311 } 312 313 /** 314 * Get the first annotation of the specified {@code annotationType} within 315 * the annotation hierarchy <em>above</em> the supplied {@code element} and 316 * merge that annotation's attributes with <em>matching</em> attributes from 317 * annotations in lower levels of the annotation hierarchy. 318 * <p>{@link AliasFor @AliasFor} semantics are fully supported, both 319 * within a single annotation and within the annotation hierarchy. 320 * <p>This method delegates to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)}. 321 * @param element the annotated element 322 * @param annotationType the annotation type to find 323 * @return the merged {@code AnnotationAttributes}, or {@code null} if not found 324 * @since 4.2 325 * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) 326 * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) 327 * @see #getMergedAnnotation(AnnotatedElement, Class) 328 * @see #findMergedAnnotation(AnnotatedElement, Class) 329 */ 330 public static AnnotationAttributes getMergedAnnotationAttributes( 331 AnnotatedElement element, Class<? extends Annotation> annotationType) { 332 333 Assert.notNull(annotationType, "'annotationType' must not be null"); 334 AnnotationAttributes attributes = searchWithGetSemantics(element, annotationType, null, 335 new MergedAnnotationAttributesProcessor()); 336 AnnotationUtils.postProcessAnnotationAttributes(element, attributes, false, false); 337 return attributes; 338 } 339 340 /** 341 * Get the first annotation of the specified {@code annotationName} within 342 * the annotation hierarchy <em>above</em> the supplied {@code element} and 343 * merge that annotation's attributes with <em>matching</em> attributes from 344 * annotations in lower levels of the annotation hierarchy. 345 * <p>{@link AliasFor @AliasFor} semantics are fully supported, both 346 * within a single annotation and within the annotation hierarchy. 347 * <p>This method delegates to {@link #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)}, 348 * supplying {@code false} for {@code classValuesAsString} and {@code nestedAnnotationsAsMap}. 349 * @param element the annotated element 350 * @param annotationName the fully qualified class name of the annotation type to find 351 * @return the merged {@code AnnotationAttributes}, or {@code null} if not found 352 * @since 4.2 353 * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) 354 * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) 355 * @see #findMergedAnnotation(AnnotatedElement, Class) 356 * @see #getAllAnnotationAttributes(AnnotatedElement, String) 357 */ 358 public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element, String annotationName) { 359 return getMergedAnnotationAttributes(element, annotationName, false, false); 360 } 361 362 /** 363 * Get the first annotation of the specified {@code annotationName} within 364 * the annotation hierarchy <em>above</em> the supplied {@code element} and 365 * merge that annotation's attributes with <em>matching</em> attributes from 366 * annotations in lower levels of the annotation hierarchy. 367 * <p>Attributes from lower levels in the annotation hierarchy override attributes 368 * of the same name from higher levels, and {@link AliasFor @AliasFor} semantics are 369 * fully supported, both within a single annotation and within the annotation hierarchy. 370 * <p>In contrast to {@link #getAllAnnotationAttributes}, the search algorithm used by 371 * this method will stop searching the annotation hierarchy once the first annotation 372 * of the specified {@code annotationName} has been found. As a consequence, 373 * additional annotations of the specified {@code annotationName} will be ignored. 374 * <p>This method follows <em>get semantics</em> as described in the 375 * {@linkplain AnnotatedElementUtils class-level javadoc}. 376 * @param element the annotated element 377 * @param annotationName the fully qualified class name of the annotation type to find 378 * @param classValuesAsString whether to convert Class references into Strings or to 379 * preserve them as Class references 380 * @param nestedAnnotationsAsMap whether to convert nested Annotation instances 381 * into {@code AnnotationAttributes} maps or to preserve them as Annotation instances 382 * @return the merged {@code AnnotationAttributes}, or {@code null} if not found 383 * @since 4.2 384 * @see #findMergedAnnotation(AnnotatedElement, Class) 385 * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) 386 * @see #getAllAnnotationAttributes(AnnotatedElement, String, boolean, boolean) 387 */ 388 public static AnnotationAttributes getMergedAnnotationAttributes(AnnotatedElement element, 389 String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { 390 391 Assert.hasLength(annotationName, "'annotationName' must not be null or empty"); 392 AnnotationAttributes attributes = searchWithGetSemantics(element, null, annotationName, 393 new MergedAnnotationAttributesProcessor(classValuesAsString, nestedAnnotationsAsMap)); 394 AnnotationUtils.postProcessAnnotationAttributes(element, attributes, classValuesAsString, nestedAnnotationsAsMap); 395 return attributes; 396 } 397 398 /** 399 * Get the first annotation of the specified {@code annotationType} within 400 * the annotation hierarchy <em>above</em> the supplied {@code element}, 401 * merge that annotation's attributes with <em>matching</em> attributes from 402 * annotations in lower levels of the annotation hierarchy, and synthesize 403 * the result back into an annotation of the specified {@code annotationType}. 404 * <p>{@link AliasFor @AliasFor} semantics are fully supported, both 405 * within a single annotation and within the annotation hierarchy. 406 * <p>This method delegates to {@link #getMergedAnnotationAttributes(AnnotatedElement, Class)} 407 * and {@link AnnotationUtils#synthesizeAnnotation(Map, Class, AnnotatedElement)}. 408 * @param element the annotated element 409 * @param annotationType the annotation type to find 410 * @return the merged, synthesized {@code Annotation}, or {@code null} if not found 411 * @since 4.2 412 * @see #getMergedAnnotationAttributes(AnnotatedElement, Class) 413 * @see #findMergedAnnotation(AnnotatedElement, Class) 414 * @see AnnotationUtils#synthesizeAnnotation(Map, Class, AnnotatedElement) 415 */ 416 public static <A extends Annotation> A getMergedAnnotation(AnnotatedElement element, Class<A> annotationType) { 417 Assert.notNull(annotationType, "'annotationType' must not be null"); 418 419 // Shortcut: directly present on the element, with no merging needed? 420 if (!(element instanceof Class)) { 421 // Do not use this shortcut against a Class: Inherited annotations 422 // would get preferred over locally declared composed annotations. 423 A annotation = element.getAnnotation(annotationType); 424 if (annotation != null) { 425 return AnnotationUtils.synthesizeAnnotation(annotation, element); 426 } 427 } 428 429 // Exhaustive retrieval of merged annotation attributes... 430 AnnotationAttributes attributes = getMergedAnnotationAttributes(element, annotationType); 431 return AnnotationUtils.synthesizeAnnotation(attributes, annotationType, element); 432 } 433 434 /** 435 * Get <strong>all</strong> annotations of the specified {@code annotationType} 436 * within the annotation hierarchy <em>above</em> the supplied {@code element}; 437 * and for each annotation found, merge that annotation's attributes with 438 * <em>matching</em> attributes from annotations in lower levels of the annotation 439 * hierarchy and synthesize the results back into an annotation of the specified 440 * {@code annotationType}. 441 * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a 442 * single annotation and within annotation hierarchies. 443 * <p>This method follows <em>get semantics</em> as described in the 444 * {@linkplain AnnotatedElementUtils class-level javadoc}. 445 * @param element the annotated element (never {@code null}) 446 * @param annotationType the annotation type to find (never {@code null}) 447 * @return the set of all merged, synthesized {@code Annotations} found, 448 * or an empty set if none were found 449 * @since 4.3 450 * @see #getMergedAnnotation(AnnotatedElement, Class) 451 * @see #getAllAnnotationAttributes(AnnotatedElement, String) 452 * @see #findAllMergedAnnotations(AnnotatedElement, Class) 453 */ 454 public static <A extends Annotation> Set<A> getAllMergedAnnotations(AnnotatedElement element, 455 Class<A> annotationType) { 456 457 Assert.notNull(element, "AnnotatedElement must not be null"); 458 Assert.notNull(annotationType, "'annotationType' must not be null"); 459 460 MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true); 461 searchWithGetSemantics(element, annotationType, null, processor); 462 return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults()); 463 } 464 465 /** 466 * Get all <em>repeatable annotations</em> of the specified {@code annotationType} 467 * within the annotation hierarchy <em>above</em> the supplied {@code element}; 468 * and for each annotation found, merge that annotation's attributes with 469 * <em>matching</em> attributes from annotations in lower levels of the annotation 470 * hierarchy and synthesize the results back into an annotation of the specified 471 * {@code annotationType}. 472 * <p>The container type that holds the repeatable annotations will be looked up 473 * via {@link java.lang.annotation.Repeatable}. 474 * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a 475 * single annotation and within annotation hierarchies. 476 * <p>This method follows <em>get semantics</em> as described in the 477 * {@linkplain AnnotatedElementUtils class-level javadoc}. 478 * @param element the annotated element (never {@code null}) 479 * @param annotationType the annotation type to find (never {@code null}) 480 * @return the set of all merged repeatable {@code Annotations} found, 481 * or an empty set if none were found 482 * @throws IllegalArgumentException if the {@code element} or {@code annotationType} 483 * is {@code null}, or if the container type cannot be resolved 484 * @since 4.3 485 * @see #getMergedAnnotation(AnnotatedElement, Class) 486 * @see #getAllMergedAnnotations(AnnotatedElement, Class) 487 * @see #getMergedRepeatableAnnotations(AnnotatedElement, Class, Class) 488 */ 489 public static <A extends Annotation> Set<A> getMergedRepeatableAnnotations(AnnotatedElement element, 490 Class<A> annotationType) { 491 492 return getMergedRepeatableAnnotations(element, annotationType, null); 493 } 494 495 /** 496 * Get all <em>repeatable annotations</em> of the specified {@code annotationType} 497 * within the annotation hierarchy <em>above</em> the supplied {@code element}; 498 * and for each annotation found, merge that annotation's attributes with 499 * <em>matching</em> attributes from annotations in lower levels of the annotation 500 * hierarchy and synthesize the results back into an annotation of the specified 501 * {@code annotationType}. 502 * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a 503 * single annotation and within annotation hierarchies. 504 * <p>This method follows <em>get semantics</em> as described in the 505 * {@linkplain AnnotatedElementUtils class-level javadoc}. 506 * @param element the annotated element (never {@code null}) 507 * @param annotationType the annotation type to find (never {@code null}) 508 * @param containerType the type of the container that holds the annotations; 509 * may be {@code null} if the container type should be looked up via 510 * {@link java.lang.annotation.Repeatable} 511 * @return the set of all merged repeatable {@code Annotations} found, 512 * or an empty set if none were found 513 * @throws IllegalArgumentException if the {@code element} or {@code annotationType} 514 * is {@code null}, or if the container type cannot be resolved 515 * @throws AnnotationConfigurationException if the supplied {@code containerType} 516 * is not a valid container annotation for the supplied {@code annotationType} 517 * @since 4.3 518 * @see #getMergedAnnotation(AnnotatedElement, Class) 519 * @see #getAllMergedAnnotations(AnnotatedElement, Class) 520 */ 521 public static <A extends Annotation> Set<A> getMergedRepeatableAnnotations(AnnotatedElement element, 522 Class<A> annotationType, Class<? extends Annotation> containerType) { 523 524 Assert.notNull(element, "AnnotatedElement must not be null"); 525 Assert.notNull(annotationType, "'annotationType' must not be null"); 526 527 if (containerType == null) { 528 containerType = resolveContainerType(annotationType); 529 } 530 else { 531 validateContainerType(annotationType, containerType); 532 } 533 534 MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true); 535 searchWithGetSemantics(element, annotationType, null, containerType, processor); 536 return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults()); 537 } 538 539 /** 540 * Get the annotation attributes of <strong>all</strong> annotations of the specified 541 * {@code annotationName} in the annotation hierarchy above the supplied 542 * {@link AnnotatedElement} and store the results in a {@link MultiValueMap}. 543 * <p>Note: in contrast to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)}, 544 * this method does <em>not</em> support attribute overrides. 545 * <p>This method follows <em>get semantics</em> as described in the 546 * {@linkplain AnnotatedElementUtils class-level javadoc}. 547 * @param element the annotated element 548 * @param annotationName the fully qualified class name of the annotation type to find 549 * @return a {@link MultiValueMap} keyed by attribute name, containing the annotation 550 * attributes from all annotations found, or {@code null} if not found 551 * @see #getAllAnnotationAttributes(AnnotatedElement, String, boolean, boolean) 552 */ 553 public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element, String annotationName) { 554 return getAllAnnotationAttributes(element, annotationName, false, false); 555 } 556 557 /** 558 * Get the annotation attributes of <strong>all</strong> annotations of 559 * the specified {@code annotationName} in the annotation hierarchy above 560 * the supplied {@link AnnotatedElement} and store the results in a 561 * {@link MultiValueMap}. 562 * <p>Note: in contrast to {@link #getMergedAnnotationAttributes(AnnotatedElement, String)}, 563 * this method does <em>not</em> support attribute overrides. 564 * <p>This method follows <em>get semantics</em> as described in the 565 * {@linkplain AnnotatedElementUtils class-level javadoc}. 566 * @param element the annotated element 567 * @param annotationName the fully qualified class name of the annotation type to find 568 * @param classValuesAsString whether to convert Class references into Strings or to 569 * preserve them as Class references 570 * @param nestedAnnotationsAsMap whether to convert nested Annotation instances into 571 * {@code AnnotationAttributes} maps or to preserve them as Annotation instances 572 * @return a {@link MultiValueMap} keyed by attribute name, containing the annotation 573 * attributes from all annotations found, or {@code null} if not found 574 */ 575 public static MultiValueMap<String, Object> getAllAnnotationAttributes(AnnotatedElement element, 576 String annotationName, final boolean classValuesAsString, final boolean nestedAnnotationsAsMap) { 577 578 final MultiValueMap<String, Object> attributesMap = new LinkedMultiValueMap<String, Object>(); 579 580 searchWithGetSemantics(element, null, annotationName, new SimpleAnnotationProcessor<Object>() { 581 @Override 582 public Object process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { 583 AnnotationAttributes annotationAttributes = AnnotationUtils.getAnnotationAttributes( 584 annotation, classValuesAsString, nestedAnnotationsAsMap); 585 for (Map.Entry<String, Object> entry : annotationAttributes.entrySet()) { 586 attributesMap.add(entry.getKey(), entry.getValue()); 587 } 588 return CONTINUE; 589 } 590 }); 591 592 return (!attributesMap.isEmpty() ? attributesMap : null); 593 } 594 595 /** 596 * Determine if an annotation of the specified {@code annotationType} 597 * is <em>available</em> on the supplied {@link AnnotatedElement} or 598 * within the annotation hierarchy <em>above</em> the specified element. 599 * <p>If this method returns {@code true}, then {@link #findMergedAnnotationAttributes} 600 * will return a non-null value. 601 * <p>This method follows <em>find semantics</em> as described in the 602 * {@linkplain AnnotatedElementUtils class-level javadoc}. 603 * @param element the annotated element 604 * @param annotationType the annotation type to find 605 * @return {@code true} if a matching annotation is present 606 * @since 4.3 607 * @see #isAnnotated(AnnotatedElement, Class) 608 */ 609 public static boolean hasAnnotation(AnnotatedElement element, Class<? extends Annotation> annotationType) { 610 Assert.notNull(element, "AnnotatedElement must not be null"); 611 Assert.notNull(annotationType, "'annotationType' must not be null"); 612 613 // Shortcut: directly present on the element, with no processing needed? 614 if (element.isAnnotationPresent(annotationType)) { 615 return true; 616 } 617 return Boolean.TRUE.equals(searchWithFindSemantics(element, annotationType, null, alwaysTrueAnnotationProcessor)); 618 } 619 620 /** 621 * Find the first annotation of the specified {@code annotationType} within 622 * the annotation hierarchy <em>above</em> the supplied {@code element} and 623 * merge that annotation's attributes with <em>matching</em> attributes from 624 * annotations in lower levels of the annotation hierarchy. 625 * <p>Attributes from lower levels in the annotation hierarchy override 626 * attributes of the same name from higher levels, and 627 * {@link AliasFor @AliasFor} semantics are fully supported, both 628 * within a single annotation and within the annotation hierarchy. 629 * <p>In contrast to {@link #getAllAnnotationAttributes}, the search algorithm 630 * used by this method will stop searching the annotation hierarchy once the 631 * first annotation of the specified {@code annotationType} has been found. 632 * As a consequence, additional annotations of the specified 633 * {@code annotationType} will be ignored. 634 * <p>This method follows <em>find semantics</em> as described in the 635 * {@linkplain AnnotatedElementUtils class-level javadoc}. 636 * @param element the annotated element 637 * @param annotationType the annotation type to find 638 * @param classValuesAsString whether to convert Class references into 639 * Strings or to preserve them as Class references 640 * @param nestedAnnotationsAsMap whether to convert nested Annotation instances into 641 * {@code AnnotationAttributes} maps or to preserve them as Annotation instances 642 * @return the merged {@code AnnotationAttributes}, or {@code null} if not found 643 * @since 4.2 644 * @see #findMergedAnnotation(AnnotatedElement, Class) 645 * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) 646 */ 647 public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element, 648 Class<? extends Annotation> annotationType, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { 649 650 AnnotationAttributes attributes = searchWithFindSemantics(element, annotationType, null, 651 new MergedAnnotationAttributesProcessor(classValuesAsString, nestedAnnotationsAsMap)); 652 AnnotationUtils.postProcessAnnotationAttributes(element, attributes, classValuesAsString, nestedAnnotationsAsMap); 653 return attributes; 654 } 655 656 /** 657 * Find the first annotation of the specified {@code annotationName} within 658 * the annotation hierarchy <em>above</em> the supplied {@code element} and 659 * merge that annotation's attributes with <em>matching</em> attributes from 660 * annotations in lower levels of the annotation hierarchy. 661 * <p>Attributes from lower levels in the annotation hierarchy override 662 * attributes of the same name from higher levels, and 663 * {@link AliasFor @AliasFor} semantics are fully supported, both 664 * within a single annotation and within the annotation hierarchy. 665 * <p>In contrast to {@link #getAllAnnotationAttributes}, the search 666 * algorithm used by this method will stop searching the annotation 667 * hierarchy once the first annotation of the specified 668 * {@code annotationName} has been found. As a consequence, additional 669 * annotations of the specified {@code annotationName} will be ignored. 670 * <p>This method follows <em>find semantics</em> as described in the 671 * {@linkplain AnnotatedElementUtils class-level javadoc}. 672 * @param element the annotated element 673 * @param annotationName the fully qualified class name of the annotation type to find 674 * @param classValuesAsString whether to convert Class references into Strings or to 675 * preserve them as Class references 676 * @param nestedAnnotationsAsMap whether to convert nested Annotation instances into 677 * {@code AnnotationAttributes} maps or to preserve them as Annotation instances 678 * @return the merged {@code AnnotationAttributes}, or {@code null} if not found 679 * @since 4.2 680 * @see #findMergedAnnotation(AnnotatedElement, Class) 681 * @see #getMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) 682 */ 683 public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element, 684 String annotationName, boolean classValuesAsString, boolean nestedAnnotationsAsMap) { 685 686 AnnotationAttributes attributes = searchWithFindSemantics(element, null, annotationName, 687 new MergedAnnotationAttributesProcessor(classValuesAsString, nestedAnnotationsAsMap)); 688 AnnotationUtils.postProcessAnnotationAttributes(element, attributes, classValuesAsString, nestedAnnotationsAsMap); 689 return attributes; 690 } 691 692 /** 693 * Find the first annotation of the specified {@code annotationType} within 694 * the annotation hierarchy <em>above</em> the supplied {@code element}, 695 * merge that annotation's attributes with <em>matching</em> attributes from 696 * annotations in lower levels of the annotation hierarchy, and synthesize 697 * the result back into an annotation of the specified {@code annotationType}. 698 * <p>{@link AliasFor @AliasFor} semantics are fully supported, both 699 * within a single annotation and within the annotation hierarchy. 700 * <p>This method follows <em>find semantics</em> as described in the 701 * {@linkplain AnnotatedElementUtils class-level javadoc}. 702 * @param element the annotated element 703 * @param annotationType the annotation type to find 704 * @return the merged, synthesized {@code Annotation}, or {@code null} if not found 705 * @since 4.2 706 * @see #findAllMergedAnnotations(AnnotatedElement, Class) 707 * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) 708 * @see #getMergedAnnotationAttributes(AnnotatedElement, Class) 709 */ 710 public static <A extends Annotation> A findMergedAnnotation(AnnotatedElement element, Class<A> annotationType) { 711 Assert.notNull(annotationType, "'annotationType' must not be null"); 712 713 // Shortcut: directly present on the element, with no merging needed? 714 if (!(element instanceof Class)) { 715 // Do not use this shortcut against a Class: Inherited annotations 716 // would get preferred over locally declared composed annotations. 717 A annotation = element.getAnnotation(annotationType); 718 if (annotation != null) { 719 return AnnotationUtils.synthesizeAnnotation(annotation, element); 720 } 721 } 722 723 // Exhaustive retrieval of merged annotation attributes... 724 AnnotationAttributes attributes = findMergedAnnotationAttributes(element, annotationType, false, false); 725 return AnnotationUtils.synthesizeAnnotation(attributes, annotationType, element); 726 } 727 728 /** 729 * Find the first annotation of the specified {@code annotationName} within 730 * the annotation hierarchy <em>above</em> the supplied {@code element}, 731 * merge that annotation's attributes with <em>matching</em> attributes from 732 * annotations in lower levels of the annotation hierarchy, and synthesize 733 * the result back into an annotation of the specified {@code annotationName}. 734 * <p>{@link AliasFor @AliasFor} semantics are fully supported, both 735 * within a single annotation and within the annotation hierarchy. 736 * <p>This method delegates to {@link #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean)} 737 * (supplying {@code false} for {@code classValuesAsString} and {@code nestedAnnotationsAsMap}) 738 * and {@link AnnotationUtils#synthesizeAnnotation(Map, Class, AnnotatedElement)}. 739 * <p>This method follows <em>find semantics</em> as described in the 740 * {@linkplain AnnotatedElementUtils class-level javadoc}. 741 * @param element the annotated element 742 * @param annotationName the fully qualified class name of the annotation type to find 743 * @return the merged, synthesized {@code Annotation}, or {@code null} if not found 744 * @since 4.2 745 * @see #findMergedAnnotation(AnnotatedElement, Class) 746 * @see #findMergedAnnotationAttributes(AnnotatedElement, String, boolean, boolean) 747 * @see AnnotationUtils#synthesizeAnnotation(Map, Class, AnnotatedElement) 748 * @deprecated As of Spring Framework 4.2.3, use {@link #findMergedAnnotation(AnnotatedElement, Class)} instead. 749 */ 750 @Deprecated 751 @SuppressWarnings("unchecked") 752 public static <A extends Annotation> A findMergedAnnotation(AnnotatedElement element, String annotationName) { 753 AnnotationAttributes attributes = findMergedAnnotationAttributes(element, annotationName, false, false); 754 return AnnotationUtils.synthesizeAnnotation(attributes, (Class<A>) attributes.annotationType(), element); 755 } 756 757 /** 758 * Find <strong>all</strong> annotations of the specified {@code annotationType} 759 * within the annotation hierarchy <em>above</em> the supplied {@code element}; 760 * and for each annotation found, merge that annotation's attributes with 761 * <em>matching</em> attributes from annotations in lower levels of the annotation 762 * hierarchy and synthesize the results back into an annotation of the specified 763 * {@code annotationType}. 764 * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a 765 * single annotation and within annotation hierarchies. 766 * <p>This method follows <em>find semantics</em> as described in the 767 * {@linkplain AnnotatedElementUtils class-level javadoc}. 768 * @param element the annotated element (never {@code null}) 769 * @param annotationType the annotation type to find (never {@code null}) 770 * @return the set of all merged, synthesized {@code Annotations} found, 771 * or an empty set if none were found 772 * @since 4.3 773 * @see #findMergedAnnotation(AnnotatedElement, Class) 774 * @see #getAllMergedAnnotations(AnnotatedElement, Class) 775 */ 776 public static <A extends Annotation> Set<A> findAllMergedAnnotations(AnnotatedElement element, 777 Class<A> annotationType) { 778 779 Assert.notNull(element, "AnnotatedElement must not be null"); 780 Assert.notNull(annotationType, "'annotationType' must not be null"); 781 782 MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true); 783 searchWithFindSemantics(element, annotationType, null, processor); 784 return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults()); 785 } 786 787 /** 788 * Find all <em>repeatable annotations</em> of the specified {@code annotationType} 789 * within the annotation hierarchy <em>above</em> the supplied {@code element}; 790 * and for each annotation found, merge that annotation's attributes with 791 * <em>matching</em> attributes from annotations in lower levels of the annotation 792 * hierarchy and synthesize the results back into an annotation of the specified 793 * {@code annotationType}. 794 * <p>The container type that holds the repeatable annotations will be looked up 795 * via {@link java.lang.annotation.Repeatable}. 796 * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a 797 * single annotation and within annotation hierarchies. 798 * <p>This method follows <em>find semantics</em> as described in the 799 * {@linkplain AnnotatedElementUtils class-level javadoc}. 800 * @param element the annotated element (never {@code null}) 801 * @param annotationType the annotation type to find (never {@code null}) 802 * @return the set of all merged repeatable {@code Annotations} found, 803 * or an empty set if none were found 804 * @throws IllegalArgumentException if the {@code element} or {@code annotationType} 805 * is {@code null}, or if the container type cannot be resolved 806 * @since 4.3 807 * @see #findMergedAnnotation(AnnotatedElement, Class) 808 * @see #findAllMergedAnnotations(AnnotatedElement, Class) 809 * @see #findMergedRepeatableAnnotations(AnnotatedElement, Class, Class) 810 */ 811 public static <A extends Annotation> Set<A> findMergedRepeatableAnnotations(AnnotatedElement element, 812 Class<A> annotationType) { 813 814 return findMergedRepeatableAnnotations(element, annotationType, null); 815 } 816 817 /** 818 * Find all <em>repeatable annotations</em> of the specified {@code annotationType} 819 * within the annotation hierarchy <em>above</em> the supplied {@code element}; 820 * and for each annotation found, merge that annotation's attributes with 821 * <em>matching</em> attributes from annotations in lower levels of the annotation 822 * hierarchy and synthesize the results back into an annotation of the specified 823 * {@code annotationType}. 824 * <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a 825 * single annotation and within annotation hierarchies. 826 * <p>This method follows <em>find semantics</em> as described in the 827 * {@linkplain AnnotatedElementUtils class-level javadoc}. 828 * @param element the annotated element (never {@code null}) 829 * @param annotationType the annotation type to find (never {@code null}) 830 * @param containerType the type of the container that holds the annotations; 831 * may be {@code null} if the container type should be looked up via 832 * {@link java.lang.annotation.Repeatable} 833 * @return the set of all merged repeatable {@code Annotations} found, 834 * or an empty set if none were found 835 * @throws IllegalArgumentException if the {@code element} or {@code annotationType} 836 * is {@code null}, or if the container type cannot be resolved 837 * @throws AnnotationConfigurationException if the supplied {@code containerType} 838 * is not a valid container annotation for the supplied {@code annotationType} 839 * @since 4.3 840 * @see #findMergedAnnotation(AnnotatedElement, Class) 841 * @see #findAllMergedAnnotations(AnnotatedElement, Class) 842 */ 843 public static <A extends Annotation> Set<A> findMergedRepeatableAnnotations(AnnotatedElement element, 844 Class<A> annotationType, Class<? extends Annotation> containerType) { 845 846 Assert.notNull(element, "AnnotatedElement must not be null"); 847 Assert.notNull(annotationType, "'annotationType' must not be null"); 848 849 if (containerType == null) { 850 containerType = resolveContainerType(annotationType); 851 } 852 else { 853 validateContainerType(annotationType, containerType); 854 } 855 856 MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true); 857 searchWithFindSemantics(element, annotationType, null, containerType, processor); 858 return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults()); 859 } 860 861 /** 862 * Search for annotations of the specified {@code annotationName} or 863 * {@code annotationType} on the specified {@code element}, following 864 * <em>get semantics</em>. 865 * @param element the annotated element 866 * @param annotationType the annotation type to find 867 * @param annotationName the fully qualified class name of the annotation 868 * type to find (as an alternative to {@code annotationType}) 869 * @param processor the processor to delegate to 870 * @return the result of the processor (potentially {@code null}) 871 */ 872 private static <T> T searchWithGetSemantics(AnnotatedElement element, 873 Class<? extends Annotation> annotationType, String annotationName, Processor<T> processor) { 874 875 return searchWithGetSemantics(element, annotationType, annotationName, null, processor); 876 } 877 878 /** 879 * Search for annotations of the specified {@code annotationName} or 880 * {@code annotationType} on the specified {@code element}, following 881 * <em>get semantics</em>. 882 * @param element the annotated element 883 * @param annotationType the annotation type to find 884 * @param annotationName the fully qualified class name of the annotation 885 * type to find (as an alternative to {@code annotationType}) 886 * @param containerType the type of the container that holds repeatable 887 * annotations, or {@code null} if the annotation is not repeatable 888 * @param processor the processor to delegate to 889 * @return the result of the processor (potentially {@code null}) 890 * @since 4.3 891 */ 892 private static <T> T searchWithGetSemantics(AnnotatedElement element, 893 Class<? extends Annotation> annotationType, String annotationName, 894 Class<? extends Annotation> containerType, Processor<T> processor) { 895 896 try { 897 return searchWithGetSemantics(element, annotationType, annotationName, 898 containerType, processor, new HashSet<AnnotatedElement>(), 0); 899 } 900 catch (Throwable ex) { 901 AnnotationUtils.rethrowAnnotationConfigurationException(ex); 902 throw new IllegalStateException("Failed to introspect annotations on " + element, ex); 903 } 904 } 905 906 /** 907 * Perform the search algorithm for the {@link #searchWithGetSemantics} 908 * method, avoiding endless recursion by tracking which annotated elements 909 * have already been <em>visited</em>. 910 * <p>The {@code metaDepth} parameter is explained in the 911 * {@link Processor#process process()} method of the {@link Processor} API. 912 * @param element the annotated element 913 * @param annotationType the annotation type to find 914 * @param annotationName the fully qualified class name of the annotation 915 * type to find (as an alternative to {@code annotationType}) 916 * @param containerType the type of the container that holds repeatable 917 * annotations, or {@code null} if the annotation is not repeatable 918 * @param processor the processor to delegate to 919 * @param visited the set of annotated elements that have already been visited 920 * @param metaDepth the meta-depth of the annotation 921 * @return the result of the processor (potentially {@code null}) 922 */ 923 private static <T> T searchWithGetSemantics(AnnotatedElement element, 924 Class<? extends Annotation> annotationType, String annotationName, 925 Class<? extends Annotation> containerType, Processor<T> processor, 926 Set<AnnotatedElement> visited, int metaDepth) { 927 928 Assert.notNull(element, "AnnotatedElement must not be null"); 929 930 if (visited.add(element)) { 931 try { 932 // Start searching within locally declared annotations 933 List<Annotation> declaredAnnotations = Arrays.asList(element.getDeclaredAnnotations()); 934 T result = searchWithGetSemanticsInAnnotations(element, declaredAnnotations, 935 annotationType, annotationName, containerType, processor, visited, metaDepth); 936 if (result != null) { 937 return result; 938 } 939 940 if (element instanceof Class) { // otherwise getAnnotations does not return anything new 941 List<Annotation> inheritedAnnotations = new ArrayList<Annotation>(); 942 for (Annotation annotation : element.getAnnotations()) { 943 if (!declaredAnnotations.contains(annotation)) { 944 inheritedAnnotations.add(annotation); 945 } 946 } 947 948 // Continue searching within inherited annotations 949 result = searchWithGetSemanticsInAnnotations(element, inheritedAnnotations, 950 annotationType, annotationName, containerType, processor, visited, metaDepth); 951 if (result != null) { 952 return result; 953 } 954 } 955 } 956 catch (Throwable ex) { 957 AnnotationUtils.handleIntrospectionFailure(element, ex); 958 } 959 } 960 961 return null; 962 } 963 964 /** 965 * This method is invoked by {@link #searchWithGetSemantics} to perform 966 * the actual search within the supplied list of annotations. 967 * <p>This method should be invoked first with locally declared annotations 968 * and then subsequently with inherited annotations, thereby allowing 969 * local annotations to take precedence over inherited annotations. 970 * <p>The {@code metaDepth} parameter is explained in the 971 * {@link Processor#process process()} method of the {@link Processor} API. 972 * @param element the element that is annotated with the supplied 973 * annotations, used for contextual logging; may be {@code null} if unknown 974 * @param annotations the annotations to search in 975 * @param annotationType the annotation type to find 976 * @param annotationName the fully qualified class name of the annotation 977 * type to find (as an alternative to {@code annotationType}) 978 * @param containerType the type of the container that holds repeatable 979 * annotations, or {@code null} if the annotation is not repeatable 980 * @param processor the processor to delegate to 981 * @param visited the set of annotated elements that have already been visited 982 * @param metaDepth the meta-depth of the annotation 983 * @return the result of the processor (potentially {@code null}) 984 * @since 4.2 985 */ 986 private static <T> T searchWithGetSemanticsInAnnotations(AnnotatedElement element, 987 List<Annotation> annotations, Class<? extends Annotation> annotationType, 988 String annotationName, Class<? extends Annotation> containerType, 989 Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth) { 990 991 // Search in annotations 992 for (Annotation annotation : annotations) { 993 Class<? extends Annotation> currentAnnotationType = annotation.annotationType(); 994 if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) { 995 if (currentAnnotationType == annotationType || 996 currentAnnotationType.getName().equals(annotationName) || 997 processor.alwaysProcesses()) { 998 T result = processor.process(element, annotation, metaDepth); 999 if (result != null) { 1000 if (processor.aggregates() && metaDepth == 0) { 1001 processor.getAggregatedResults().add(result); 1002 } 1003 else { 1004 return result; 1005 } 1006 } 1007 } 1008 // Repeatable annotations in container? 1009 else if (currentAnnotationType == containerType) { 1010 for (Annotation contained : getRawAnnotationsFromContainer(element, annotation)) { 1011 T result = processor.process(element, contained, metaDepth); 1012 if (result != null) { 1013 // No need to post-process since repeatable annotations within a 1014 // container cannot be composed annotations. 1015 processor.getAggregatedResults().add(result); 1016 } 1017 } 1018 } 1019 } 1020 } 1021 1022 // Recursively search in meta-annotations 1023 for (Annotation annotation : annotations) { 1024 Class<? extends Annotation> currentAnnotationType = annotation.annotationType(); 1025 if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) { 1026 T result = searchWithGetSemantics(currentAnnotationType, annotationType, 1027 annotationName, containerType, processor, visited, metaDepth + 1); 1028 if (result != null) { 1029 processor.postProcess(element, annotation, result); 1030 if (processor.aggregates() && metaDepth == 0) { 1031 processor.getAggregatedResults().add(result); 1032 } 1033 else { 1034 return result; 1035 } 1036 } 1037 } 1038 } 1039 1040 return null; 1041 } 1042 1043 /** 1044 * Search for annotations of the specified {@code annotationName} or 1045 * {@code annotationType} on the specified {@code element}, following 1046 * <em>find semantics</em>. 1047 * @param element the annotated element 1048 * @param annotationType the annotation type to find 1049 * @param annotationName the fully qualified class name of the annotation 1050 * type to find (as an alternative to {@code annotationType}) 1051 * @param processor the processor to delegate to 1052 * @return the result of the processor (potentially {@code null}) 1053 * @since 4.2 1054 */ 1055 private static <T> T searchWithFindSemantics(AnnotatedElement element, 1056 Class<? extends Annotation> annotationType, 1057 String annotationName, Processor<T> processor) { 1058 1059 return searchWithFindSemantics(element, annotationType, annotationName, null, processor); 1060 } 1061 1062 /** 1063 * Search for annotations of the specified {@code annotationName} or 1064 * {@code annotationType} on the specified {@code element}, following 1065 * <em>find semantics</em>. 1066 * @param element the annotated element 1067 * @param annotationType the annotation type to find 1068 * @param annotationName the fully qualified class name of the annotation 1069 * type to find (as an alternative to {@code annotationType}) 1070 * @param containerType the type of the container that holds repeatable 1071 * annotations, or {@code null} if the annotation is not repeatable 1072 * @param processor the processor to delegate to 1073 * @return the result of the processor (potentially {@code null}) 1074 * @since 4.3 1075 */ 1076 private static <T> T searchWithFindSemantics(AnnotatedElement element, 1077 Class<? extends Annotation> annotationType, String annotationName, 1078 Class<? extends Annotation> containerType, Processor<T> processor) { 1079 1080 if (containerType != null && !processor.aggregates()) { 1081 throw new IllegalArgumentException( 1082 "Searches for repeatable annotations must supply an aggregating Processor"); 1083 } 1084 1085 try { 1086 return searchWithFindSemantics(element, annotationType, annotationName, 1087 containerType, processor, new HashSet<AnnotatedElement>(), 0); 1088 } 1089 catch (Throwable ex) { 1090 AnnotationUtils.rethrowAnnotationConfigurationException(ex); 1091 throw new IllegalStateException("Failed to introspect annotations on " + element, ex); 1092 } 1093 } 1094 1095 /** 1096 * Perform the search algorithm for the {@link #searchWithFindSemantics} 1097 * method, avoiding endless recursion by tracking which annotated elements 1098 * have already been <em>visited</em>. 1099 * <p>The {@code metaDepth} parameter is explained in the 1100 * {@link Processor#process process()} method of the {@link Processor} API. 1101 * @param element the annotated element (never {@code null}) 1102 * @param annotationType the annotation type to find 1103 * @param annotationName the fully qualified class name of the annotation 1104 * type to find (as an alternative to {@code annotationType}) 1105 * @param containerType the type of the container that holds repeatable 1106 * annotations, or {@code null} if the annotation is not repeatable 1107 * @param processor the processor to delegate to 1108 * @param visited the set of annotated elements that have already been visited 1109 * @param metaDepth the meta-depth of the annotation 1110 * @return the result of the processor (potentially {@code null}) 1111 * @since 4.2 1112 */ 1113 private static <T> T searchWithFindSemantics(AnnotatedElement element, Class<? extends Annotation> annotationType, 1114 String annotationName, Class<? extends Annotation> containerType, Processor<T> processor, 1115 Set<AnnotatedElement> visited, int metaDepth) { 1116 1117 Assert.notNull(element, "AnnotatedElement must not be null"); 1118 1119 if (visited.add(element)) { 1120 try { 1121 // Locally declared annotations (ignoring @Inherited) 1122 Annotation[] annotations = element.getDeclaredAnnotations(); 1123 if (annotations.length > 0) { 1124 List<T> aggregatedResults = (processor.aggregates() ? new ArrayList<T>() : null); 1125 1126 // Search in local annotations 1127 for (Annotation annotation : annotations) { 1128 Class<? extends Annotation> currentAnnotationType = annotation.annotationType(); 1129 if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) { 1130 if (currentAnnotationType == annotationType || 1131 currentAnnotationType.getName().equals(annotationName) || 1132 processor.alwaysProcesses()) { 1133 T result = processor.process(element, annotation, metaDepth); 1134 if (result != null) { 1135 if (aggregatedResults != null && metaDepth == 0) { 1136 aggregatedResults.add(result); 1137 } 1138 else { 1139 return result; 1140 } 1141 } 1142 } 1143 // Repeatable annotations in container? 1144 else if (currentAnnotationType == containerType) { 1145 for (Annotation contained : getRawAnnotationsFromContainer(element, annotation)) { 1146 T result = processor.process(element, contained, metaDepth); 1147 if (aggregatedResults != null && result != null) { 1148 // No need to post-process since repeatable annotations within a 1149 // container cannot be composed annotations. 1150 aggregatedResults.add(result); 1151 } 1152 } 1153 } 1154 } 1155 } 1156 1157 // Recursively search in meta-annotations 1158 for (Annotation annotation : annotations) { 1159 Class<? extends Annotation> currentAnnotationType = annotation.annotationType(); 1160 if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) { 1161 T result = searchWithFindSemantics(currentAnnotationType, annotationType, annotationName, 1162 containerType, processor, visited, metaDepth + 1); 1163 if (result != null) { 1164 processor.postProcess(currentAnnotationType, annotation, result); 1165 if (aggregatedResults != null && metaDepth == 0) { 1166 aggregatedResults.add(result); 1167 } 1168 else { 1169 return result; 1170 } 1171 } 1172 } 1173 } 1174 1175 if (!CollectionUtils.isEmpty(aggregatedResults)) { 1176 // Prepend to support top-down ordering within class hierarchies 1177 processor.getAggregatedResults().addAll(0, aggregatedResults); 1178 } 1179 } 1180 1181 if (element instanceof Method) { 1182 Method method = (Method) element; 1183 T result; 1184 1185 // Search on possibly bridged method 1186 Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method); 1187 if (resolvedMethod != method) { 1188 result = searchWithFindSemantics(resolvedMethod, annotationType, annotationName, 1189 containerType, processor, visited, metaDepth); 1190 if (result != null) { 1191 return result; 1192 } 1193 } 1194 1195 // Search on methods in interfaces declared locally 1196 Class<?>[] ifcs = method.getDeclaringClass().getInterfaces(); 1197 if (ifcs.length > 0) { 1198 result = searchOnInterfaces(method, annotationType, annotationName, 1199 containerType, processor, visited, metaDepth, ifcs); 1200 if (result != null) { 1201 return result; 1202 } 1203 } 1204 1205 // Search on methods in class hierarchy and interface hierarchy 1206 Class<?> clazz = method.getDeclaringClass(); 1207 while (true) { 1208 clazz = clazz.getSuperclass(); 1209 if (clazz == null || Object.class == clazz) { 1210 break; 1211 } 1212 try { 1213 Method equivalentMethod = clazz.getDeclaredMethod(method.getName(), method.getParameterTypes()); 1214 Method resolvedEquivalentMethod = BridgeMethodResolver.findBridgedMethod(equivalentMethod); 1215 result = searchWithFindSemantics(resolvedEquivalentMethod, annotationType, annotationName, 1216 containerType, processor, visited, metaDepth); 1217 if (result != null) { 1218 return result; 1219 } 1220 } 1221 catch (NoSuchMethodException ex) { 1222 // No equivalent method found 1223 } 1224 // Search on interfaces declared on superclass 1225 result = searchOnInterfaces(method, annotationType, annotationName, 1226 containerType, processor, visited, metaDepth, clazz.getInterfaces()); 1227 if (result != null) { 1228 return result; 1229 } 1230 } 1231 } 1232 else if (element instanceof Class) { 1233 Class<?> clazz = (Class<?>) element; 1234 1235 // Search on interfaces 1236 for (Class<?> ifc : clazz.getInterfaces()) { 1237 T result = searchWithFindSemantics(ifc, annotationType, annotationName, 1238 containerType, processor, visited, metaDepth); 1239 if (result != null) { 1240 return result; 1241 } 1242 } 1243 1244 // Search on superclass 1245 Class<?> superclass = clazz.getSuperclass(); 1246 if (superclass != null && Object.class != superclass) { 1247 T result = searchWithFindSemantics(superclass, annotationType, annotationName, 1248 containerType, processor, visited, metaDepth); 1249 if (result != null) { 1250 return result; 1251 } 1252 } 1253 } 1254 } 1255 catch (Throwable ex) { 1256 AnnotationUtils.handleIntrospectionFailure(element, ex); 1257 } 1258 } 1259 return null; 1260 } 1261 1262 private static <T> T searchOnInterfaces(Method method, Class<? extends Annotation> annotationType, 1263 String annotationName, Class<? extends Annotation> containerType, Processor<T> processor, 1264 Set<AnnotatedElement> visited, int metaDepth, Class<?>[] ifcs) { 1265 1266 for (Class<?> iface : ifcs) { 1267 if (AnnotationUtils.isInterfaceWithAnnotatedMethods(iface)) { 1268 try { 1269 Method equivalentMethod = iface.getMethod(method.getName(), method.getParameterTypes()); 1270 T result = searchWithFindSemantics(equivalentMethod, annotationType, annotationName, containerType, 1271 processor, visited, metaDepth); 1272 if (result != null) { 1273 return result; 1274 } 1275 } 1276 catch (NoSuchMethodException ex) { 1277 // Skip this interface - it doesn't have the method... 1278 } 1279 } 1280 } 1281 1282 return null; 1283 } 1284 1285 /** 1286 * Get the array of raw (unsynthesized) annotations from the {@code value} 1287 * attribute of the supplied repeatable annotation {@code container}. 1288 * @since 4.3 1289 */ 1290 @SuppressWarnings("unchecked") 1291 private static <A extends Annotation> A[] getRawAnnotationsFromContainer(AnnotatedElement element, 1292 Annotation container) { 1293 1294 try { 1295 return (A[]) AnnotationUtils.getValue(container); 1296 } 1297 catch (Throwable ex) { 1298 AnnotationUtils.handleIntrospectionFailure(element, ex); 1299 } 1300 // Unable to read value from repeating annotation container -> ignore it. 1301 return (A[]) EMPTY_ANNOTATION_ARRAY; 1302 } 1303 1304 /** 1305 * Resolve the container type for the supplied repeatable {@code annotationType}. 1306 * <p>Delegates to {@link AnnotationUtils#resolveContainerAnnotationType(Class)}. 1307 * @param annotationType the annotation type to resolve the container for 1308 * @return the container type (never {@code null}) 1309 * @throws IllegalArgumentException if the container type cannot be resolved 1310 * @since 4.3 1311 */ 1312 private static Class<? extends Annotation> resolveContainerType(Class<? extends Annotation> annotationType) { 1313 Class<? extends Annotation> containerType = AnnotationUtils.resolveContainerAnnotationType(annotationType); 1314 if (containerType == null) { 1315 throw new IllegalArgumentException( 1316 "Annotation type must be a repeatable annotation: failed to resolve container type for " + 1317 annotationType.getName()); 1318 } 1319 return containerType; 1320 } 1321 1322 /** 1323 * Validate that the supplied {@code containerType} is a proper container 1324 * annotation for the supplied repeatable {@code annotationType} (i.e., 1325 * that it declares a {@code value} attribute that holds an array of the 1326 * {@code annotationType}). 1327 * @throws AnnotationConfigurationException if the supplied {@code containerType} 1328 * is not a valid container annotation for the supplied {@code annotationType} 1329 * @since 4.3 1330 */ 1331 private static void validateContainerType(Class<? extends Annotation> annotationType, 1332 Class<? extends Annotation> containerType) { 1333 1334 try { 1335 Method method = containerType.getDeclaredMethod(AnnotationUtils.VALUE); 1336 Class<?> returnType = method.getReturnType(); 1337 if (!returnType.isArray() || returnType.getComponentType() != annotationType) { 1338 String msg = String.format( 1339 "Container type [%s] must declare a 'value' attribute for an array of type [%s]", 1340 containerType.getName(), annotationType.getName()); 1341 throw new AnnotationConfigurationException(msg); 1342 } 1343 } 1344 catch (Throwable ex) { 1345 AnnotationUtils.rethrowAnnotationConfigurationException(ex); 1346 String msg = String.format("Invalid declaration of container type [%s] for repeatable annotation [%s]", 1347 containerType.getName(), annotationType.getName()); 1348 throw new AnnotationConfigurationException(msg, ex); 1349 } 1350 } 1351 1352 private static <A extends Annotation> Set<A> postProcessAndSynthesizeAggregatedResults(AnnotatedElement element, 1353 Class<A> annotationType, List<AnnotationAttributes> aggregatedResults) { 1354 1355 Set<A> annotations = new LinkedHashSet<A>(); 1356 for (AnnotationAttributes attributes : aggregatedResults) { 1357 AnnotationUtils.postProcessAnnotationAttributes(element, attributes, false, false); 1358 annotations.add(AnnotationUtils.synthesizeAnnotation(attributes, annotationType, element)); 1359 } 1360 return annotations; 1361 } 1362 1363 1364 /** 1365 * Callback interface that is used to process annotations during a search. 1366 * <p>Depending on the use case, a processor may choose to 1367 * {@linkplain #process} a single target annotation, multiple target 1368 * annotations, or all annotations discovered by the currently executing 1369 * search. The term "target" in this context refers to a matching 1370 * annotation (i.e., a specific annotation type that was found during 1371 * the search). 1372 * <p>Returning a non-null value from the {@link #process} 1373 * method instructs the search algorithm to stop searching further; 1374 * whereas, returning {@code null} from the {@link #process} method 1375 * instructs the search algorithm to continue searching for additional 1376 * annotations. One exception to this rule applies to processors 1377 * that {@linkplain #aggregates aggregate} results. If an aggregating 1378 * processor returns a non-null value, that value will be added to the 1379 * list of {@linkplain #getAggregatedResults aggregated results} 1380 * and the search algorithm will continue. 1381 * <p>Processors can optionally {@linkplain #postProcess post-process} 1382 * the result of the {@link #process} method as the search algorithm 1383 * goes back down the annotation hierarchy from an invocation of 1384 * {@link #process} that returned a non-null value down to the 1385 * {@link AnnotatedElement} that was supplied as the starting point to 1386 * the search algorithm. 1387 * @param <T> the type of result returned by the processor 1388 */ 1389 private interface Processor<T> { 1390 1391 /** 1392 * Process the supplied annotation. 1393 * <p>The supplied annotation will be an actual target annotation 1394 * that has been found by the search algorithm, unless this processor 1395 * is configured to {@linkplain #alwaysProcesses always process} 1396 * annotations in which case it may be some other annotation within an 1397 * annotation hierarchy. In the latter case, the {@code metaDepth} 1398 * will have a value greater than {@code 0}. In any case, it is 1399 * up to concrete implementations of this method to decide what to 1400 * do with the supplied annotation. 1401 * <p>The {@code metaDepth} parameter represents the depth of the 1402 * annotation relative to the first annotated element in the 1403 * annotation hierarchy. For example, an annotation that is 1404 * <em>present</em> on a non-annotation element will have a depth 1405 * of 0; a meta-annotation will have a depth of 1; and a 1406 * meta-meta-annotation will have a depth of 2; etc. 1407 * @param annotatedElement the element that is annotated with the 1408 * supplied annotation, used for contextual logging; may be 1409 * {@code null} if unknown 1410 * @param annotation the annotation to process 1411 * @param metaDepth the meta-depth of the annotation 1412 * @return the result of the processing, or {@code null} to continue 1413 * searching for additional annotations 1414 */ 1415 T process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth); 1416 1417 /** 1418 * Post-process the result returned by the {@link #process} method. 1419 * <p>The {@code annotation} supplied to this method is an annotation 1420 * that is present in the annotation hierarchy, between the initial 1421 * {@link AnnotatedElement} and an invocation of {@link #process} 1422 * that returned a non-null value. 1423 * @param annotatedElement the element that is annotated with the 1424 * supplied annotation, used for contextual logging; may be 1425 * {@code null} if unknown 1426 * @param annotation the annotation to post-process 1427 * @param result the result to post-process 1428 */ 1429 void postProcess(AnnotatedElement annotatedElement, Annotation annotation, T result); 1430 1431 /** 1432 * Determine if this processor always processes annotations regardless of 1433 * whether or not the target annotation has been found. 1434 * @return {@code true} if this processor always processes annotations 1435 * @since 4.3 1436 */ 1437 boolean alwaysProcesses(); 1438 1439 /** 1440 * Determine if this processor aggregates the results returned by {@link #process}. 1441 * <p>If this method returns {@code true}, then {@link #getAggregatedResults()} 1442 * must return a non-null value. 1443 * @return {@code true} if this processor supports aggregated results 1444 * @since 4.3 1445 * @see #getAggregatedResults 1446 */ 1447 boolean aggregates(); 1448 1449 /** 1450 * Get the list of results aggregated by this processor. 1451 * <p>NOTE: the processor does <strong>not</strong> aggregate the results 1452 * itself. Rather, the search algorithm that uses this processor is 1453 * responsible for asking this processor if it {@link #aggregates} results 1454 * and then adding the post-processed results to the list returned by this 1455 * method. 1456 * @return the list of results aggregated by this processor (never {@code null}) 1457 * @since 4.3 1458 * @see #aggregates 1459 */ 1460 List<T> getAggregatedResults(); 1461 } 1462 1463 1464 /** 1465 * {@link Processor} that {@linkplain #process(AnnotatedElement, Annotation, int) 1466 * processes} annotations but does not {@linkplain #postProcess post-process} or 1467 * {@linkplain #aggregates aggregate} results. 1468 * @since 4.2 1469 */ 1470 private abstract static class SimpleAnnotationProcessor<T> implements Processor<T> { 1471 1472 private final boolean alwaysProcesses; 1473 1474 public SimpleAnnotationProcessor() { 1475 this(false); 1476 } 1477 1478 public SimpleAnnotationProcessor(boolean alwaysProcesses) { 1479 this.alwaysProcesses = alwaysProcesses; 1480 } 1481 1482 @Override 1483 public final boolean alwaysProcesses() { 1484 return this.alwaysProcesses; 1485 } 1486 1487 @Override 1488 public final void postProcess(AnnotatedElement annotatedElement, Annotation annotation, T result) { 1489 // no-op 1490 } 1491 1492 @Override 1493 public final boolean aggregates() { 1494 return false; 1495 } 1496 1497 @Override 1498 public final List<T> getAggregatedResults() { 1499 throw new UnsupportedOperationException("SimpleAnnotationProcessor does not support aggregated results"); 1500 } 1501 } 1502 1503 1504 /** 1505 * {@link SimpleAnnotationProcessor} that always returns {@link Boolean#TRUE} when 1506 * asked to {@linkplain #process(AnnotatedElement, Annotation, int) process} an 1507 * annotation. 1508 * @since 4.3 1509 */ 1510 static class AlwaysTrueBooleanAnnotationProcessor extends SimpleAnnotationProcessor<Boolean> { 1511 1512 @Override 1513 public final Boolean process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { 1514 return Boolean.TRUE; 1515 } 1516 } 1517 1518 1519 /** 1520 * {@link Processor} that gets the {@code AnnotationAttributes} for the 1521 * target annotation during the {@link #process} phase and then merges 1522 * annotation attributes from lower levels in the annotation hierarchy 1523 * during the {@link #postProcess} phase. 1524 * <p>A {@code MergedAnnotationAttributesProcessor} may optionally be 1525 * configured to {@linkplain #aggregates aggregate} results. 1526 * @since 4.2 1527 * @see AnnotationUtils#retrieveAnnotationAttributes 1528 * @see AnnotationUtils#postProcessAnnotationAttributes 1529 */ 1530 private static class MergedAnnotationAttributesProcessor implements Processor<AnnotationAttributes> { 1531 1532 private final boolean classValuesAsString; 1533 1534 private final boolean nestedAnnotationsAsMap; 1535 1536 private final boolean aggregates; 1537 1538 private final List<AnnotationAttributes> aggregatedResults; 1539 1540 MergedAnnotationAttributesProcessor() { 1541 this(false, false, false); 1542 } 1543 1544 MergedAnnotationAttributesProcessor(boolean classValuesAsString, boolean nestedAnnotationsAsMap) { 1545 this(classValuesAsString, nestedAnnotationsAsMap, false); 1546 } 1547 1548 MergedAnnotationAttributesProcessor(boolean classValuesAsString, boolean nestedAnnotationsAsMap, 1549 boolean aggregates) { 1550 1551 this.classValuesAsString = classValuesAsString; 1552 this.nestedAnnotationsAsMap = nestedAnnotationsAsMap; 1553 this.aggregates = aggregates; 1554 this.aggregatedResults = (aggregates ? new ArrayList<AnnotationAttributes>() : null); 1555 } 1556 1557 @Override 1558 public boolean alwaysProcesses() { 1559 return false; 1560 } 1561 1562 @Override 1563 public boolean aggregates() { 1564 return this.aggregates; 1565 } 1566 1567 @Override 1568 public List<AnnotationAttributes> getAggregatedResults() { 1569 return this.aggregatedResults; 1570 } 1571 1572 @Override 1573 public AnnotationAttributes process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { 1574 return AnnotationUtils.retrieveAnnotationAttributes(annotatedElement, annotation, 1575 this.classValuesAsString, this.nestedAnnotationsAsMap); 1576 } 1577 1578 @Override 1579 public void postProcess(AnnotatedElement element, Annotation annotation, AnnotationAttributes attributes) { 1580 annotation = AnnotationUtils.synthesizeAnnotation(annotation, element); 1581 Class<? extends Annotation> targetAnnotationType = attributes.annotationType(); 1582 1583 // Track which attribute values have already been replaced so that we can short 1584 // circuit the search algorithms. 1585 Set<String> valuesAlreadyReplaced = new HashSet<String>(); 1586 1587 for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotation.annotationType())) { 1588 String attributeName = attributeMethod.getName(); 1589 String attributeOverrideName = AnnotationUtils.getAttributeOverrideName(attributeMethod, targetAnnotationType); 1590 1591 // Explicit annotation attribute override declared via @AliasFor 1592 if (attributeOverrideName != null) { 1593 if (valuesAlreadyReplaced.contains(attributeOverrideName)) { 1594 continue; 1595 } 1596 1597 List<String> targetAttributeNames = new ArrayList<String>(); 1598 targetAttributeNames.add(attributeOverrideName); 1599 valuesAlreadyReplaced.add(attributeOverrideName); 1600 1601 // Ensure all aliased attributes in the target annotation are overridden. (SPR-14069) 1602 List<String> aliases = AnnotationUtils.getAttributeAliasMap(targetAnnotationType).get(attributeOverrideName); 1603 if (aliases != null) { 1604 for (String alias : aliases) { 1605 if (!valuesAlreadyReplaced.contains(alias)) { 1606 targetAttributeNames.add(alias); 1607 valuesAlreadyReplaced.add(alias); 1608 } 1609 } 1610 } 1611 1612 overrideAttributes(element, annotation, attributes, attributeName, targetAttributeNames); 1613 } 1614 // Implicit annotation attribute override based on convention 1615 else if (!AnnotationUtils.VALUE.equals(attributeName) && attributes.containsKey(attributeName)) { 1616 overrideAttribute(element, annotation, attributes, attributeName, attributeName); 1617 } 1618 } 1619 } 1620 1621 private void overrideAttributes(AnnotatedElement element, Annotation annotation, 1622 AnnotationAttributes attributes, String sourceAttributeName, List<String> targetAttributeNames) { 1623 1624 Object adaptedValue = getAdaptedValue(element, annotation, sourceAttributeName); 1625 1626 for (String targetAttributeName : targetAttributeNames) { 1627 attributes.put(targetAttributeName, adaptedValue); 1628 } 1629 } 1630 1631 private void overrideAttribute(AnnotatedElement element, Annotation annotation, AnnotationAttributes attributes, 1632 String sourceAttributeName, String targetAttributeName) { 1633 1634 attributes.put(targetAttributeName, getAdaptedValue(element, annotation, sourceAttributeName)); 1635 } 1636 1637 private Object getAdaptedValue(AnnotatedElement element, Annotation annotation, String sourceAttributeName) { 1638 Object value = AnnotationUtils.getValue(annotation, sourceAttributeName); 1639 return AnnotationUtils.adaptValue(element, value, this.classValuesAsString, this.nestedAnnotationsAsMap); 1640 } 1641 } 1642 1643}