diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java index fa34e3bcb0..95d6772687 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -120,7 +120,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean protected final Log logger = LogFactory.getLog(getClass()); - private final Set> autowiredAnnotationTypes = new LinkedHashSet<>(); + private final Set> autowiredAnnotationTypes = new LinkedHashSet<>(4); private String requiredParameterName = "required"; @@ -490,7 +490,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean @Nullable private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao) { - if (ao.getAnnotations().length > 0) { + if (ao.getAnnotations().length > 0) { // autowiring annotations have to be local for (Class type : this.autowiredAnnotationTypes) { AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ao, type); if (attributes != null) { diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java index 19a6424231..45b6b8ef37 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -347,10 +347,12 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa */ @Nullable protected Object findValue(Annotation[] annotationsToSearch) { - AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes( - AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType); - if (attr != null) { - return extractValue(attr); + if (annotationsToSearch.length > 0) { // qualifier annotations have to be local + AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes( + AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType); + if (attr != null) { + return extractValue(attr); + } } return null; } diff --git a/spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java b/spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java index bac6e7930c..a660f4f6c3 100644 --- a/spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java +++ b/spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java @@ -22,6 +22,8 @@ import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.Set; import org.springframework.cache.interceptor.CacheEvictOperation; import org.springframework.cache.interceptor.CacheOperation; @@ -29,7 +31,6 @@ import org.springframework.cache.interceptor.CachePutOperation; import org.springframework.cache.interceptor.CacheableOperation; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.lang.Nullable; -import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** @@ -47,24 +48,34 @@ import org.springframework.util.StringUtils; @SuppressWarnings("serial") public class SpringCacheAnnotationParser implements CacheAnnotationParser, Serializable { + private static final Set> CACHE_OPERATION_ANNOTATIONS = new LinkedHashSet<>(8); + + static { + CACHE_OPERATION_ANNOTATIONS.add(Cacheable.class); + CACHE_OPERATION_ANNOTATIONS.add(CacheEvict.class); + CACHE_OPERATION_ANNOTATIONS.add(CachePut.class); + CACHE_OPERATION_ANNOTATIONS.add(Caching.class); + } + + @Override @Nullable public Collection parseCacheAnnotations(Class type) { - DefaultCacheConfig defaultConfig = getDefaultCacheConfig(type); + DefaultCacheConfig defaultConfig = new DefaultCacheConfig(type); return parseCacheAnnotations(defaultConfig, type); } @Override @Nullable public Collection parseCacheAnnotations(Method method) { - DefaultCacheConfig defaultConfig = getDefaultCacheConfig(method.getDeclaringClass()); + DefaultCacheConfig defaultConfig = new DefaultCacheConfig(method.getDeclaringClass()); return parseCacheAnnotations(defaultConfig, method); } @Nullable private Collection parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) { Collection ops = parseCacheAnnotations(cachingConfig, ae, false); - if (ops != null && ops.size() > 1 && ae.getAnnotations().length > 0) { + if (ops != null && ops.size() > 1) { // More than one operation found -> local declarations override interface-declared ones... Collection localOps = parseCacheAnnotations(cachingConfig, ae, true); if (localOps != null) { @@ -78,55 +89,28 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria private Collection parseCacheAnnotations( DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) { - Collection ops = null; - - Collection cacheables = (localOnly ? AnnotatedElementUtils.getAllMergedAnnotations(ae, Cacheable.class) : - AnnotatedElementUtils.findAllMergedAnnotations(ae, Cacheable.class)); - if (!cacheables.isEmpty()) { - ops = lazyInit(null); - for (Cacheable cacheable : cacheables) { - ops.add(parseCacheableAnnotation(ae, cachingConfig, cacheable)); - } - } - - Collection evicts = (localOnly ? AnnotatedElementUtils.getAllMergedAnnotations(ae, CacheEvict.class) : - AnnotatedElementUtils.findAllMergedAnnotations(ae, CacheEvict.class)); - if (!evicts.isEmpty()) { - ops = lazyInit(ops); - for (CacheEvict evict : evicts) { - ops.add(parseEvictAnnotation(ae, cachingConfig, evict)); - } - } - - Collection puts = (localOnly ? AnnotatedElementUtils.getAllMergedAnnotations(ae, CachePut.class) : - AnnotatedElementUtils.findAllMergedAnnotations(ae, CachePut.class)); - if (!puts.isEmpty()) { - ops = lazyInit(ops); - for (CachePut put : puts) { - ops.add(parsePutAnnotation(ae, cachingConfig, put)); - } - } - - Collection cachings = (localOnly ? AnnotatedElementUtils.getAllMergedAnnotations(ae, Caching.class) : - AnnotatedElementUtils.findAllMergedAnnotations(ae, Caching.class)); - if (!cachings.isEmpty()) { - ops = lazyInit(ops); - for (Caching caching : cachings) { - Collection cachingOps = parseCachingAnnotation(ae, cachingConfig, caching); - if (cachingOps != null) { - ops.addAll(cachingOps); - } - } + Collection anns = (localOnly ? + AnnotatedElementUtils.getAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS) : + AnnotatedElementUtils.findAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS)); + if (anns.isEmpty()) { + return null; } + final Collection ops = new ArrayList<>(1); + anns.stream().filter(ann -> ann instanceof Cacheable).forEach( + ann -> ops.add(parseCacheableAnnotation(ae, cachingConfig, (Cacheable) ann))); + anns.stream().filter(ann -> ann instanceof CacheEvict).forEach( + ann -> ops.add(parseEvictAnnotation(ae, cachingConfig, (CacheEvict) ann))); + anns.stream().filter(ann -> ann instanceof CachePut).forEach( + ann -> ops.add(parsePutAnnotation(ae, cachingConfig, (CachePut) ann))); + anns.stream().filter(ann -> ann instanceof Caching).forEach( + ann -> parseCachingAnnotation(ae, cachingConfig, (Caching) ann, ops)); return ops; } - private Collection lazyInit(@Nullable Collection ops) { - return (ops != null ? ops : new ArrayList<>(1)); - } + private CacheableOperation parseCacheableAnnotation( + AnnotatedElement ae, DefaultCacheConfig defaultConfig, Cacheable cacheable) { - CacheableOperation parseCacheableAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, Cacheable cacheable) { CacheableOperation.Builder builder = new CacheableOperation.Builder(); builder.setName(ae.toString()); @@ -146,7 +130,9 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria return op; } - CacheEvictOperation parseEvictAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, CacheEvict cacheEvict) { + private CacheEvictOperation parseEvictAnnotation( + AnnotatedElement ae, DefaultCacheConfig defaultConfig, CacheEvict cacheEvict) { + CacheEvictOperation.Builder builder = new CacheEvictOperation.Builder(); builder.setName(ae.toString()); @@ -166,7 +152,9 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria return op; } - CacheOperation parsePutAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, CachePut cachePut) { + private CacheOperation parsePutAnnotation( + AnnotatedElement ae, DefaultCacheConfig defaultConfig, CachePut cachePut) { + CachePutOperation.Builder builder = new CachePutOperation.Builder(); builder.setName(ae.toString()); @@ -185,48 +173,23 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria return op; } - @Nullable - Collection parseCachingAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, Caching caching) { - Collection ops = null; + private void parseCachingAnnotation( + AnnotatedElement ae, DefaultCacheConfig defaultConfig, Caching caching, Collection ops) { Cacheable[] cacheables = caching.cacheable(); - if (!ObjectUtils.isEmpty(cacheables)) { - ops = lazyInit(null); - for (Cacheable cacheable : cacheables) { - ops.add(parseCacheableAnnotation(ae, defaultConfig, cacheable)); - } + for (Cacheable cacheable : cacheables) { + ops.add(parseCacheableAnnotation(ae, defaultConfig, cacheable)); } CacheEvict[] cacheEvicts = caching.evict(); - if (!ObjectUtils.isEmpty(cacheEvicts)) { - ops = lazyInit(ops); - for (CacheEvict cacheEvict : cacheEvicts) { - ops.add(parseEvictAnnotation(ae, defaultConfig, cacheEvict)); - } + for (CacheEvict cacheEvict : cacheEvicts) { + ops.add(parseEvictAnnotation(ae, defaultConfig, cacheEvict)); } CachePut[] cachePuts = caching.put(); - if (!ObjectUtils.isEmpty(cachePuts)) { - ops = lazyInit(ops); - for (CachePut cachePut : cachePuts) { - ops.add(parsePutAnnotation(ae, defaultConfig, cachePut)); - } + for (CachePut cachePut : cachePuts) { + ops.add(parsePutAnnotation(ae, defaultConfig, cachePut)); } - - return ops; } - /** - * Provides the {@link DefaultCacheConfig} instance for the specified {@link Class}. - * @param target the class-level to handle - * @return the default config (never {@code null}) - */ - DefaultCacheConfig getDefaultCacheConfig(Class target) { - CacheConfig annotation = AnnotatedElementUtils.findMergedAnnotation(target, CacheConfig.class); - if (annotation != null) { - return new DefaultCacheConfig(annotation.cacheNames(), annotation.keyGenerator(), - annotation.cacheManager(), annotation.cacheResolver()); - } - return new DefaultCacheConfig(); - } /** * Validates the specified {@link CacheOperation}. @@ -266,31 +229,26 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria /** * Provides default settings for a given set of cache operations. */ - static class DefaultCacheConfig { + private static class DefaultCacheConfig { - @Nullable - private final String[] cacheNames; + private final Class target; @Nullable - private final String keyGenerator; + private String[] cacheNames; @Nullable - private final String cacheManager; + private String keyGenerator; @Nullable - private final String cacheResolver; + private String cacheManager; - public DefaultCacheConfig() { - this(null, null, null, null); - } + @Nullable + private String cacheResolver; - private DefaultCacheConfig(@Nullable String[] cacheNames, @Nullable String keyGenerator, - @Nullable String cacheManager, @Nullable String cacheResolver) { + private boolean initialized = false; - this.cacheNames = cacheNames; - this.keyGenerator = keyGenerator; - this.cacheManager = cacheManager; - this.cacheResolver = cacheResolver; + public DefaultCacheConfig(Class target) { + this.target = target; } /** @@ -298,6 +256,17 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria * @param builder the operation builder to update */ public void applyDefault(CacheOperation.Builder builder) { + if (!this.initialized) { + CacheConfig annotation = AnnotatedElementUtils.findMergedAnnotation(this.target, CacheConfig.class); + if (annotation != null) { + this.cacheNames = annotation.cacheNames(); + this.keyGenerator = annotation.keyGenerator(); + this.cacheManager = annotation.cacheManager(); + this.cacheResolver = annotation.cacheResolver(); + } + this.initialized = true; + } + if (builder.getCacheNames().isEmpty() && this.cacheNames != null) { builder.setCacheNames(this.cacheNames); } diff --git a/spring-context/src/test/java/org/springframework/cache/annotation/AnnotationCacheOperationSourceTests.java b/spring-context/src/test/java/org/springframework/cache/annotation/AnnotationCacheOperationSourceTests.java index 52bb3d2e3b..956af11544 100644 --- a/spring-context/src/test/java/org/springframework/cache/annotation/AnnotationCacheOperationSourceTests.java +++ b/spring-context/src/test/java/org/springframework/cache/annotation/AnnotationCacheOperationSourceTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,13 +52,13 @@ public class AnnotationCacheOperationSourceTests { @Test - public void singularAnnotation() throws Exception { + public void singularAnnotation() { Collection ops = getOps(AnnotatedClass.class, "singular", 1); assertTrue(ops.iterator().next() instanceof CacheableOperation); } @Test - public void multipleAnnotation() throws Exception { + public void multipleAnnotation() { Collection ops = getOps(AnnotatedClass.class, "multiple", 2); Iterator it = ops.iterator(); assertTrue(it.next() instanceof CacheableOperation); @@ -66,7 +66,7 @@ public class AnnotationCacheOperationSourceTests { } @Test - public void caching() throws Exception { + public void caching() { Collection ops = getOps(AnnotatedClass.class, "caching", 2); Iterator it = ops.iterator(); assertTrue(it.next() instanceof CacheableOperation); @@ -74,18 +74,18 @@ public class AnnotationCacheOperationSourceTests { } @Test - public void emptyCaching() throws Exception { + public void emptyCaching() { getOps(AnnotatedClass.class, "emptyCaching", 0); } @Test - public void singularStereotype() throws Exception { + public void singularStereotype() { Collection ops = getOps(AnnotatedClass.class, "singleStereotype", 1); assertTrue(ops.iterator().next() instanceof CacheEvictOperation); } @Test - public void multipleStereotypes() throws Exception { + public void multipleStereotypes() { Collection ops = getOps(AnnotatedClass.class, "multipleStereotype", 3); Iterator it = ops.iterator(); assertTrue(it.next() instanceof CacheableOperation); @@ -98,7 +98,7 @@ public class AnnotationCacheOperationSourceTests { } @Test - public void singleComposedAnnotation() throws Exception { + public void singleComposedAnnotation() { Collection ops = getOps(AnnotatedClass.class, "singleComposed", 2); Iterator it = ops.iterator(); @@ -114,7 +114,7 @@ public class AnnotationCacheOperationSourceTests { } @Test - public void multipleComposedAnnotations() throws Exception { + public void multipleComposedAnnotations() { Collection ops = getOps(AnnotatedClass.class, "multipleComposed", 4); Iterator it = ops.iterator(); diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java index be6ec8b1ad..8631fdf73a 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java @@ -24,6 +24,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -180,7 +181,7 @@ public abstract class AnnotatedElementUtils { try { final Set types = new LinkedHashSet<>(); - searchWithGetSemantics(composed.annotationType(), null, null, null, new SimpleAnnotationProcessor(true) { + searchWithGetSemantics(composed.annotationType(), Collections.emptySet(), null, null, new SimpleAnnotationProcessor(true) { @Override @Nullable public Object process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { @@ -391,8 +392,8 @@ public abstract class AnnotatedElementUtils { return AnnotationUtils.synthesizeAnnotation(annotation, element); } - // Shortcut: no non-java annotations to be found on plain Java classes and org.springframework.lang types... - if (AnnotationUtils.hasPlainJavaAnnotationsOnly(element) && !annotationType.getName().startsWith("java")) { + // Shortcut: no searchable annotations to be found on plain Java classes and org.springframework.lang types... + if (AnnotationUtils.hasPlainJavaAnnotationsOnly(element)) { return null; } @@ -424,7 +425,31 @@ public abstract class AnnotatedElementUtils { public static Set getAllMergedAnnotations(AnnotatedElement element, Class annotationType) { MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true); searchWithGetSemantics(element, annotationType, null, processor); - return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults()); + return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults()); + } + + /** + * Get all annotations of the specified {@code annotationTypes} + * within the annotation hierarchy above the supplied {@code element}; + * and for each annotation found, merge that annotation's attributes with + * matching attributes from annotations in lower levels of the + * annotation hierarchy and synthesize the results back into an annotation + * of the corresponding {@code annotationType}. + *

{@link AliasFor @AliasFor} semantics are fully supported, both within a + * single annotation and within annotation hierarchies. + *

This method follows get semantics as described in the + * {@linkplain AnnotatedElementUtils class-level javadoc}. + * @param element the annotated element (never {@code null}) + * @param annotationTypes the annotation types to find + * @return the set of all merged, synthesized {@code Annotations} found, + * or an empty set if none were found + * @since 5.1 + * @see #getAllMergedAnnotations(AnnotatedElement, Class) + */ + public static Set getAllMergedAnnotations(AnnotatedElement element, Set> annotationTypes) { + MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true); + searchWithGetSemantics(element, annotationTypes, null, null, processor); + return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults()); } /** @@ -494,8 +519,8 @@ public abstract class AnnotatedElementUtils { } MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true); - searchWithGetSemantics(element, annotationType, null, containerType, processor); - return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults()); + searchWithGetSemantics(element, Collections.singleton(annotationType), null, containerType, processor); + return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults()); } /** @@ -677,8 +702,8 @@ public abstract class AnnotatedElementUtils { return AnnotationUtils.synthesizeAnnotation(annotation, element); } - // Shortcut: no non-java annotations to be found on plain Java classes and org.springframework.lang types... - if (AnnotationUtils.hasPlainJavaAnnotationsOnly(element) && !annotationType.getName().startsWith("java")) { + // Shortcut: no searchable annotations to be found on plain Java classes and org.springframework.lang types... + if (AnnotationUtils.hasPlainJavaAnnotationsOnly(element)) { return null; } @@ -709,7 +734,31 @@ public abstract class AnnotatedElementUtils { public static Set findAllMergedAnnotations(AnnotatedElement element, Class annotationType) { MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true); searchWithFindSemantics(element, annotationType, null, processor); - return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults()); + return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults()); + } + + /** + * Find all annotations of the specified {@code annotationTypes} + * within the annotation hierarchy above the supplied {@code element}; + * and for each annotation found, merge that annotation's attributes with + * matching attributes from annotations in lower levels of the + * annotation hierarchy and synthesize the results back into an annotation + * of the corresponding {@code annotationType}. + *

{@link AliasFor @AliasFor} semantics are fully supported, both within a + * single annotation and within annotation hierarchies. + *

This method follows find semantics as described in the + * {@linkplain AnnotatedElementUtils class-level javadoc}. + * @param element the annotated element (never {@code null}) + * @param annotationTypes the annotation types to find + * @return the set of all merged, synthesized {@code Annotations} found, + * or an empty set if none were found + * @since 5.1 + * @see #findAllMergedAnnotations(AnnotatedElement, Class) + */ + public static Set findAllMergedAnnotations(AnnotatedElement element, Set> annotationTypes) { + MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true); + searchWithFindSemantics(element, annotationTypes, null, null, processor); + return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults()); } /** @@ -779,8 +828,8 @@ public abstract class AnnotatedElementUtils { } MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true); - searchWithFindSemantics(element, annotationType, null, containerType, processor); - return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults()); + searchWithFindSemantics(element, Collections.singleton(annotationType), null, containerType, processor); + return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults()); } /** @@ -799,7 +848,9 @@ public abstract class AnnotatedElementUtils { @Nullable Class annotationType, @Nullable String annotationName, Processor processor) { - return searchWithGetSemantics(element, annotationType, annotationName, null, processor); + return searchWithGetSemantics(element, + (annotationType != null ? Collections.singleton(annotationType) : Collections.emptySet()), + annotationName, null, processor); } /** @@ -807,7 +858,7 @@ public abstract class AnnotatedElementUtils { * {@code annotationType} on the specified {@code element}, following * get semantics. * @param element the annotated element - * @param annotationType the annotation type to find + * @param annotationTypes the annotation types to find * @param annotationName the fully qualified class name of the annotation * type to find (as an alternative to {@code annotationType}) * @param containerType the type of the container that holds repeatable @@ -818,11 +869,11 @@ public abstract class AnnotatedElementUtils { */ @Nullable private static T searchWithGetSemantics(AnnotatedElement element, - @Nullable Class annotationType, @Nullable String annotationName, + Set> annotationTypes, @Nullable String annotationName, @Nullable Class containerType, Processor processor) { try { - return searchWithGetSemantics(element, annotationType, annotationName, containerType, processor, + return searchWithGetSemantics(element, annotationTypes, annotationName, containerType, processor, new HashSet<>(), 0); } catch (Throwable ex) { @@ -838,7 +889,7 @@ public abstract class AnnotatedElementUtils { *

The {@code metaDepth} parameter is explained in the * {@link Processor#process process()} method of the {@link Processor} API. * @param element the annotated element - * @param annotationType the annotation type to find + * @param annotationTypes the annotation types to find * @param annotationName the fully qualified class name of the annotation * type to find (as an alternative to {@code annotationType}) * @param containerType the type of the container that holds repeatable @@ -850,33 +901,35 @@ public abstract class AnnotatedElementUtils { */ @Nullable private static T searchWithGetSemantics(AnnotatedElement element, - @Nullable Class annotationType, @Nullable String annotationName, + Set> annotationTypes, @Nullable String annotationName, @Nullable Class containerType, Processor processor, Set visited, int metaDepth) { if (visited.add(element)) { try { // Start searching within locally declared annotations - List declaredAnnotations = Arrays.asList(element.getDeclaredAnnotations()); + List declaredAnnotations = Arrays.asList(AnnotationUtils.getDeclaredAnnotations(element)); T result = searchWithGetSemanticsInAnnotations(element, declaredAnnotations, - annotationType, annotationName, containerType, processor, visited, metaDepth); + annotationTypes, annotationName, containerType, processor, visited, metaDepth); if (result != null) { return result; } - if (element instanceof Class) { // otherwise getAnnotations doesn't return anything new - List inheritedAnnotations = new ArrayList<>(); - for (Annotation annotation : element.getAnnotations()) { - if (!declaredAnnotations.contains(annotation)) { - inheritedAnnotations.add(annotation); + if (element instanceof Class) { // otherwise getAnnotations doesn't return anything new + Class superclass = ((Class) element).getSuperclass(); + if (superclass != null && superclass != Object.class) { + List inheritedAnnotations = new LinkedList<>(); + for (Annotation annotation : element.getAnnotations()) { + if (!declaredAnnotations.contains(annotation)) { + inheritedAnnotations.add(annotation); + } + } + // Continue searching within inherited annotations + result = searchWithGetSemanticsInAnnotations(element, inheritedAnnotations, + annotationTypes, annotationName, containerType, processor, visited, metaDepth); + if (result != null) { + return result; } - } - - // Continue searching within inherited annotations - result = searchWithGetSemanticsInAnnotations(element, inheritedAnnotations, - annotationType, annotationName, containerType, processor, visited, metaDepth); - if (result != null) { - return result; } } } @@ -899,7 +952,7 @@ public abstract class AnnotatedElementUtils { * @param element the element that is annotated with the supplied * annotations, used for contextual logging; may be {@code null} if unknown * @param annotations the annotations to search in - * @param annotationType the annotation type to find + * @param annotationTypes the annotation types to find * @param annotationName the fully qualified class name of the annotation * type to find (as an alternative to {@code annotationType}) * @param containerType the type of the container that holds repeatable @@ -912,7 +965,7 @@ public abstract class AnnotatedElementUtils { */ @Nullable private static T searchWithGetSemanticsInAnnotations(@Nullable AnnotatedElement element, - List annotations, @Nullable Class annotationType, + List annotations, Set> annotationTypes, @Nullable String annotationName, @Nullable Class containerType, Processor processor, Set visited, int metaDepth) { @@ -920,7 +973,7 @@ public abstract class AnnotatedElementUtils { for (Annotation annotation : annotations) { Class currentAnnotationType = annotation.annotationType(); if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) { - if (currentAnnotationType == annotationType || + if (annotationTypes.contains(currentAnnotationType) || currentAnnotationType.getName().equals(annotationName) || processor.alwaysProcesses()) { T result = processor.process(element, annotation, metaDepth); @@ -950,8 +1003,8 @@ public abstract class AnnotatedElementUtils { // Recursively search in meta-annotations for (Annotation annotation : annotations) { Class currentAnnotationType = annotation.annotationType(); - if (hasSearchableMetaAnnotations(currentAnnotationType, annotationType, annotationName)) { - T result = searchWithGetSemantics(currentAnnotationType, annotationType, + if (!AnnotationUtils.hasPlainJavaAnnotationsOnly(currentAnnotationType)) { + T result = searchWithGetSemantics(currentAnnotationType, annotationTypes, annotationName, containerType, processor, visited, metaDepth + 1); if (result != null) { processor.postProcess(element, annotation, result); @@ -985,7 +1038,9 @@ public abstract class AnnotatedElementUtils { @Nullable Class annotationType, @Nullable String annotationName, Processor processor) { - return searchWithFindSemantics(element, annotationType, annotationName, null, processor); + return searchWithFindSemantics(element, + (annotationType != null ? Collections.singleton(annotationType) : Collections.emptySet()), + annotationName, null, processor); } /** @@ -993,7 +1048,7 @@ public abstract class AnnotatedElementUtils { * {@code annotationType} on the specified {@code element}, following * find semantics. * @param element the annotated element - * @param annotationType the annotation type to find + * @param annotationTypes the annotation types to find * @param annotationName the fully qualified class name of the annotation * type to find (as an alternative to {@code annotationType}) * @param containerType the type of the container that holds repeatable @@ -1004,7 +1059,7 @@ public abstract class AnnotatedElementUtils { */ @Nullable private static T searchWithFindSemantics(AnnotatedElement element, - @Nullable Class annotationType, @Nullable String annotationName, + Set> annotationTypes, @Nullable String annotationName, @Nullable Class containerType, Processor processor) { if (containerType != null && !processor.aggregates()) { @@ -1014,7 +1069,7 @@ public abstract class AnnotatedElementUtils { try { return searchWithFindSemantics( - element, annotationType, annotationName, containerType, processor, new HashSet<>(), 0); + element, annotationTypes, annotationName, containerType, processor, new HashSet<>(), 0); } catch (Throwable ex) { AnnotationUtils.rethrowAnnotationConfigurationException(ex); @@ -1029,7 +1084,7 @@ public abstract class AnnotatedElementUtils { *

The {@code metaDepth} parameter is explained in the * {@link Processor#process process()} method of the {@link Processor} API. * @param element the annotated element (never {@code null}) - * @param annotationType the annotation type to find + * @param annotationTypes the annotation types to find * @param annotationName the fully qualified class name of the annotation * type to find (as an alternative to {@code annotationType}) * @param containerType the type of the container that holds repeatable @@ -1042,14 +1097,14 @@ public abstract class AnnotatedElementUtils { */ @Nullable private static T searchWithFindSemantics(AnnotatedElement element, - @Nullable Class annotationType, @Nullable String annotationName, + Set> annotationTypes, @Nullable String annotationName, @Nullable Class containerType, Processor processor, Set visited, int metaDepth) { if (visited.add(element)) { try { // Locally declared annotations (ignoring @Inherited) - Annotation[] annotations = element.getDeclaredAnnotations(); + Annotation[] annotations = AnnotationUtils.getDeclaredAnnotations(element); if (annotations.length > 0) { List aggregatedResults = (processor.aggregates() ? new ArrayList<>() : null); @@ -1057,7 +1112,7 @@ public abstract class AnnotatedElementUtils { for (Annotation annotation : annotations) { Class currentAnnotationType = annotation.annotationType(); if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) { - if (currentAnnotationType == annotationType || + if (annotationTypes.contains(currentAnnotationType) || currentAnnotationType.getName().equals(annotationName) || processor.alwaysProcesses()) { T result = processor.process(element, annotation, metaDepth); @@ -1087,8 +1142,8 @@ public abstract class AnnotatedElementUtils { // Recursively search in meta-annotations for (Annotation annotation : annotations) { Class currentAnnotationType = annotation.annotationType(); - if (hasSearchableMetaAnnotations(currentAnnotationType, annotationType, annotationName)) { - T result = searchWithFindSemantics(currentAnnotationType, annotationType, annotationName, + if (!AnnotationUtils.hasPlainJavaAnnotationsOnly(currentAnnotationType)) { + T result = searchWithFindSemantics(currentAnnotationType, annotationTypes, annotationName, containerType, processor, visited, metaDepth + 1); if (result != null) { processor.postProcess(currentAnnotationType, annotation, result); @@ -1115,7 +1170,7 @@ public abstract class AnnotatedElementUtils { // Search on possibly bridged method Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method); if (resolvedMethod != method) { - result = searchWithFindSemantics(resolvedMethod, annotationType, annotationName, + result = searchWithFindSemantics(resolvedMethod, annotationTypes, annotationName, containerType, processor, visited, metaDepth); if (result != null) { return result; @@ -1125,7 +1180,7 @@ public abstract class AnnotatedElementUtils { // Search on methods in interfaces declared locally Class[] ifcs = method.getDeclaringClass().getInterfaces(); if (ifcs.length > 0) { - result = searchOnInterfaces(method, annotationType, annotationName, + result = searchOnInterfaces(method, annotationTypes, annotationName, containerType, processor, visited, metaDepth, ifcs); if (result != null) { return result; @@ -1136,7 +1191,7 @@ public abstract class AnnotatedElementUtils { Class clazz = method.getDeclaringClass(); while (true) { clazz = clazz.getSuperclass(); - if (clazz == null || Object.class == clazz) { + if (clazz == null || clazz == Object.class) { break; } Set annotatedMethods = AnnotationUtils.getAnnotatedMethodsInBaseType(clazz); @@ -1144,8 +1199,8 @@ public abstract class AnnotatedElementUtils { for (Method annotatedMethod : annotatedMethods) { if (AnnotationUtils.isOverride(method, annotatedMethod)) { Method resolvedSuperMethod = BridgeMethodResolver.findBridgedMethod(annotatedMethod); - result = searchWithFindSemantics(resolvedSuperMethod, annotationType, annotationName, - containerType, processor, visited, metaDepth); + result = searchWithFindSemantics(resolvedSuperMethod, annotationTypes, + annotationName, containerType, processor, visited, metaDepth); if (result != null) { return result; } @@ -1153,7 +1208,7 @@ public abstract class AnnotatedElementUtils { } } // Search on interfaces declared on superclass - result = searchOnInterfaces(method, annotationType, annotationName, + result = searchOnInterfaces(method, annotationTypes, annotationName, containerType, processor, visited, metaDepth, clazz.getInterfaces()); if (result != null) { return result; @@ -1162,23 +1217,23 @@ public abstract class AnnotatedElementUtils { } else if (element instanceof Class) { Class clazz = (Class) element; - - // Search on interfaces - for (Class ifc : clazz.getInterfaces()) { - T result = searchWithFindSemantics(ifc, annotationType, annotationName, - containerType, processor, visited, metaDepth); - if (result != null) { - return result; + if (!Annotation.class.isAssignableFrom(clazz)) { + // Search on interfaces + for (Class ifc : clazz.getInterfaces()) { + T result = searchWithFindSemantics(ifc, annotationTypes, annotationName, + containerType, processor, visited, metaDepth); + if (result != null) { + return result; + } } - } - - // Search on superclass - Class superclass = clazz.getSuperclass(); - if (superclass != null && Object.class != superclass) { - T result = searchWithFindSemantics(superclass, annotationType, annotationName, - containerType, processor, visited, metaDepth); - if (result != null) { - return result; + // Search on superclass + Class superclass = clazz.getSuperclass(); + if (superclass != null && superclass != Object.class) { + T result = searchWithFindSemantics(superclass, annotationTypes, annotationName, + containerType, processor, visited, metaDepth); + if (result != null) { + return result; + } } } } @@ -1191,7 +1246,7 @@ public abstract class AnnotatedElementUtils { } @Nullable - private static T searchOnInterfaces(Method method, @Nullable Class annotationType, + private static T searchOnInterfaces(Method method, Set> annotationTypes, @Nullable String annotationName, @Nullable Class containerType, Processor processor, Set visited, int metaDepth, Class[] ifcs) { @@ -1200,7 +1255,7 @@ public abstract class AnnotatedElementUtils { if (!annotatedMethods.isEmpty()) { for (Method annotatedMethod : annotatedMethods) { if (AnnotationUtils.isOverride(method, annotatedMethod)) { - T result = searchWithFindSemantics(annotatedMethod, annotationType, annotationName, + T result = searchWithFindSemantics(annotatedMethod, annotationTypes, annotationName, containerType, processor, visited, metaDepth); if (result != null) { return result; @@ -1213,26 +1268,6 @@ public abstract class AnnotatedElementUtils { return null; } - /** - * Determine whether the current annotation type is generally expected to have - * meta-annotations of the specified annotation type that we're searching for, - * explicitly excluding some common cases that would never deliver any results. - */ - private static boolean hasSearchableMetaAnnotations(Class currentAnnotationType, - @Nullable Class annotationType, @Nullable String annotationName) { - - if (AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) { - return false; - } - if (AnnotationUtils.hasPlainJavaAnnotationsOnly(currentAnnotationType)) { - // @Nullable and standard Java annotations are only meant to have standard Java meta-annotations - // -> not worth searching otherwise. - return ((annotationType != null && annotationType.getName().startsWith("java")) || - (annotationName != null && annotationName.startsWith("java"))); - } - return true; - } - /** * Get the array of raw (unsynthesized) annotations from the {@code value} * attribute of the supplied repeatable annotation {@code container}. @@ -1303,13 +1338,23 @@ public abstract class AnnotatedElementUtils { } } - private static Set postProcessAndSynthesizeAggregatedResults(AnnotatedElement element, - Class annotationType, List aggregatedResults) { + /** + * Post-process the aggregated results into a set of synthesized annotations. + * @param element the annotated element + * @param aggregatedResults the aggregated results for the given element + * @return the set of annotations + */ + @SuppressWarnings("unchecked") + private static Set postProcessAndSynthesizeAggregatedResults( + AnnotatedElement element, List aggregatedResults) { Set annotations = new LinkedHashSet<>(); for (AnnotationAttributes attributes : aggregatedResults) { AnnotationUtils.postProcessAnnotationAttributes(element, attributes, false, false); - annotations.add(AnnotationUtils.synthesizeAnnotation(attributes, annotationType, element)); + Class annType = attributes.annotationType(); + if (annType != null) { + annotations.add((A) AnnotationUtils.synthesizeAnnotation(attributes, annType, element)); + } } return annotations; } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java index 2d04f8ee9a..c8982c81a7 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java @@ -125,6 +125,9 @@ public abstract class AnnotationUtils { private static final Map metaPresentCache = new ConcurrentReferenceHashMap<>(256); + private static final Map declaredAnnotationsCache = + new ConcurrentReferenceHashMap<>(256); + private static final Map, Set> annotatedBaseTypeCache = new ConcurrentReferenceHashMap<>(256); @@ -341,18 +344,13 @@ public abstract class AnnotationUtils { Class annotationType, @Nullable Class containerAnnotationType) { Set annotations = getDeclaredRepeatableAnnotations(annotatedElement, annotationType, containerAnnotationType); - if (!annotations.isEmpty()) { - return annotations; - } - - if (annotatedElement instanceof Class) { + if (annotations.isEmpty() && annotatedElement instanceof Class) { Class superclass = ((Class) annotatedElement).getSuperclass(); - if (superclass != null && Object.class != superclass) { + if (superclass != null && superclass != Object.class) { return getRepeatableAnnotations(superclass, annotationType, containerAnnotationType); } } - - return getRepeatableAnnotations(annotatedElement, annotationType, containerAnnotationType, false); + return annotations; } /** @@ -422,37 +420,11 @@ public abstract class AnnotationUtils { public static Set getDeclaredRepeatableAnnotations(AnnotatedElement annotatedElement, Class annotationType, @Nullable Class containerAnnotationType) { - return getRepeatableAnnotations(annotatedElement, annotationType, containerAnnotationType, true); - } - - /** - * Perform the actual work for {@link #getRepeatableAnnotations(AnnotatedElement, Class, Class)} - * and {@link #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class)}. - *

Correctly handles bridge methods generated by the - * compiler if the supplied element is a {@link Method}. - *

Meta-annotations will be searched if the annotation is not - * present on the supplied element. - * @param annotatedElement the element to look for annotations on - * @param annotationType the annotation type to look for - * @param containerAnnotationType the type of the container that holds - * the annotations; may be {@code null} if a container is not supported - * or if it should be looked up via @{@link java.lang.annotation.Repeatable} - * when running on Java 8 or higher - * @param declaredMode {@code true} if only declared annotations (i.e., - * directly or indirectly present) should be considered - * @return the annotations found or an empty set (never {@code null}) - * @since 4.2 - * @see org.springframework.core.BridgeMethodResolver#findBridgedMethod - * @see java.lang.annotation.Repeatable - */ - private static Set getRepeatableAnnotations(AnnotatedElement annotatedElement, - Class annotationType, @Nullable Class containerAnnotationType, boolean declaredMode) { - try { if (annotatedElement instanceof Method) { annotatedElement = BridgeMethodResolver.findBridgedMethod((Method) annotatedElement); } - return new AnnotationCollector<>(annotationType, containerAnnotationType, declaredMode).getResult(annotatedElement); + return new AnnotationCollector<>(annotationType, containerAnnotationType).getResult(annotatedElement); } catch (Throwable ex) { handleIntrospectionFailure(annotatedElement, ex); @@ -502,7 +474,7 @@ public abstract class AnnotationUtils { if (annotation != null) { return annotation; } - for (Annotation declaredAnn : annotatedElement.getDeclaredAnnotations()) { + for (Annotation declaredAnn : getDeclaredAnnotations(annotatedElement)) { Class declaredType = declaredAnn.annotationType(); if (!isInJavaLangAnnotationPackage(declaredType) && visited.add(declaredAnn)) { annotation = findAnnotation((AnnotatedElement) declaredType, annotationType, visited); @@ -554,7 +526,7 @@ public abstract class AnnotationUtils { Class clazz = method.getDeclaringClass(); while (result == null) { clazz = clazz.getSuperclass(); - if (clazz == null || Object.class == clazz) { + if (clazz == null || clazz == Object.class) { break; } Set annotatedMethods = getAnnotatedMethodsInBaseType(clazz); @@ -601,6 +573,35 @@ public abstract class AnnotationUtils { return null; } + /** + * Does the given method override the given candidate method? + * @param method the overriding method + * @param candidate the potentially overridden method + * @since 5.0.8 + */ + static boolean isOverride(Method method, Method candidate) { + if (!candidate.getName().equals(method.getName()) || + candidate.getParameterCount() != method.getParameterCount()) { + return false; + } + Class[] paramTypes = method.getParameterTypes(); + if (Arrays.equals(candidate.getParameterTypes(), paramTypes)) { + return true; + } + for (int i = 0; i < paramTypes.length; i++) { + if (paramTypes[i] != ResolvableType.forMethodParameter(candidate, i, method.getDeclaringClass()).resolve()) { + return false; + } + } + return true; + } + + /** + * Determine the methods on the given type with searchable annotations on them. + * @param baseType the superclass or interface to search + * @return the cached set of annotated methods + * @since 5.0.5 + */ static Set getAnnotatedMethodsInBaseType(Class baseType) { boolean ifcCheck = baseType.isInterface(); if (ifcCheck && ClassUtils.isJavaLanguageInterface(baseType)) { @@ -635,8 +636,15 @@ public abstract class AnnotationUtils { return annotatedMethods; } + /** + * Determine whether the specified method has searchable annotations, + * i.e. not just {@code java.lang} or {@code org.springframework.lang} + * annotations such as {@link Deprecated} and {@link Nullable}. + * @param ifcMethod the interface method to check + * @@since 5.0.5 + */ private static boolean hasSearchableAnnotations(Method ifcMethod) { - Annotation[] anns = ifcMethod.getAnnotations(); + Annotation[] anns = getDeclaredAnnotations(ifcMethod); if (anns.length == 0) { return false; } @@ -649,21 +657,21 @@ public abstract class AnnotationUtils { return false; } - static boolean isOverride(Method method, Method candidate) { - if (!candidate.getName().equals(method.getName()) || - candidate.getParameterCount() != method.getParameterCount()) { - return false; - } - Class[] paramTypes = method.getParameterTypes(); - if (Arrays.equals(candidate.getParameterTypes(), paramTypes)) { - return true; - } - for (int i = 0; i < paramTypes.length; i++) { - if (paramTypes[i] != ResolvableType.forMethodParameter(candidate, i, method.getDeclaringClass()).resolve()) { - return false; - } + /** + * Retrieve a potentially cached array of declared annotations for the + * given element. + * @param element the annotated element to introspect + * @return a potentially cached array of declared annotations + * (only for internal iteration purposes, not for external exposure) + * @since 5.1 + */ + static Annotation[] getDeclaredAnnotations(AnnotatedElement element) { + if (element instanceof Class || element instanceof Member) { + // Class/Field/Method/Constructor returns a defensively cloned array from getDeclaredAnnotations. + // Since we use our result for internal iteration purposes only, it's safe to use a shared copy. + return declaredAnnotationsCache.computeIfAbsent(element, AnnotatedElement::getDeclaredAnnotations); } - return true; + return element.getDeclaredAnnotations(); } /** @@ -741,7 +749,7 @@ public abstract class AnnotationUtils { if (annotation != null) { return annotation; } - for (Annotation declaredAnn : clazz.getDeclaredAnnotations()) { + for (Annotation declaredAnn : getDeclaredAnnotations(clazz)) { Class declaredType = declaredAnn.annotationType(); if (!isInJavaLangAnnotationPackage(declaredType) && visited.add(declaredAnn)) { annotation = findAnnotation(declaredType, annotationType, visited); @@ -764,7 +772,7 @@ public abstract class AnnotationUtils { } Class superclass = clazz.getSuperclass(); - if (superclass == null || Object.class == superclass) { + if (superclass == null || superclass == Object.class) { return null; } return findAnnotation(superclass, annotationType, visited); @@ -794,7 +802,7 @@ public abstract class AnnotationUtils { */ @Nullable public static Class findAnnotationDeclaringClass(Class annotationType, @Nullable Class clazz) { - if (clazz == null || Object.class == clazz) { + if (clazz == null || clazz == Object.class) { return null; } if (isAnnotationDeclaredLocally(annotationType, clazz)) { @@ -828,8 +836,10 @@ public abstract class AnnotationUtils { * @see #isAnnotationDeclaredLocally(Class, Class) */ @Nullable - public static Class findAnnotationDeclaringClassForTypes(List> annotationTypes, @Nullable Class clazz) { - if (clazz == null || Object.class == clazz) { + public static Class findAnnotationDeclaringClassForTypes( + List> annotationTypes, @Nullable Class clazz) { + + if (clazz == null || clazz == Object.class) { return null; } for (Class annotationType : annotationTypes) { @@ -1965,6 +1975,7 @@ public abstract class AnnotationUtils { public static void clearCache() { findAnnotationCache.clear(); metaPresentCache.clear(); + declaredAnnotationsCache.clear(); annotatedBaseTypeCache.clear(); synthesizableCache.clear(); attributeAliasesCache.clear(); @@ -2027,19 +2038,14 @@ public abstract class AnnotationUtils { @Nullable private final Class containerAnnotationType; - private final boolean declaredMode; - private final Set visited = new HashSet<>(); private final Set result = new LinkedHashSet<>(); - AnnotationCollector(Class annotationType, - @Nullable Class containerAnnotationType, boolean declaredMode) { - + AnnotationCollector(Class annotationType,@Nullable Class containerAnnotationType) { this.annotationType = annotationType; this.containerAnnotationType = (containerAnnotationType != null ? containerAnnotationType : resolveContainerAnnotationType(annotationType)); - this.declaredMode = declaredMode; } Set getResult(AnnotatedElement element) { @@ -2051,7 +2057,7 @@ public abstract class AnnotationUtils { private void process(AnnotatedElement element) { if (this.visited.add(element)) { try { - Annotation[] annotations = (this.declaredMode ? element.getDeclaredAnnotations() : element.getAnnotations()); + Annotation[] annotations = getDeclaredAnnotations(element); for (Annotation ann : annotations) { Class currentAnnotationType = ann.annotationType(); if (ObjectUtils.nullSafeEquals(this.annotationType, currentAnnotationType)) {