Browse Source

Consistently skip unnecessary search on superclasses and empty elements

Includes caching of declared annotation arrays and combined searching for several annotation types (used in SpringCacheAnnotationParser).

Issue: SPR-16933
pull/1929/head
Juergen Hoeller 7 years ago
parent
commit
109a2b49e5
  1. 4
      spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java
  2. 12
      spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java
  3. 163
      spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java
  4. 18
      spring-context/src/test/java/org/springframework/cache/annotation/AnnotationCacheOperationSourceTests.java
  5. 231
      spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java
  6. 134
      spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java

4
spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java

@ -120,7 +120,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean @@ -120,7 +120,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
protected final Log logger = LogFactory.getLog(getClass());
private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>();
private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>(4);
private String requiredParameterName = "required";
@ -490,7 +490,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean @@ -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<? extends Annotation> type : this.autowiredAnnotationTypes) {
AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ao, type);
if (attributes != null) {

12
spring-beans/src/main/java/org/springframework/beans/factory/annotation/QualifierAnnotationAutowireCandidateResolver.java

@ -1,5 +1,5 @@ @@ -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 @@ -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;
}

163
spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java vendored

@ -22,6 +22,8 @@ import java.lang.reflect.AnnotatedElement; @@ -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; @@ -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; @@ -47,24 +48,34 @@ import org.springframework.util.StringUtils;
@SuppressWarnings("serial")
public class SpringCacheAnnotationParser implements CacheAnnotationParser, Serializable {
private static final Set<Class<? extends Annotation>> 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<CacheOperation> parseCacheAnnotations(Class<?> type) {
DefaultCacheConfig defaultConfig = getDefaultCacheConfig(type);
DefaultCacheConfig defaultConfig = new DefaultCacheConfig(type);
return parseCacheAnnotations(defaultConfig, type);
}
@Override
@Nullable
public Collection<CacheOperation> parseCacheAnnotations(Method method) {
DefaultCacheConfig defaultConfig = getDefaultCacheConfig(method.getDeclaringClass());
DefaultCacheConfig defaultConfig = new DefaultCacheConfig(method.getDeclaringClass());
return parseCacheAnnotations(defaultConfig, method);
}
@Nullable
private Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
Collection<CacheOperation> 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<CacheOperation> localOps = parseCacheAnnotations(cachingConfig, ae, true);
if (localOps != null) {
@ -78,55 +89,28 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria @@ -78,55 +89,28 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
private Collection<CacheOperation> parseCacheAnnotations(
DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) {
Collection<CacheOperation> ops = null;
Collection<Cacheable> 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<CacheEvict> 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<CachePut> 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<Caching> cachings = (localOnly ? AnnotatedElementUtils.getAllMergedAnnotations(ae, Caching.class) :
AnnotatedElementUtils.findAllMergedAnnotations(ae, Caching.class));
if (!cachings.isEmpty()) {
ops = lazyInit(ops);
for (Caching caching : cachings) {
Collection<CacheOperation> cachingOps = parseCachingAnnotation(ae, cachingConfig, caching);
if (cachingOps != null) {
ops.addAll(cachingOps);
}
}
Collection<? extends Annotation> anns = (localOnly ?
AnnotatedElementUtils.getAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS) :
AnnotatedElementUtils.findAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS));
if (anns.isEmpty()) {
return null;
}
final Collection<CacheOperation> 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 <T extends Annotation> Collection<CacheOperation> lazyInit(@Nullable Collection<CacheOperation> 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 @@ -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 @@ -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 @@ -185,48 +173,23 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
return op;
}
@Nullable
Collection<CacheOperation> parseCachingAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, Caching caching) {
Collection<CacheOperation> ops = null;
private void parseCachingAnnotation(
AnnotatedElement ae, DefaultCacheConfig defaultConfig, Caching caching, Collection<CacheOperation> 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 @@ -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 @@ -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);
}

18
spring-context/src/test/java/org/springframework/cache/annotation/AnnotationCacheOperationSourceTests.java vendored

@ -1,5 +1,5 @@ @@ -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 { @@ -52,13 +52,13 @@ public class AnnotationCacheOperationSourceTests {
@Test
public void singularAnnotation() throws Exception {
public void singularAnnotation() {
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "singular", 1);
assertTrue(ops.iterator().next() instanceof CacheableOperation);
}
@Test
public void multipleAnnotation() throws Exception {
public void multipleAnnotation() {
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "multiple", 2);
Iterator<CacheOperation> it = ops.iterator();
assertTrue(it.next() instanceof CacheableOperation);
@ -66,7 +66,7 @@ public class AnnotationCacheOperationSourceTests { @@ -66,7 +66,7 @@ public class AnnotationCacheOperationSourceTests {
}
@Test
public void caching() throws Exception {
public void caching() {
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "caching", 2);
Iterator<CacheOperation> it = ops.iterator();
assertTrue(it.next() instanceof CacheableOperation);
@ -74,18 +74,18 @@ public class AnnotationCacheOperationSourceTests { @@ -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<CacheOperation> ops = getOps(AnnotatedClass.class, "singleStereotype", 1);
assertTrue(ops.iterator().next() instanceof CacheEvictOperation);
}
@Test
public void multipleStereotypes() throws Exception {
public void multipleStereotypes() {
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "multipleStereotype", 3);
Iterator<CacheOperation> it = ops.iterator();
assertTrue(it.next() instanceof CacheableOperation);
@ -98,7 +98,7 @@ public class AnnotationCacheOperationSourceTests { @@ -98,7 +98,7 @@ public class AnnotationCacheOperationSourceTests {
}
@Test
public void singleComposedAnnotation() throws Exception {
public void singleComposedAnnotation() {
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "singleComposed", 2);
Iterator<CacheOperation> it = ops.iterator();
@ -114,7 +114,7 @@ public class AnnotationCacheOperationSourceTests { @@ -114,7 +114,7 @@ public class AnnotationCacheOperationSourceTests {
}
@Test
public void multipleComposedAnnotations() throws Exception {
public void multipleComposedAnnotations() {
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "multipleComposed", 4);
Iterator<CacheOperation> it = ops.iterator();

231
spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java

@ -24,6 +24,7 @@ import java.util.Arrays; @@ -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 { @@ -180,7 +181,7 @@ public abstract class AnnotatedElementUtils {
try {
final Set<String> types = new LinkedHashSet<>();
searchWithGetSemantics(composed.annotationType(), null, null, null, new SimpleAnnotationProcessor<Object>(true) {
searchWithGetSemantics(composed.annotationType(), Collections.emptySet(), null, null, new SimpleAnnotationProcessor<Object>(true) {
@Override
@Nullable
public Object process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
@ -391,8 +392,8 @@ public abstract class AnnotatedElementUtils { @@ -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 { @@ -424,7 +425,31 @@ public abstract class AnnotatedElementUtils {
public static <A extends Annotation> Set<A> getAllMergedAnnotations(AnnotatedElement element, Class<A> 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 <strong>all</strong> annotations of the specified {@code annotationTypes}
* within the annotation hierarchy <em>above</em> the supplied {@code element};
* and for each annotation found, merge that annotation's attributes with
* <em>matching</em> attributes from annotations in lower levels of the
* annotation hierarchy and synthesize the results back into an annotation
* of the corresponding {@code annotationType}.
* <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
* single annotation and within annotation hierarchies.
* <p>This method follows <em>get semantics</em> 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<Annotation> getAllMergedAnnotations(AnnotatedElement element, Set<Class<? extends Annotation>> 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 { @@ -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 { @@ -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 { @@ -709,7 +734,31 @@ public abstract class AnnotatedElementUtils {
public static <A extends Annotation> Set<A> findAllMergedAnnotations(AnnotatedElement element, Class<A> 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 <strong>all</strong> annotations of the specified {@code annotationTypes}
* within the annotation hierarchy <em>above</em> the supplied {@code element};
* and for each annotation found, merge that annotation's attributes with
* <em>matching</em> attributes from annotations in lower levels of the
* annotation hierarchy and synthesize the results back into an annotation
* of the corresponding {@code annotationType}.
* <p>{@link AliasFor @AliasFor} semantics are fully supported, both within a
* single annotation and within annotation hierarchies.
* <p>This method follows <em>find semantics</em> 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<Annotation> findAllMergedAnnotations(AnnotatedElement element, Set<Class<? extends Annotation>> 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 { @@ -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 { @@ -799,7 +848,9 @@ public abstract class AnnotatedElementUtils {
@Nullable Class<? extends Annotation> annotationType,
@Nullable String annotationName, Processor<T> 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 { @@ -807,7 +858,7 @@ public abstract class AnnotatedElementUtils {
* {@code annotationType} on the specified {@code element}, following
* <em>get semantics</em>.
* @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 { @@ -818,11 +869,11 @@ public abstract class AnnotatedElementUtils {
*/
@Nullable
private static <T> T searchWithGetSemantics(AnnotatedElement element,
@Nullable Class<? extends Annotation> annotationType, @Nullable String annotationName,
Set<Class<? extends Annotation>> annotationTypes, @Nullable String annotationName,
@Nullable Class<? extends Annotation> containerType, Processor<T> 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 { @@ -838,7 +889,7 @@ public abstract class AnnotatedElementUtils {
* <p>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 { @@ -850,33 +901,35 @@ public abstract class AnnotatedElementUtils {
*/
@Nullable
private static <T> T searchWithGetSemantics(AnnotatedElement element,
@Nullable Class<? extends Annotation> annotationType, @Nullable String annotationName,
Set<Class<? extends Annotation>> annotationTypes, @Nullable String annotationName,
@Nullable Class<? extends Annotation> containerType, Processor<T> processor,
Set<AnnotatedElement> visited, int metaDepth) {
if (visited.add(element)) {
try {
// Start searching within locally declared annotations
List<Annotation> declaredAnnotations = Arrays.asList(element.getDeclaredAnnotations());
List<Annotation> 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<Annotation> 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<Annotation> 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 { @@ -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 { @@ -912,7 +965,7 @@ public abstract class AnnotatedElementUtils {
*/
@Nullable
private static <T> T searchWithGetSemanticsInAnnotations(@Nullable AnnotatedElement element,
List<Annotation> annotations, @Nullable Class<? extends Annotation> annotationType,
List<Annotation> annotations, Set<Class<? extends Annotation>> annotationTypes,
@Nullable String annotationName, @Nullable Class<? extends Annotation> containerType,
Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth) {
@ -920,7 +973,7 @@ public abstract class AnnotatedElementUtils { @@ -920,7 +973,7 @@ public abstract class AnnotatedElementUtils {
for (Annotation annotation : annotations) {
Class<? extends Annotation> 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 { @@ -950,8 +1003,8 @@ public abstract class AnnotatedElementUtils {
// Recursively search in meta-annotations
for (Annotation annotation : annotations) {
Class<? extends Annotation> 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 { @@ -985,7 +1038,9 @@ public abstract class AnnotatedElementUtils {
@Nullable Class<? extends Annotation> annotationType,
@Nullable String annotationName, Processor<T> 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 { @@ -993,7 +1048,7 @@ public abstract class AnnotatedElementUtils {
* {@code annotationType} on the specified {@code element}, following
* <em>find semantics</em>.
* @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 { @@ -1004,7 +1059,7 @@ public abstract class AnnotatedElementUtils {
*/
@Nullable
private static <T> T searchWithFindSemantics(AnnotatedElement element,
@Nullable Class<? extends Annotation> annotationType, @Nullable String annotationName,
Set<Class<? extends Annotation>> annotationTypes, @Nullable String annotationName,
@Nullable Class<? extends Annotation> containerType, Processor<T> processor) {
if (containerType != null && !processor.aggregates()) {
@ -1014,7 +1069,7 @@ public abstract class AnnotatedElementUtils { @@ -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 { @@ -1029,7 +1084,7 @@ public abstract class AnnotatedElementUtils {
* <p>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 { @@ -1042,14 +1097,14 @@ public abstract class AnnotatedElementUtils {
*/
@Nullable
private static <T> T searchWithFindSemantics(AnnotatedElement element,
@Nullable Class<? extends Annotation> annotationType, @Nullable String annotationName,
Set<Class<? extends Annotation>> annotationTypes, @Nullable String annotationName,
@Nullable Class<? extends Annotation> containerType, Processor<T> processor,
Set<AnnotatedElement> 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<T> aggregatedResults = (processor.aggregates() ? new ArrayList<>() : null);
@ -1057,7 +1112,7 @@ public abstract class AnnotatedElementUtils { @@ -1057,7 +1112,7 @@ public abstract class AnnotatedElementUtils {
for (Annotation annotation : annotations) {
Class<? extends Annotation> 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 { @@ -1087,8 +1142,8 @@ public abstract class AnnotatedElementUtils {
// Recursively search in meta-annotations
for (Annotation annotation : annotations) {
Class<? extends Annotation> 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 { @@ -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 { @@ -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 { @@ -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<Method> annotatedMethods = AnnotationUtils.getAnnotatedMethodsInBaseType(clazz);
@ -1144,8 +1199,8 @@ public abstract class AnnotatedElementUtils { @@ -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 { @@ -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 { @@ -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 { @@ -1191,7 +1246,7 @@ public abstract class AnnotatedElementUtils {
}
@Nullable
private static <T> T searchOnInterfaces(Method method, @Nullable Class<? extends Annotation> annotationType,
private static <T> T searchOnInterfaces(Method method, Set<Class<? extends Annotation>> annotationTypes,
@Nullable String annotationName, @Nullable Class<? extends Annotation> containerType,
Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth, Class<?>[] ifcs) {
@ -1200,7 +1255,7 @@ public abstract class AnnotatedElementUtils { @@ -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 { @@ -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<? extends Annotation> 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 { @@ -1303,13 +1338,23 @@ public abstract class AnnotatedElementUtils {
}
}
private static <A extends Annotation> Set<A> postProcessAndSynthesizeAggregatedResults(AnnotatedElement element,
Class<A> annotationType, List<AnnotationAttributes> 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 <A extends Annotation> Set<A> postProcessAndSynthesizeAggregatedResults(
AnnotatedElement element, List<AnnotationAttributes> aggregatedResults) {
Set<A> annotations = new LinkedHashSet<>();
for (AnnotationAttributes attributes : aggregatedResults) {
AnnotationUtils.postProcessAnnotationAttributes(element, attributes, false, false);
annotations.add(AnnotationUtils.synthesizeAnnotation(attributes, annotationType, element));
Class<? extends Annotation> annType = attributes.annotationType();
if (annType != null) {
annotations.add((A) AnnotationUtils.synthesizeAnnotation(attributes, annType, element));
}
}
return annotations;
}

134
spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java

@ -125,6 +125,9 @@ public abstract class AnnotationUtils { @@ -125,6 +125,9 @@ public abstract class AnnotationUtils {
private static final Map<AnnotationCacheKey, Boolean> metaPresentCache =
new ConcurrentReferenceHashMap<>(256);
private static final Map<AnnotatedElement, Annotation[]> declaredAnnotationsCache =
new ConcurrentReferenceHashMap<>(256);
private static final Map<Class<?>, Set<Method>> annotatedBaseTypeCache =
new ConcurrentReferenceHashMap<>(256);
@ -341,18 +344,13 @@ public abstract class AnnotationUtils { @@ -341,18 +344,13 @@ public abstract class AnnotationUtils {
Class<A> annotationType, @Nullable Class<? extends Annotation> containerAnnotationType) {
Set<A> 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 { @@ -422,37 +420,11 @@ public abstract class AnnotationUtils {
public static <A extends Annotation> Set<A> getDeclaredRepeatableAnnotations(AnnotatedElement annotatedElement,
Class<A> annotationType, @Nullable Class<? extends Annotation> containerAnnotationType) {
return getRepeatableAnnotations(annotatedElement, annotationType, containerAnnotationType, true);
}
/**
* Perform the actual work for {@link #getRepeatableAnnotations(AnnotatedElement, Class, Class)}
* and {@link #getDeclaredRepeatableAnnotations(AnnotatedElement, Class, Class)}.
* <p>Correctly handles <em>bridge methods</em> generated by the
* compiler if the supplied element is a {@link Method}.
* <p>Meta-annotations will be searched if the annotation is not
* <em>present</em> 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 <A extends Annotation> Set<A> getRepeatableAnnotations(AnnotatedElement annotatedElement,
Class<A> annotationType, @Nullable Class<? extends Annotation> 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 { @@ -502,7 +474,7 @@ public abstract class AnnotationUtils {
if (annotation != null) {
return annotation;
}
for (Annotation declaredAnn : annotatedElement.getDeclaredAnnotations()) {
for (Annotation declaredAnn : getDeclaredAnnotations(annotatedElement)) {
Class<? extends Annotation> declaredType = declaredAnn.annotationType();
if (!isInJavaLangAnnotationPackage(declaredType) && visited.add(declaredAnn)) {
annotation = findAnnotation((AnnotatedElement) declaredType, annotationType, visited);
@ -554,7 +526,7 @@ public abstract class AnnotationUtils { @@ -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<Method> annotatedMethods = getAnnotatedMethodsInBaseType(clazz);
@ -601,6 +573,35 @@ public abstract class AnnotationUtils { @@ -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<Method> getAnnotatedMethodsInBaseType(Class<?> baseType) {
boolean ifcCheck = baseType.isInterface();
if (ifcCheck && ClassUtils.isJavaLanguageInterface(baseType)) {
@ -635,8 +636,15 @@ public abstract class AnnotationUtils { @@ -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 { @@ -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 { @@ -741,7 +749,7 @@ public abstract class AnnotationUtils {
if (annotation != null) {
return annotation;
}
for (Annotation declaredAnn : clazz.getDeclaredAnnotations()) {
for (Annotation declaredAnn : getDeclaredAnnotations(clazz)) {
Class<? extends Annotation> declaredType = declaredAnn.annotationType();
if (!isInJavaLangAnnotationPackage(declaredType) && visited.add(declaredAnn)) {
annotation = findAnnotation(declaredType, annotationType, visited);
@ -764,7 +772,7 @@ public abstract class AnnotationUtils { @@ -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 { @@ -794,7 +802,7 @@ public abstract class AnnotationUtils {
*/
@Nullable
public static Class<?> findAnnotationDeclaringClass(Class<? extends Annotation> 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 { @@ -828,8 +836,10 @@ public abstract class AnnotationUtils {
* @see #isAnnotationDeclaredLocally(Class, Class)
*/
@Nullable
public static Class<?> findAnnotationDeclaringClassForTypes(List<Class<? extends Annotation>> annotationTypes, @Nullable Class<?> clazz) {
if (clazz == null || Object.class == clazz) {
public static Class<?> findAnnotationDeclaringClassForTypes(
List<Class<? extends Annotation>> annotationTypes, @Nullable Class<?> clazz) {
if (clazz == null || clazz == Object.class) {
return null;
}
for (Class<? extends Annotation> annotationType : annotationTypes) {
@ -1965,6 +1975,7 @@ public abstract class AnnotationUtils { @@ -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 { @@ -2027,19 +2038,14 @@ public abstract class AnnotationUtils {
@Nullable
private final Class<? extends Annotation> containerAnnotationType;
private final boolean declaredMode;
private final Set<AnnotatedElement> visited = new HashSet<>();
private final Set<A> result = new LinkedHashSet<>();
AnnotationCollector(Class<A> annotationType,
@Nullable Class<? extends Annotation> containerAnnotationType, boolean declaredMode) {
AnnotationCollector(Class<A> annotationType,@Nullable Class<? extends Annotation> containerAnnotationType) {
this.annotationType = annotationType;
this.containerAnnotationType = (containerAnnotationType != null ? containerAnnotationType :
resolveContainerAnnotationType(annotationType));
this.declaredMode = declaredMode;
}
Set<A> getResult(AnnotatedElement element) {
@ -2051,7 +2057,7 @@ public abstract class AnnotationUtils { @@ -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<? extends Annotation> currentAnnotationType = ann.annotationType();
if (ObjectUtils.nullSafeEquals(this.annotationType, currentAnnotationType)) {

Loading…
Cancel
Save