19
19
import java .lang .annotation .Annotation ;
20
20
import java .lang .reflect .AnnotatedElement ;
21
21
import java .lang .reflect .Method ;
22
+ import java .util .ArrayList ;
23
+ import java .util .Arrays ;
22
24
import java .util .HashSet ;
23
25
import java .util .LinkedHashSet ;
26
+ import java .util .List ;
24
27
import java .util .Map ;
25
28
import java .util .Set ;
26
29
71
74
*
72
75
* <h3>Support for {@code @Inherited}</h3>
73
76
* <p>Methods following <em>get semantics</em> will honor the contract of
74
- * Java's {@link java.lang.annotation.Inherited @Inherited} annotation.
75
- * However, methods following <em>find semantics</em> will ignore the
76
- * presence of {@code @Inherited} since the <em>find</em> search algorithm
77
- * manually traverses type and method hierarchies and thereby implicitly
78
- * supports annotation inheritance without the need for {@code @Inherited}.
77
+ * Java's {@link java.lang.annotation.Inherited @Inherited} annotation except
78
+ * that locally declared annotations (including custom composed annotations)
79
+ * will be favored over inherited annotations. In contrast, methods following
80
+ * <em>find semantics</em> will completely ignore the presence of
81
+ * {@code @Inherited} since the <em>find</em> search algorithm manually
82
+ * traverses type and method hierarchies and thereby implicitly supports
83
+ * annotation inheritance without the need for {@code @Inherited}.
79
84
*
80
85
* @author Phillip Webb
81
86
* @author Juergen Hoeller
@@ -352,45 +357,8 @@ public static AnnotationAttributes findAnnotationAttributes(AnnotatedElement ele
352
357
*/
353
358
public static AnnotationAttributes findAnnotationAttributes (AnnotatedElement element , String annotationType ,
354
359
boolean classValuesAsString , boolean nestedAnnotationsAsMap ) {
355
- return findAnnotationAttributes (element , annotationType , true , true , true , true , classValuesAsString ,
356
- nestedAnnotationsAsMap );
357
- }
358
-
359
- /**
360
- * Find the first annotation of the specified {@code annotationType} within
361
- * the annotation hierarchy <em>above</em> the supplied {@code element} and
362
- * merge that annotation's attributes with <em>matching</em> attributes from
363
- * annotations in lower levels of the annotation hierarchy.
364
- *
365
- * @param element the annotated element; never {@code null}
366
- * @param annotationType the fully qualified class name of the annotation
367
- * type to find; never {@code null} or empty
368
- * @param searchOnInterfaces whether to search on interfaces, if the
369
- * annotated element is a class
370
- * @param searchOnSuperclasses whether to search on superclasses, if
371
- * the annotated element is a class
372
- * @param searchOnMethodsInInterfaces whether to search on methods in
373
- * interfaces, if the annotated element is a method
374
- * @param searchOnMethodsInSuperclasses whether to search on methods
375
- * in superclasses, if the annotated element is a method
376
- * @param classValuesAsString whether to convert Class references into
377
- * Strings or to preserve them as Class references
378
- * @param nestedAnnotationsAsMap whether to convert nested Annotation
379
- * instances into {@code AnnotationAttributes} maps or to preserve them
380
- * as Annotation instances
381
- * @return the merged {@code AnnotationAttributes}, or {@code null} if
382
- * not found
383
- * @since 4.2
384
- * @see #searchWithFindSemantics
385
- * @see MergedAnnotationAttributesProcessor
386
- */
387
- private static AnnotationAttributes findAnnotationAttributes (AnnotatedElement element , String annotationType ,
388
- boolean searchOnInterfaces , boolean searchOnSuperclasses , boolean searchOnMethodsInInterfaces ,
389
- boolean searchOnMethodsInSuperclasses , boolean classValuesAsString , boolean nestedAnnotationsAsMap ) {
390
-
391
- return searchWithFindSemantics (element , annotationType , searchOnInterfaces , searchOnSuperclasses ,
392
- searchOnMethodsInInterfaces , searchOnMethodsInSuperclasses , new MergedAnnotationAttributesProcessor (
393
- annotationType , classValuesAsString , nestedAnnotationsAsMap ));
360
+ return searchWithFindSemantics (element , annotationType , new MergedAnnotationAttributesProcessor (annotationType ,
361
+ classValuesAsString , nestedAnnotationsAsMap ));
394
362
}
395
363
396
364
/**
@@ -511,32 +479,28 @@ private static <T> T searchWithGetSemantics(AnnotatedElement element, String ann
511
479
512
480
if (visited .add (element )) {
513
481
try {
514
- // Local annotations: declared OR inherited
515
- Annotation [] annotations = element .getAnnotations ();
516
482
517
- // Search in local annotations
518
- for (Annotation annotation : annotations ) {
519
- if (!AnnotationUtils .isInJavaLangAnnotationPackage (annotation )
520
- && (annotation .annotationType ().getName ().equals (annotationType ) || metaDepth > 0 )) {
521
- T result = processor .process (annotation , metaDepth );
522
- if (result != null ) {
523
- return result ;
524
- }
525
- }
483
+ // Start searching within locally declared annotations
484
+ List <Annotation > declaredAnnotations = Arrays .asList (element .getDeclaredAnnotations ());
485
+ T result = searchWithGetSemanticsInAnnotations (declaredAnnotations , annotationType , processor , visited ,
486
+ metaDepth );
487
+ if (result != null ) {
488
+ return result ;
526
489
}
527
490
528
- // Search in meta annotations on local annotations
529
- for (Annotation annotation : annotations ) {
530
- if (!AnnotationUtils .isInJavaLangAnnotationPackage (annotation )) {
531
- T result = searchWithGetSemantics (annotation .annotationType (), annotationType , processor ,
532
- visited , metaDepth + 1 );
533
- if (result != null ) {
534
- processor .postProcess (annotation , result );
535
- return result ;
536
- }
491
+ List <Annotation > inheritedAnnotations = new ArrayList <Annotation >();
492
+ for (Annotation annotation : element .getAnnotations ()) {
493
+ if (!declaredAnnotations .contains (annotation )) {
494
+ inheritedAnnotations .add (annotation );
537
495
}
538
496
}
539
497
498
+ // Continue searching within inherited annotations
499
+ result = searchWithGetSemanticsInAnnotations (inheritedAnnotations , annotationType , processor , visited ,
500
+ metaDepth );
501
+ if (result != null ) {
502
+ return result ;
503
+ }
540
504
}
541
505
catch (Exception ex ) {
542
506
AnnotationUtils .logIntrospectionFailure (element , ex );
@@ -545,6 +509,69 @@ private static <T> T searchWithGetSemantics(AnnotatedElement element, String ann
545
509
return null ;
546
510
}
547
511
512
+ /**
513
+ * This method is invoked by
514
+ * {@link #searchWithGetSemantics(AnnotatedElement, String, Processor, Set, int)}
515
+ * to perform the actual search within the supplied list of annotations.
516
+ * <p>This method should be invoked first with locally declared annotations
517
+ * and then subsequently with inherited annotations, thereby allowing
518
+ * local annotations to take precedence over inherited annotations.
519
+ *
520
+ * <p>The {@code metaDepth} parameter is explained in the
521
+ * {@link Processor#process process()} method of the {@link Processor}
522
+ * API.
523
+ *
524
+ * @param annotations the annotations to search in; never {@code null}
525
+ * @param annotationType the fully qualified class name of the annotation
526
+ * type to find; never {@code null} or empty
527
+ * @param processor the processor to delegate to
528
+ * @param visited the set of annotated elements that have already been visited
529
+ * @param metaDepth the meta-depth of the annotation
530
+ * @return the result of the processor, potentially {@code null}
531
+ */
532
+ private static <T > T searchWithGetSemanticsInAnnotations (List <Annotation > annotations , String annotationType ,
533
+ Processor <T > processor , Set <AnnotatedElement > visited , int metaDepth ) {
534
+
535
+ // Search in annotations
536
+ for (Annotation annotation : annotations ) {
537
+ if (!AnnotationUtils .isInJavaLangAnnotationPackage (annotation )
538
+ && (annotation .annotationType ().getName ().equals (annotationType ) || metaDepth > 0 )) {
539
+ T result = processor .process (annotation , metaDepth );
540
+ if (result != null ) {
541
+ return result ;
542
+ }
543
+ }
544
+ }
545
+
546
+ // Recursively search in meta-annotations
547
+ for (Annotation annotation : annotations ) {
548
+ if (!AnnotationUtils .isInJavaLangAnnotationPackage (annotation )) {
549
+ T result = searchWithGetSemantics (annotation .annotationType (), annotationType , processor , visited ,
550
+ metaDepth + 1 );
551
+ if (result != null ) {
552
+ processor .postProcess (annotation , result );
553
+ return result ;
554
+ }
555
+ }
556
+ }
557
+
558
+ return null ;
559
+ }
560
+
561
+ /**
562
+ * Search for annotations of the specified {@code annotationType} on
563
+ * the specified {@code element}, following <em>find semantics</em>.
564
+ *
565
+ * @param element the annotated element; never {@code null}
566
+ * @param annotationType the fully qualified class name of the annotation
567
+ * type to find; never {@code null} or empty
568
+ * @param processor the processor to delegate to
569
+ * @return the result of the processor, potentially {@code null}
570
+ */
571
+ private static <T > T searchWithFindSemantics (AnnotatedElement element , String annotationType , Processor <T > processor ) {
572
+ return searchWithFindSemantics (element , annotationType , true , true , true , true , processor );
573
+ }
574
+
548
575
/**
549
576
* Search for annotations of the specified {@code annotationType} on
550
577
* the specified {@code element}, following <em>find semantics</em>.
0 commit comments