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:
Juergen Hoeller
2018-08-15 17:30:14 +02:00
parent 50dc8c2358
commit 109a2b49e5
6 changed files with 292 additions and 270 deletions

View File

@@ -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);
}