1919import java .lang .annotation .Annotation ;
2020import java .lang .reflect .AnnotatedElement ;
2121import java .lang .reflect .Method ;
22+ import java .util .ArrayList ;
23+ import java .util .Arrays ;
2224import java .util .HashSet ;
2325import java .util .LinkedHashSet ;
26+ import java .util .List ;
2427import java .util .Map ;
2528import java .util .Set ;
2629
7174 *
7275 * <h3>Support for {@code @Inherited}</h3>
7376 * <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}.
7984 *
8085 * @author Phillip Webb
8186 * @author Juergen Hoeller
@@ -352,45 +357,8 @@ public static AnnotationAttributes findAnnotationAttributes(AnnotatedElement ele
352357 */
353358 public static AnnotationAttributes findAnnotationAttributes (AnnotatedElement element , String annotationType ,
354359 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 ));
394362 }
395363
396364 /**
@@ -511,32 +479,28 @@ private static <T> T searchWithGetSemantics(AnnotatedElement element, String ann
511479
512480 if (visited .add (element )) {
513481 try {
514- // Local annotations: declared OR inherited
515- Annotation [] annotations = element .getAnnotations ();
516482
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 ;
526489 }
527490
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 );
537495 }
538496 }
539497
498+ // Continue searching within inherited annotations
499+ result = searchWithGetSemanticsInAnnotations (inheritedAnnotations , annotationType , processor , visited ,
500+ metaDepth );
501+ if (result != null ) {
502+ return result ;
503+ }
540504 }
541505 catch (Exception ex ) {
542506 AnnotationUtils .logIntrospectionFailure (element , ex );
@@ -545,6 +509,69 @@ private static <T> T searchWithGetSemantics(AnnotatedElement element, String ann
545509 return null ;
546510 }
547511
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+
548575 /**
549576 * Search for annotations of the specified {@code annotationType} on
550577 * the specified {@code element}, following <em>find semantics</em>.
0 commit comments