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
This commit is contained in:
parent
50dc8c2358
commit
109a2b49e5
|
|
@ -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
|
|||
|
||||
@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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<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
|
|||
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
|
|||
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<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
|
|||
/**
|
||||
* Provides default settings for a given set of cache operations.
|
||||
*/
|
||||
static class DefaultCacheConfig {
|
||||
private static class DefaultCacheConfig {
|
||||
|
||||
private final Class<?> target;
|
||||
|
||||
@Nullable
|
||||
private final String[] cacheNames;
|
||||
private String[] cacheNames;
|
||||
|
||||
@Nullable
|
||||
private final String keyGenerator;
|
||||
private String keyGenerator;
|
||||
|
||||
@Nullable
|
||||
private final String cacheManager;
|
||||
private String cacheManager;
|
||||
|
||||
@Nullable
|
||||
private final String cacheResolver;
|
||||
private String cacheResolver;
|
||||
|
||||
public DefaultCacheConfig() {
|
||||
this(null, null, null, null);
|
||||
}
|
||||
private boolean initialized = false;
|
||||
|
||||
private DefaultCacheConfig(@Nullable String[] cacheNames, @Nullable String keyGenerator,
|
||||
@Nullable String cacheManager, @Nullable String cacheResolver) {
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<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 {
|
|||
}
|
||||
|
||||
@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 {
|
|||
}
|
||||
|
||||
@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 {
|
|||
}
|
||||
|
||||
@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 {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void multipleComposedAnnotations() throws Exception {
|
||||
public void multipleComposedAnnotations() {
|
||||
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "multipleComposed", 4);
|
||||
Iterator<CacheOperation> it = ops.iterator();
|
||||
|
||||
|
|
|
|||
|
|
@ -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<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 {
|
|||
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 <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 {
|
|||
}
|
||||
|
||||
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 <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 {
|
|||
}
|
||||
|
||||
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<? 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 {
|
|||
* {@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 {
|
|||
*/
|
||||
@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 {
|
|||
* <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 {
|
|||
*/
|
||||
@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 {
|
|||
* @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> 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 {
|
|||
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 {
|
|||
// 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 {
|
|||
@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 {
|
|||
* {@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 {
|
|||
*/
|
||||
@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 {
|
|||
|
||||
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 {
|
|||
* <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 {
|
|||
*/
|
||||
@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 {
|
|||
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 {
|
|||
// 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 {
|
|||
// 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<Method> 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> 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 {
|
|||
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<? 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 {
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
|||
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 {
|
|||
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 {
|
|||
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 {
|
|||
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 {
|
|||
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 {
|
|||
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;
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
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;
|
||||
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<? extends Annotation> 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<? 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 {
|
|||
* @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 {
|
|||
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<? 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 {
|
|||
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…
Reference in New Issue