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:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user