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());
|
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";
|
private String requiredParameterName = "required";
|
||||||
|
|
||||||
|
|
@ -490,7 +490,7 @@ public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBean
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao) {
|
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) {
|
for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
|
||||||
AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ao, type);
|
AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ao, type);
|
||||||
if (attributes != null) {
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -347,10 +347,12 @@ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwa
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
protected Object findValue(Annotation[] annotationsToSearch) {
|
protected Object findValue(Annotation[] annotationsToSearch) {
|
||||||
AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes(
|
if (annotationsToSearch.length > 0) { // qualifier annotations have to be local
|
||||||
AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType);
|
AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes(
|
||||||
if (attr != null) {
|
AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType);
|
||||||
return extractValue(attr);
|
if (attr != null) {
|
||||||
|
return extractValue(attr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ import java.lang.reflect.AnnotatedElement;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.springframework.cache.interceptor.CacheEvictOperation;
|
import org.springframework.cache.interceptor.CacheEvictOperation;
|
||||||
import org.springframework.cache.interceptor.CacheOperation;
|
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.cache.interceptor.CacheableOperation;
|
||||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.ObjectUtils;
|
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -47,24 +48,34 @@ import org.springframework.util.StringUtils;
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class SpringCacheAnnotationParser implements CacheAnnotationParser, Serializable {
|
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
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public Collection<CacheOperation> parseCacheAnnotations(Class<?> type) {
|
public Collection<CacheOperation> parseCacheAnnotations(Class<?> type) {
|
||||||
DefaultCacheConfig defaultConfig = getDefaultCacheConfig(type);
|
DefaultCacheConfig defaultConfig = new DefaultCacheConfig(type);
|
||||||
return parseCacheAnnotations(defaultConfig, type);
|
return parseCacheAnnotations(defaultConfig, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public Collection<CacheOperation> parseCacheAnnotations(Method method) {
|
public Collection<CacheOperation> parseCacheAnnotations(Method method) {
|
||||||
DefaultCacheConfig defaultConfig = getDefaultCacheConfig(method.getDeclaringClass());
|
DefaultCacheConfig defaultConfig = new DefaultCacheConfig(method.getDeclaringClass());
|
||||||
return parseCacheAnnotations(defaultConfig, method);
|
return parseCacheAnnotations(defaultConfig, method);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
|
private Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
|
||||||
Collection<CacheOperation> ops = parseCacheAnnotations(cachingConfig, ae, false);
|
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...
|
// More than one operation found -> local declarations override interface-declared ones...
|
||||||
Collection<CacheOperation> localOps = parseCacheAnnotations(cachingConfig, ae, true);
|
Collection<CacheOperation> localOps = parseCacheAnnotations(cachingConfig, ae, true);
|
||||||
if (localOps != null) {
|
if (localOps != null) {
|
||||||
|
|
@ -78,55 +89,28 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
|
||||||
private Collection<CacheOperation> parseCacheAnnotations(
|
private Collection<CacheOperation> parseCacheAnnotations(
|
||||||
DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) {
|
DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) {
|
||||||
|
|
||||||
Collection<CacheOperation> ops = null;
|
Collection<? extends Annotation> anns = (localOnly ?
|
||||||
|
AnnotatedElementUtils.getAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS) :
|
||||||
Collection<Cacheable> cacheables = (localOnly ? AnnotatedElementUtils.getAllMergedAnnotations(ae, Cacheable.class) :
|
AnnotatedElementUtils.findAllMergedAnnotations(ae, CACHE_OPERATION_ANNOTATIONS));
|
||||||
AnnotatedElementUtils.findAllMergedAnnotations(ae, Cacheable.class));
|
if (anns.isEmpty()) {
|
||||||
if (!cacheables.isEmpty()) {
|
return null;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
return ops;
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends Annotation> Collection<CacheOperation> lazyInit(@Nullable Collection<CacheOperation> ops) {
|
private CacheableOperation parseCacheableAnnotation(
|
||||||
return (ops != null ? ops : new ArrayList<>(1));
|
AnnotatedElement ae, DefaultCacheConfig defaultConfig, Cacheable cacheable) {
|
||||||
}
|
|
||||||
|
|
||||||
CacheableOperation parseCacheableAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, Cacheable cacheable) {
|
|
||||||
CacheableOperation.Builder builder = new CacheableOperation.Builder();
|
CacheableOperation.Builder builder = new CacheableOperation.Builder();
|
||||||
|
|
||||||
builder.setName(ae.toString());
|
builder.setName(ae.toString());
|
||||||
|
|
@ -146,7 +130,9 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
|
||||||
return op;
|
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();
|
CacheEvictOperation.Builder builder = new CacheEvictOperation.Builder();
|
||||||
|
|
||||||
builder.setName(ae.toString());
|
builder.setName(ae.toString());
|
||||||
|
|
@ -166,7 +152,9 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
|
||||||
return op;
|
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();
|
CachePutOperation.Builder builder = new CachePutOperation.Builder();
|
||||||
|
|
||||||
builder.setName(ae.toString());
|
builder.setName(ae.toString());
|
||||||
|
|
@ -185,48 +173,23 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
|
||||||
return op;
|
return op;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private void parseCachingAnnotation(
|
||||||
Collection<CacheOperation> parseCachingAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, Caching caching) {
|
AnnotatedElement ae, DefaultCacheConfig defaultConfig, Caching caching, Collection<CacheOperation> ops) {
|
||||||
Collection<CacheOperation> ops = null;
|
|
||||||
|
|
||||||
Cacheable[] cacheables = caching.cacheable();
|
Cacheable[] cacheables = caching.cacheable();
|
||||||
if (!ObjectUtils.isEmpty(cacheables)) {
|
for (Cacheable cacheable : cacheables) {
|
||||||
ops = lazyInit(null);
|
ops.add(parseCacheableAnnotation(ae, defaultConfig, cacheable));
|
||||||
for (Cacheable cacheable : cacheables) {
|
|
||||||
ops.add(parseCacheableAnnotation(ae, defaultConfig, cacheable));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
CacheEvict[] cacheEvicts = caching.evict();
|
CacheEvict[] cacheEvicts = caching.evict();
|
||||||
if (!ObjectUtils.isEmpty(cacheEvicts)) {
|
for (CacheEvict cacheEvict : cacheEvicts) {
|
||||||
ops = lazyInit(ops);
|
ops.add(parseEvictAnnotation(ae, defaultConfig, cacheEvict));
|
||||||
for (CacheEvict cacheEvict : cacheEvicts) {
|
|
||||||
ops.add(parseEvictAnnotation(ae, defaultConfig, cacheEvict));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
CachePut[] cachePuts = caching.put();
|
CachePut[] cachePuts = caching.put();
|
||||||
if (!ObjectUtils.isEmpty(cachePuts)) {
|
for (CachePut cachePut : cachePuts) {
|
||||||
ops = lazyInit(ops);
|
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}.
|
* 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.
|
* Provides default settings for a given set of cache operations.
|
||||||
*/
|
*/
|
||||||
static class DefaultCacheConfig {
|
private static class DefaultCacheConfig {
|
||||||
|
|
||||||
|
private final Class<?> target;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final String[] cacheNames;
|
private String[] cacheNames;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final String keyGenerator;
|
private String keyGenerator;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final String cacheManager;
|
private String cacheManager;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final String cacheResolver;
|
private String cacheResolver;
|
||||||
|
|
||||||
public DefaultCacheConfig() {
|
private boolean initialized = false;
|
||||||
this(null, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private DefaultCacheConfig(@Nullable String[] cacheNames, @Nullable String keyGenerator,
|
public DefaultCacheConfig(Class<?> target) {
|
||||||
@Nullable String cacheManager, @Nullable String cacheResolver) {
|
this.target = target;
|
||||||
|
|
||||||
this.cacheNames = cacheNames;
|
|
||||||
this.keyGenerator = keyGenerator;
|
|
||||||
this.cacheManager = cacheManager;
|
|
||||||
this.cacheResolver = cacheResolver;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -298,6 +256,17 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
|
||||||
* @param builder the operation builder to update
|
* @param builder the operation builder to update
|
||||||
*/
|
*/
|
||||||
public void applyDefault(CacheOperation.Builder builder) {
|
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) {
|
if (builder.getCacheNames().isEmpty() && this.cacheNames != null) {
|
||||||
builder.setCacheNames(this.cacheNames);
|
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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -52,13 +52,13 @@ public class AnnotationCacheOperationSourceTests {
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void singularAnnotation() throws Exception {
|
public void singularAnnotation() {
|
||||||
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "singular", 1);
|
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "singular", 1);
|
||||||
assertTrue(ops.iterator().next() instanceof CacheableOperation);
|
assertTrue(ops.iterator().next() instanceof CacheableOperation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void multipleAnnotation() throws Exception {
|
public void multipleAnnotation() {
|
||||||
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "multiple", 2);
|
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "multiple", 2);
|
||||||
Iterator<CacheOperation> it = ops.iterator();
|
Iterator<CacheOperation> it = ops.iterator();
|
||||||
assertTrue(it.next() instanceof CacheableOperation);
|
assertTrue(it.next() instanceof CacheableOperation);
|
||||||
|
|
@ -66,7 +66,7 @@ public class AnnotationCacheOperationSourceTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void caching() throws Exception {
|
public void caching() {
|
||||||
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "caching", 2);
|
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "caching", 2);
|
||||||
Iterator<CacheOperation> it = ops.iterator();
|
Iterator<CacheOperation> it = ops.iterator();
|
||||||
assertTrue(it.next() instanceof CacheableOperation);
|
assertTrue(it.next() instanceof CacheableOperation);
|
||||||
|
|
@ -74,18 +74,18 @@ public class AnnotationCacheOperationSourceTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void emptyCaching() throws Exception {
|
public void emptyCaching() {
|
||||||
getOps(AnnotatedClass.class, "emptyCaching", 0);
|
getOps(AnnotatedClass.class, "emptyCaching", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void singularStereotype() throws Exception {
|
public void singularStereotype() {
|
||||||
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "singleStereotype", 1);
|
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "singleStereotype", 1);
|
||||||
assertTrue(ops.iterator().next() instanceof CacheEvictOperation);
|
assertTrue(ops.iterator().next() instanceof CacheEvictOperation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void multipleStereotypes() throws Exception {
|
public void multipleStereotypes() {
|
||||||
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "multipleStereotype", 3);
|
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "multipleStereotype", 3);
|
||||||
Iterator<CacheOperation> it = ops.iterator();
|
Iterator<CacheOperation> it = ops.iterator();
|
||||||
assertTrue(it.next() instanceof CacheableOperation);
|
assertTrue(it.next() instanceof CacheableOperation);
|
||||||
|
|
@ -98,7 +98,7 @@ public class AnnotationCacheOperationSourceTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void singleComposedAnnotation() throws Exception {
|
public void singleComposedAnnotation() {
|
||||||
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "singleComposed", 2);
|
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "singleComposed", 2);
|
||||||
Iterator<CacheOperation> it = ops.iterator();
|
Iterator<CacheOperation> it = ops.iterator();
|
||||||
|
|
||||||
|
|
@ -114,7 +114,7 @@ public class AnnotationCacheOperationSourceTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void multipleComposedAnnotations() throws Exception {
|
public void multipleComposedAnnotations() {
|
||||||
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "multipleComposed", 4);
|
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "multipleComposed", 4);
|
||||||
Iterator<CacheOperation> it = ops.iterator();
|
Iterator<CacheOperation> it = ops.iterator();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
@ -180,7 +181,7 @@ public abstract class AnnotatedElementUtils {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final Set<String> types = new LinkedHashSet<>();
|
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
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public Object process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
|
public Object process(@Nullable AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
|
||||||
|
|
@ -391,8 +392,8 @@ public abstract class AnnotatedElementUtils {
|
||||||
return AnnotationUtils.synthesizeAnnotation(annotation, element);
|
return AnnotationUtils.synthesizeAnnotation(annotation, element);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shortcut: no non-java annotations to be found on plain Java classes and org.springframework.lang types...
|
// Shortcut: no searchable annotations to be found on plain Java classes and org.springframework.lang types...
|
||||||
if (AnnotationUtils.hasPlainJavaAnnotationsOnly(element) && !annotationType.getName().startsWith("java")) {
|
if (AnnotationUtils.hasPlainJavaAnnotationsOnly(element)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -424,7 +425,31 @@ public abstract class AnnotatedElementUtils {
|
||||||
public static <A extends Annotation> Set<A> getAllMergedAnnotations(AnnotatedElement element, Class<A> annotationType) {
|
public static <A extends Annotation> Set<A> getAllMergedAnnotations(AnnotatedElement element, Class<A> annotationType) {
|
||||||
MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
|
MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
|
||||||
searchWithGetSemantics(element, annotationType, null, processor);
|
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);
|
MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
|
||||||
searchWithGetSemantics(element, annotationType, null, containerType, processor);
|
searchWithGetSemantics(element, Collections.singleton(annotationType), null, containerType, processor);
|
||||||
return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults());
|
return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -677,8 +702,8 @@ public abstract class AnnotatedElementUtils {
|
||||||
return AnnotationUtils.synthesizeAnnotation(annotation, element);
|
return AnnotationUtils.synthesizeAnnotation(annotation, element);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shortcut: no non-java annotations to be found on plain Java classes and org.springframework.lang types...
|
// Shortcut: no searchable annotations to be found on plain Java classes and org.springframework.lang types...
|
||||||
if (AnnotationUtils.hasPlainJavaAnnotationsOnly(element) && !annotationType.getName().startsWith("java")) {
|
if (AnnotationUtils.hasPlainJavaAnnotationsOnly(element)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -709,7 +734,31 @@ public abstract class AnnotatedElementUtils {
|
||||||
public static <A extends Annotation> Set<A> findAllMergedAnnotations(AnnotatedElement element, Class<A> annotationType) {
|
public static <A extends Annotation> Set<A> findAllMergedAnnotations(AnnotatedElement element, Class<A> annotationType) {
|
||||||
MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
|
MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
|
||||||
searchWithFindSemantics(element, annotationType, null, processor);
|
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);
|
MergedAnnotationAttributesProcessor processor = new MergedAnnotationAttributesProcessor(false, false, true);
|
||||||
searchWithFindSemantics(element, annotationType, null, containerType, processor);
|
searchWithFindSemantics(element, Collections.singleton(annotationType), null, containerType, processor);
|
||||||
return postProcessAndSynthesizeAggregatedResults(element, annotationType, processor.getAggregatedResults());
|
return postProcessAndSynthesizeAggregatedResults(element, processor.getAggregatedResults());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -799,7 +848,9 @@ public abstract class AnnotatedElementUtils {
|
||||||
@Nullable Class<? extends Annotation> annotationType,
|
@Nullable Class<? extends Annotation> annotationType,
|
||||||
@Nullable String annotationName, Processor<T> processor) {
|
@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
|
* {@code annotationType} on the specified {@code element}, following
|
||||||
* <em>get semantics</em>.
|
* <em>get semantics</em>.
|
||||||
* @param element the annotated element
|
* @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
|
* @param annotationName the fully qualified class name of the annotation
|
||||||
* type to find (as an alternative to {@code annotationType})
|
* type to find (as an alternative to {@code annotationType})
|
||||||
* @param containerType the type of the container that holds repeatable
|
* @param containerType the type of the container that holds repeatable
|
||||||
|
|
@ -818,11 +869,11 @@ public abstract class AnnotatedElementUtils {
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
private static <T> T searchWithGetSemantics(AnnotatedElement element,
|
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) {
|
@Nullable Class<? extends Annotation> containerType, Processor<T> processor) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return searchWithGetSemantics(element, annotationType, annotationName, containerType, processor,
|
return searchWithGetSemantics(element, annotationTypes, annotationName, containerType, processor,
|
||||||
new HashSet<>(), 0);
|
new HashSet<>(), 0);
|
||||||
}
|
}
|
||||||
catch (Throwable ex) {
|
catch (Throwable ex) {
|
||||||
|
|
@ -838,7 +889,7 @@ public abstract class AnnotatedElementUtils {
|
||||||
* <p>The {@code metaDepth} parameter is explained in the
|
* <p>The {@code metaDepth} parameter is explained in the
|
||||||
* {@link Processor#process process()} method of the {@link Processor} API.
|
* {@link Processor#process process()} method of the {@link Processor} API.
|
||||||
* @param element the annotated element
|
* @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
|
* @param annotationName the fully qualified class name of the annotation
|
||||||
* type to find (as an alternative to {@code annotationType})
|
* type to find (as an alternative to {@code annotationType})
|
||||||
* @param containerType the type of the container that holds repeatable
|
* @param containerType the type of the container that holds repeatable
|
||||||
|
|
@ -850,33 +901,35 @@ public abstract class AnnotatedElementUtils {
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
private static <T> T searchWithGetSemantics(AnnotatedElement element,
|
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,
|
@Nullable Class<? extends Annotation> containerType, Processor<T> processor,
|
||||||
Set<AnnotatedElement> visited, int metaDepth) {
|
Set<AnnotatedElement> visited, int metaDepth) {
|
||||||
|
|
||||||
if (visited.add(element)) {
|
if (visited.add(element)) {
|
||||||
try {
|
try {
|
||||||
// Start searching within locally declared annotations
|
// 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,
|
T result = searchWithGetSemanticsInAnnotations(element, declaredAnnotations,
|
||||||
annotationType, annotationName, containerType, processor, visited, metaDepth);
|
annotationTypes, annotationName, containerType, processor, visited, metaDepth);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element instanceof Class) { // otherwise getAnnotations doesn't return anything new
|
if (element instanceof Class) { // otherwise getAnnotations doesn't return anything new
|
||||||
List<Annotation> inheritedAnnotations = new ArrayList<>();
|
Class<?> superclass = ((Class) element).getSuperclass();
|
||||||
for (Annotation annotation : element.getAnnotations()) {
|
if (superclass != null && superclass != Object.class) {
|
||||||
if (!declaredAnnotations.contains(annotation)) {
|
List<Annotation> inheritedAnnotations = new LinkedList<>();
|
||||||
inheritedAnnotations.add(annotation);
|
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
|
* @param element the element that is annotated with the supplied
|
||||||
* annotations, used for contextual logging; may be {@code null} if unknown
|
* annotations, used for contextual logging; may be {@code null} if unknown
|
||||||
* @param annotations the annotations to search in
|
* @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
|
* @param annotationName the fully qualified class name of the annotation
|
||||||
* type to find (as an alternative to {@code annotationType})
|
* type to find (as an alternative to {@code annotationType})
|
||||||
* @param containerType the type of the container that holds repeatable
|
* @param containerType the type of the container that holds repeatable
|
||||||
|
|
@ -912,7 +965,7 @@ public abstract class AnnotatedElementUtils {
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
private static <T> T searchWithGetSemanticsInAnnotations(@Nullable AnnotatedElement element,
|
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,
|
@Nullable String annotationName, @Nullable Class<? extends Annotation> containerType,
|
||||||
Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth) {
|
Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth) {
|
||||||
|
|
||||||
|
|
@ -920,7 +973,7 @@ public abstract class AnnotatedElementUtils {
|
||||||
for (Annotation annotation : annotations) {
|
for (Annotation annotation : annotations) {
|
||||||
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
|
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
|
||||||
if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) {
|
if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) {
|
||||||
if (currentAnnotationType == annotationType ||
|
if (annotationTypes.contains(currentAnnotationType) ||
|
||||||
currentAnnotationType.getName().equals(annotationName) ||
|
currentAnnotationType.getName().equals(annotationName) ||
|
||||||
processor.alwaysProcesses()) {
|
processor.alwaysProcesses()) {
|
||||||
T result = processor.process(element, annotation, metaDepth);
|
T result = processor.process(element, annotation, metaDepth);
|
||||||
|
|
@ -950,8 +1003,8 @@ public abstract class AnnotatedElementUtils {
|
||||||
// Recursively search in meta-annotations
|
// Recursively search in meta-annotations
|
||||||
for (Annotation annotation : annotations) {
|
for (Annotation annotation : annotations) {
|
||||||
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
|
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
|
||||||
if (hasSearchableMetaAnnotations(currentAnnotationType, annotationType, annotationName)) {
|
if (!AnnotationUtils.hasPlainJavaAnnotationsOnly(currentAnnotationType)) {
|
||||||
T result = searchWithGetSemantics(currentAnnotationType, annotationType,
|
T result = searchWithGetSemantics(currentAnnotationType, annotationTypes,
|
||||||
annotationName, containerType, processor, visited, metaDepth + 1);
|
annotationName, containerType, processor, visited, metaDepth + 1);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
processor.postProcess(element, annotation, result);
|
processor.postProcess(element, annotation, result);
|
||||||
|
|
@ -985,7 +1038,9 @@ public abstract class AnnotatedElementUtils {
|
||||||
@Nullable Class<? extends Annotation> annotationType,
|
@Nullable Class<? extends Annotation> annotationType,
|
||||||
@Nullable String annotationName, Processor<T> processor) {
|
@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
|
* {@code annotationType} on the specified {@code element}, following
|
||||||
* <em>find semantics</em>.
|
* <em>find semantics</em>.
|
||||||
* @param element the annotated element
|
* @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
|
* @param annotationName the fully qualified class name of the annotation
|
||||||
* type to find (as an alternative to {@code annotationType})
|
* type to find (as an alternative to {@code annotationType})
|
||||||
* @param containerType the type of the container that holds repeatable
|
* @param containerType the type of the container that holds repeatable
|
||||||
|
|
@ -1004,7 +1059,7 @@ public abstract class AnnotatedElementUtils {
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
private static <T> T searchWithFindSemantics(AnnotatedElement element,
|
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) {
|
@Nullable Class<? extends Annotation> containerType, Processor<T> processor) {
|
||||||
|
|
||||||
if (containerType != null && !processor.aggregates()) {
|
if (containerType != null && !processor.aggregates()) {
|
||||||
|
|
@ -1014,7 +1069,7 @@ public abstract class AnnotatedElementUtils {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return searchWithFindSemantics(
|
return searchWithFindSemantics(
|
||||||
element, annotationType, annotationName, containerType, processor, new HashSet<>(), 0);
|
element, annotationTypes, annotationName, containerType, processor, new HashSet<>(), 0);
|
||||||
}
|
}
|
||||||
catch (Throwable ex) {
|
catch (Throwable ex) {
|
||||||
AnnotationUtils.rethrowAnnotationConfigurationException(ex);
|
AnnotationUtils.rethrowAnnotationConfigurationException(ex);
|
||||||
|
|
@ -1029,7 +1084,7 @@ public abstract class AnnotatedElementUtils {
|
||||||
* <p>The {@code metaDepth} parameter is explained in the
|
* <p>The {@code metaDepth} parameter is explained in the
|
||||||
* {@link Processor#process process()} method of the {@link Processor} API.
|
* {@link Processor#process process()} method of the {@link Processor} API.
|
||||||
* @param element the annotated element (never {@code null})
|
* @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
|
* @param annotationName the fully qualified class name of the annotation
|
||||||
* type to find (as an alternative to {@code annotationType})
|
* type to find (as an alternative to {@code annotationType})
|
||||||
* @param containerType the type of the container that holds repeatable
|
* @param containerType the type of the container that holds repeatable
|
||||||
|
|
@ -1042,14 +1097,14 @@ public abstract class AnnotatedElementUtils {
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
private static <T> T searchWithFindSemantics(AnnotatedElement element,
|
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,
|
@Nullable Class<? extends Annotation> containerType, Processor<T> processor,
|
||||||
Set<AnnotatedElement> visited, int metaDepth) {
|
Set<AnnotatedElement> visited, int metaDepth) {
|
||||||
|
|
||||||
if (visited.add(element)) {
|
if (visited.add(element)) {
|
||||||
try {
|
try {
|
||||||
// Locally declared annotations (ignoring @Inherited)
|
// Locally declared annotations (ignoring @Inherited)
|
||||||
Annotation[] annotations = element.getDeclaredAnnotations();
|
Annotation[] annotations = AnnotationUtils.getDeclaredAnnotations(element);
|
||||||
if (annotations.length > 0) {
|
if (annotations.length > 0) {
|
||||||
List<T> aggregatedResults = (processor.aggregates() ? new ArrayList<>() : null);
|
List<T> aggregatedResults = (processor.aggregates() ? new ArrayList<>() : null);
|
||||||
|
|
||||||
|
|
@ -1057,7 +1112,7 @@ public abstract class AnnotatedElementUtils {
|
||||||
for (Annotation annotation : annotations) {
|
for (Annotation annotation : annotations) {
|
||||||
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
|
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
|
||||||
if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) {
|
if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) {
|
||||||
if (currentAnnotationType == annotationType ||
|
if (annotationTypes.contains(currentAnnotationType) ||
|
||||||
currentAnnotationType.getName().equals(annotationName) ||
|
currentAnnotationType.getName().equals(annotationName) ||
|
||||||
processor.alwaysProcesses()) {
|
processor.alwaysProcesses()) {
|
||||||
T result = processor.process(element, annotation, metaDepth);
|
T result = processor.process(element, annotation, metaDepth);
|
||||||
|
|
@ -1087,8 +1142,8 @@ public abstract class AnnotatedElementUtils {
|
||||||
// Recursively search in meta-annotations
|
// Recursively search in meta-annotations
|
||||||
for (Annotation annotation : annotations) {
|
for (Annotation annotation : annotations) {
|
||||||
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
|
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
|
||||||
if (hasSearchableMetaAnnotations(currentAnnotationType, annotationType, annotationName)) {
|
if (!AnnotationUtils.hasPlainJavaAnnotationsOnly(currentAnnotationType)) {
|
||||||
T result = searchWithFindSemantics(currentAnnotationType, annotationType, annotationName,
|
T result = searchWithFindSemantics(currentAnnotationType, annotationTypes, annotationName,
|
||||||
containerType, processor, visited, metaDepth + 1);
|
containerType, processor, visited, metaDepth + 1);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
processor.postProcess(currentAnnotationType, annotation, result);
|
processor.postProcess(currentAnnotationType, annotation, result);
|
||||||
|
|
@ -1115,7 +1170,7 @@ public abstract class AnnotatedElementUtils {
|
||||||
// Search on possibly bridged method
|
// Search on possibly bridged method
|
||||||
Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
|
Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
|
||||||
if (resolvedMethod != method) {
|
if (resolvedMethod != method) {
|
||||||
result = searchWithFindSemantics(resolvedMethod, annotationType, annotationName,
|
result = searchWithFindSemantics(resolvedMethod, annotationTypes, annotationName,
|
||||||
containerType, processor, visited, metaDepth);
|
containerType, processor, visited, metaDepth);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -1125,7 +1180,7 @@ public abstract class AnnotatedElementUtils {
|
||||||
// Search on methods in interfaces declared locally
|
// Search on methods in interfaces declared locally
|
||||||
Class<?>[] ifcs = method.getDeclaringClass().getInterfaces();
|
Class<?>[] ifcs = method.getDeclaringClass().getInterfaces();
|
||||||
if (ifcs.length > 0) {
|
if (ifcs.length > 0) {
|
||||||
result = searchOnInterfaces(method, annotationType, annotationName,
|
result = searchOnInterfaces(method, annotationTypes, annotationName,
|
||||||
containerType, processor, visited, metaDepth, ifcs);
|
containerType, processor, visited, metaDepth, ifcs);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -1136,7 +1191,7 @@ public abstract class AnnotatedElementUtils {
|
||||||
Class<?> clazz = method.getDeclaringClass();
|
Class<?> clazz = method.getDeclaringClass();
|
||||||
while (true) {
|
while (true) {
|
||||||
clazz = clazz.getSuperclass();
|
clazz = clazz.getSuperclass();
|
||||||
if (clazz == null || Object.class == clazz) {
|
if (clazz == null || clazz == Object.class) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Set<Method> annotatedMethods = AnnotationUtils.getAnnotatedMethodsInBaseType(clazz);
|
Set<Method> annotatedMethods = AnnotationUtils.getAnnotatedMethodsInBaseType(clazz);
|
||||||
|
|
@ -1144,8 +1199,8 @@ public abstract class AnnotatedElementUtils {
|
||||||
for (Method annotatedMethod : annotatedMethods) {
|
for (Method annotatedMethod : annotatedMethods) {
|
||||||
if (AnnotationUtils.isOverride(method, annotatedMethod)) {
|
if (AnnotationUtils.isOverride(method, annotatedMethod)) {
|
||||||
Method resolvedSuperMethod = BridgeMethodResolver.findBridgedMethod(annotatedMethod);
|
Method resolvedSuperMethod = BridgeMethodResolver.findBridgedMethod(annotatedMethod);
|
||||||
result = searchWithFindSemantics(resolvedSuperMethod, annotationType, annotationName,
|
result = searchWithFindSemantics(resolvedSuperMethod, annotationTypes,
|
||||||
containerType, processor, visited, metaDepth);
|
annotationName, containerType, processor, visited, metaDepth);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -1153,7 +1208,7 @@ public abstract class AnnotatedElementUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Search on interfaces declared on superclass
|
// Search on interfaces declared on superclass
|
||||||
result = searchOnInterfaces(method, annotationType, annotationName,
|
result = searchOnInterfaces(method, annotationTypes, annotationName,
|
||||||
containerType, processor, visited, metaDepth, clazz.getInterfaces());
|
containerType, processor, visited, metaDepth, clazz.getInterfaces());
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -1162,23 +1217,23 @@ public abstract class AnnotatedElementUtils {
|
||||||
}
|
}
|
||||||
else if (element instanceof Class) {
|
else if (element instanceof Class) {
|
||||||
Class<?> clazz = (Class<?>) element;
|
Class<?> clazz = (Class<?>) element;
|
||||||
|
if (!Annotation.class.isAssignableFrom(clazz)) {
|
||||||
// Search on interfaces
|
// Search on interfaces
|
||||||
for (Class<?> ifc : clazz.getInterfaces()) {
|
for (Class<?> ifc : clazz.getInterfaces()) {
|
||||||
T result = searchWithFindSemantics(ifc, annotationType, annotationName,
|
T result = searchWithFindSemantics(ifc, annotationTypes, annotationName,
|
||||||
containerType, processor, visited, metaDepth);
|
containerType, processor, visited, metaDepth);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
return result;
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
// Search on superclass
|
||||||
|
Class<?> superclass = clazz.getSuperclass();
|
||||||
// Search on superclass
|
if (superclass != null && superclass != Object.class) {
|
||||||
Class<?> superclass = clazz.getSuperclass();
|
T result = searchWithFindSemantics(superclass, annotationTypes, annotationName,
|
||||||
if (superclass != null && Object.class != superclass) {
|
containerType, processor, visited, metaDepth);
|
||||||
T result = searchWithFindSemantics(superclass, annotationType, annotationName,
|
if (result != null) {
|
||||||
containerType, processor, visited, metaDepth);
|
return result;
|
||||||
if (result != null) {
|
}
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1191,7 +1246,7 @@ public abstract class AnnotatedElementUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@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,
|
@Nullable String annotationName, @Nullable Class<? extends Annotation> containerType,
|
||||||
Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth, Class<?>[] ifcs) {
|
Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth, Class<?>[] ifcs) {
|
||||||
|
|
||||||
|
|
@ -1200,7 +1255,7 @@ public abstract class AnnotatedElementUtils {
|
||||||
if (!annotatedMethods.isEmpty()) {
|
if (!annotatedMethods.isEmpty()) {
|
||||||
for (Method annotatedMethod : annotatedMethods) {
|
for (Method annotatedMethod : annotatedMethods) {
|
||||||
if (AnnotationUtils.isOverride(method, annotatedMethod)) {
|
if (AnnotationUtils.isOverride(method, annotatedMethod)) {
|
||||||
T result = searchWithFindSemantics(annotatedMethod, annotationType, annotationName,
|
T result = searchWithFindSemantics(annotatedMethod, annotationTypes, annotationName,
|
||||||
containerType, processor, visited, metaDepth);
|
containerType, processor, visited, metaDepth);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -1213,26 +1268,6 @@ public abstract class AnnotatedElementUtils {
|
||||||
return null;
|
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}
|
* Get the array of raw (unsynthesized) annotations from the {@code value}
|
||||||
* attribute of the supplied repeatable annotation {@code container}.
|
* 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<>();
|
Set<A> annotations = new LinkedHashSet<>();
|
||||||
for (AnnotationAttributes attributes : aggregatedResults) {
|
for (AnnotationAttributes attributes : aggregatedResults) {
|
||||||
AnnotationUtils.postProcessAnnotationAttributes(element, attributes, false, false);
|
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;
|
return annotations;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,9 @@ public abstract class AnnotationUtils {
|
||||||
private static final Map<AnnotationCacheKey, Boolean> metaPresentCache =
|
private static final Map<AnnotationCacheKey, Boolean> metaPresentCache =
|
||||||
new ConcurrentReferenceHashMap<>(256);
|
new ConcurrentReferenceHashMap<>(256);
|
||||||
|
|
||||||
|
private static final Map<AnnotatedElement, Annotation[]> declaredAnnotationsCache =
|
||||||
|
new ConcurrentReferenceHashMap<>(256);
|
||||||
|
|
||||||
private static final Map<Class<?>, Set<Method>> annotatedBaseTypeCache =
|
private static final Map<Class<?>, Set<Method>> annotatedBaseTypeCache =
|
||||||
new ConcurrentReferenceHashMap<>(256);
|
new ConcurrentReferenceHashMap<>(256);
|
||||||
|
|
||||||
|
|
@ -341,18 +344,13 @@ public abstract class AnnotationUtils {
|
||||||
Class<A> annotationType, @Nullable Class<? extends Annotation> containerAnnotationType) {
|
Class<A> annotationType, @Nullable Class<? extends Annotation> containerAnnotationType) {
|
||||||
|
|
||||||
Set<A> annotations = getDeclaredRepeatableAnnotations(annotatedElement, annotationType, containerAnnotationType);
|
Set<A> annotations = getDeclaredRepeatableAnnotations(annotatedElement, annotationType, containerAnnotationType);
|
||||||
if (!annotations.isEmpty()) {
|
if (annotations.isEmpty() && annotatedElement instanceof Class) {
|
||||||
return annotations;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (annotatedElement instanceof Class) {
|
|
||||||
Class<?> superclass = ((Class<?>) annotatedElement).getSuperclass();
|
Class<?> superclass = ((Class<?>) annotatedElement).getSuperclass();
|
||||||
if (superclass != null && Object.class != superclass) {
|
if (superclass != null && superclass != Object.class) {
|
||||||
return getRepeatableAnnotations(superclass, annotationType, containerAnnotationType);
|
return getRepeatableAnnotations(superclass, annotationType, containerAnnotationType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return annotations;
|
||||||
return getRepeatableAnnotations(annotatedElement, annotationType, containerAnnotationType, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -422,37 +420,11 @@ public abstract class AnnotationUtils {
|
||||||
public static <A extends Annotation> Set<A> getDeclaredRepeatableAnnotations(AnnotatedElement annotatedElement,
|
public static <A extends Annotation> Set<A> getDeclaredRepeatableAnnotations(AnnotatedElement annotatedElement,
|
||||||
Class<A> annotationType, @Nullable Class<? extends Annotation> containerAnnotationType) {
|
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 {
|
try {
|
||||||
if (annotatedElement instanceof Method) {
|
if (annotatedElement instanceof Method) {
|
||||||
annotatedElement = BridgeMethodResolver.findBridgedMethod((Method) annotatedElement);
|
annotatedElement = BridgeMethodResolver.findBridgedMethod((Method) annotatedElement);
|
||||||
}
|
}
|
||||||
return new AnnotationCollector<>(annotationType, containerAnnotationType, declaredMode).getResult(annotatedElement);
|
return new AnnotationCollector<>(annotationType, containerAnnotationType).getResult(annotatedElement);
|
||||||
}
|
}
|
||||||
catch (Throwable ex) {
|
catch (Throwable ex) {
|
||||||
handleIntrospectionFailure(annotatedElement, ex);
|
handleIntrospectionFailure(annotatedElement, ex);
|
||||||
|
|
@ -502,7 +474,7 @@ public abstract class AnnotationUtils {
|
||||||
if (annotation != null) {
|
if (annotation != null) {
|
||||||
return annotation;
|
return annotation;
|
||||||
}
|
}
|
||||||
for (Annotation declaredAnn : annotatedElement.getDeclaredAnnotations()) {
|
for (Annotation declaredAnn : getDeclaredAnnotations(annotatedElement)) {
|
||||||
Class<? extends Annotation> declaredType = declaredAnn.annotationType();
|
Class<? extends Annotation> declaredType = declaredAnn.annotationType();
|
||||||
if (!isInJavaLangAnnotationPackage(declaredType) && visited.add(declaredAnn)) {
|
if (!isInJavaLangAnnotationPackage(declaredType) && visited.add(declaredAnn)) {
|
||||||
annotation = findAnnotation((AnnotatedElement) declaredType, annotationType, visited);
|
annotation = findAnnotation((AnnotatedElement) declaredType, annotationType, visited);
|
||||||
|
|
@ -554,7 +526,7 @@ public abstract class AnnotationUtils {
|
||||||
Class<?> clazz = method.getDeclaringClass();
|
Class<?> clazz = method.getDeclaringClass();
|
||||||
while (result == null) {
|
while (result == null) {
|
||||||
clazz = clazz.getSuperclass();
|
clazz = clazz.getSuperclass();
|
||||||
if (clazz == null || Object.class == clazz) {
|
if (clazz == null || clazz == Object.class) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Set<Method> annotatedMethods = getAnnotatedMethodsInBaseType(clazz);
|
Set<Method> annotatedMethods = getAnnotatedMethodsInBaseType(clazz);
|
||||||
|
|
@ -601,6 +573,35 @@ public abstract class AnnotationUtils {
|
||||||
return null;
|
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) {
|
static Set<Method> getAnnotatedMethodsInBaseType(Class<?> baseType) {
|
||||||
boolean ifcCheck = baseType.isInterface();
|
boolean ifcCheck = baseType.isInterface();
|
||||||
if (ifcCheck && ClassUtils.isJavaLanguageInterface(baseType)) {
|
if (ifcCheck && ClassUtils.isJavaLanguageInterface(baseType)) {
|
||||||
|
|
@ -635,8 +636,15 @@ public abstract class AnnotationUtils {
|
||||||
return annotatedMethods;
|
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) {
|
private static boolean hasSearchableAnnotations(Method ifcMethod) {
|
||||||
Annotation[] anns = ifcMethod.getAnnotations();
|
Annotation[] anns = getDeclaredAnnotations(ifcMethod);
|
||||||
if (anns.length == 0) {
|
if (anns.length == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -649,21 +657,21 @@ public abstract class AnnotationUtils {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isOverride(Method method, Method candidate) {
|
/**
|
||||||
if (!candidate.getName().equals(method.getName()) ||
|
* Retrieve a potentially cached array of declared annotations for the
|
||||||
candidate.getParameterCount() != method.getParameterCount()) {
|
* given element.
|
||||||
return false;
|
* @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();
|
return element.getDeclaredAnnotations();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -741,7 +749,7 @@ public abstract class AnnotationUtils {
|
||||||
if (annotation != null) {
|
if (annotation != null) {
|
||||||
return annotation;
|
return annotation;
|
||||||
}
|
}
|
||||||
for (Annotation declaredAnn : clazz.getDeclaredAnnotations()) {
|
for (Annotation declaredAnn : getDeclaredAnnotations(clazz)) {
|
||||||
Class<? extends Annotation> declaredType = declaredAnn.annotationType();
|
Class<? extends Annotation> declaredType = declaredAnn.annotationType();
|
||||||
if (!isInJavaLangAnnotationPackage(declaredType) && visited.add(declaredAnn)) {
|
if (!isInJavaLangAnnotationPackage(declaredType) && visited.add(declaredAnn)) {
|
||||||
annotation = findAnnotation(declaredType, annotationType, visited);
|
annotation = findAnnotation(declaredType, annotationType, visited);
|
||||||
|
|
@ -764,7 +772,7 @@ public abstract class AnnotationUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
Class<?> superclass = clazz.getSuperclass();
|
Class<?> superclass = clazz.getSuperclass();
|
||||||
if (superclass == null || Object.class == superclass) {
|
if (superclass == null || superclass == Object.class) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return findAnnotation(superclass, annotationType, visited);
|
return findAnnotation(superclass, annotationType, visited);
|
||||||
|
|
@ -794,7 +802,7 @@ public abstract class AnnotationUtils {
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Class<?> findAnnotationDeclaringClass(Class<? extends Annotation> annotationType, @Nullable Class<?> clazz) {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
if (isAnnotationDeclaredLocally(annotationType, clazz)) {
|
if (isAnnotationDeclaredLocally(annotationType, clazz)) {
|
||||||
|
|
@ -828,8 +836,10 @@ public abstract class AnnotationUtils {
|
||||||
* @see #isAnnotationDeclaredLocally(Class, Class)
|
* @see #isAnnotationDeclaredLocally(Class, Class)
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Class<?> findAnnotationDeclaringClassForTypes(List<Class<? extends Annotation>> annotationTypes, @Nullable Class<?> clazz) {
|
public static Class<?> findAnnotationDeclaringClassForTypes(
|
||||||
if (clazz == null || Object.class == clazz) {
|
List<Class<? extends Annotation>> annotationTypes, @Nullable Class<?> clazz) {
|
||||||
|
|
||||||
|
if (clazz == null || clazz == Object.class) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
for (Class<? extends Annotation> annotationType : annotationTypes) {
|
for (Class<? extends Annotation> annotationType : annotationTypes) {
|
||||||
|
|
@ -1965,6 +1975,7 @@ public abstract class AnnotationUtils {
|
||||||
public static void clearCache() {
|
public static void clearCache() {
|
||||||
findAnnotationCache.clear();
|
findAnnotationCache.clear();
|
||||||
metaPresentCache.clear();
|
metaPresentCache.clear();
|
||||||
|
declaredAnnotationsCache.clear();
|
||||||
annotatedBaseTypeCache.clear();
|
annotatedBaseTypeCache.clear();
|
||||||
synthesizableCache.clear();
|
synthesizableCache.clear();
|
||||||
attributeAliasesCache.clear();
|
attributeAliasesCache.clear();
|
||||||
|
|
@ -2027,19 +2038,14 @@ public abstract class AnnotationUtils {
|
||||||
@Nullable
|
@Nullable
|
||||||
private final Class<? extends Annotation> containerAnnotationType;
|
private final Class<? extends Annotation> containerAnnotationType;
|
||||||
|
|
||||||
private final boolean declaredMode;
|
|
||||||
|
|
||||||
private final Set<AnnotatedElement> visited = new HashSet<>();
|
private final Set<AnnotatedElement> visited = new HashSet<>();
|
||||||
|
|
||||||
private final Set<A> result = new LinkedHashSet<>();
|
private final Set<A> result = new LinkedHashSet<>();
|
||||||
|
|
||||||
AnnotationCollector(Class<A> annotationType,
|
AnnotationCollector(Class<A> annotationType,@Nullable Class<? extends Annotation> containerAnnotationType) {
|
||||||
@Nullable Class<? extends Annotation> containerAnnotationType, boolean declaredMode) {
|
|
||||||
|
|
||||||
this.annotationType = annotationType;
|
this.annotationType = annotationType;
|
||||||
this.containerAnnotationType = (containerAnnotationType != null ? containerAnnotationType :
|
this.containerAnnotationType = (containerAnnotationType != null ? containerAnnotationType :
|
||||||
resolveContainerAnnotationType(annotationType));
|
resolveContainerAnnotationType(annotationType));
|
||||||
this.declaredMode = declaredMode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<A> getResult(AnnotatedElement element) {
|
Set<A> getResult(AnnotatedElement element) {
|
||||||
|
|
@ -2051,7 +2057,7 @@ public abstract class AnnotationUtils {
|
||||||
private void process(AnnotatedElement element) {
|
private void process(AnnotatedElement element) {
|
||||||
if (this.visited.add(element)) {
|
if (this.visited.add(element)) {
|
||||||
try {
|
try {
|
||||||
Annotation[] annotations = (this.declaredMode ? element.getDeclaredAnnotations() : element.getAnnotations());
|
Annotation[] annotations = getDeclaredAnnotations(element);
|
||||||
for (Annotation ann : annotations) {
|
for (Annotation ann : annotations) {
|
||||||
Class<? extends Annotation> currentAnnotationType = ann.annotationType();
|
Class<? extends Annotation> currentAnnotationType = ann.annotationType();
|
||||||
if (ObjectUtils.nullSafeEquals(this.annotationType, currentAnnotationType)) {
|
if (ObjectUtils.nullSafeEquals(this.annotationType, currentAnnotationType)) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue