org.springframework.cache.annotation.SpringCacheAnnotationParser Maven / Gradle / Ivy
Show all versions of spring-context Show documentation
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.annotation;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.cache.interceptor.CacheEvictOperation;
import org.springframework.cache.interceptor.CacheOperation;
import org.springframework.cache.interceptor.CachePutOperation;
import org.springframework.cache.interceptor.CacheableOperation;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Strategy implementation for parsing Spring's {@link Caching}, {@link Cacheable},
* {@link CacheEvict}, and {@link CachePut} annotations.
*
* @author Costin Leau
* @author Juergen Hoeller
* @author Chris Beams
* @author Phillip Webb
* @author Stephane Nicoll
* @author Sam Brannen
* @since 3.1
*/
@SuppressWarnings("serial")
public class SpringCacheAnnotationParser implements CacheAnnotationParser, Serializable {
@Override
public Collection parseCacheAnnotations(Class> type) {
DefaultCacheConfig defaultConfig = getDefaultCacheConfig(type);
return parseCacheAnnotations(defaultConfig, type);
}
@Override
public Collection parseCacheAnnotations(Method method) {
DefaultCacheConfig defaultConfig = getDefaultCacheConfig(method.getDeclaringClass());
return parseCacheAnnotations(defaultConfig, method);
}
protected Collection parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) {
Collection ops = null;
Collection cacheables = getAnnotations(ae, Cacheable.class);
if (cacheables != null) {
ops = lazyInit(ops);
for (Cacheable cacheable : cacheables) {
ops.add(parseCacheableAnnotation(ae, cachingConfig, cacheable));
}
}
Collection evicts = getAnnotations(ae, CacheEvict.class);
if (evicts != null) {
ops = lazyInit(ops);
for (CacheEvict evict : evicts) {
ops.add(parseEvictAnnotation(ae, cachingConfig, evict));
}
}
Collection puts = getAnnotations(ae, CachePut.class);
if (puts != null) {
ops = lazyInit(ops);
for (CachePut put : puts) {
ops.add(parsePutAnnotation(ae, cachingConfig, put));
}
}
Collection cachings = getAnnotations(ae, Caching.class);
if (cachings != null) {
ops = lazyInit(ops);
for (Caching caching : cachings) {
ops.addAll(parseCachingAnnotation(ae, cachingConfig, caching));
}
}
return ops;
}
private Collection lazyInit(Collection ops) {
return (ops != null ? ops : new ArrayList(1));
}
CacheableOperation parseCacheableAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, Cacheable cacheable) {
CacheableOperation op = new CacheableOperation();
op.setCacheNames(cacheable.cacheNames());
op.setCondition(cacheable.condition());
op.setUnless(cacheable.unless());
op.setKey(cacheable.key());
op.setKeyGenerator(cacheable.keyGenerator());
op.setCacheManager(cacheable.cacheManager());
op.setCacheResolver(cacheable.cacheResolver());
op.setName(ae.toString());
defaultConfig.applyDefault(op);
validateCacheOperation(ae, op);
return op;
}
CacheEvictOperation parseEvictAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, CacheEvict cacheEvict) {
CacheEvictOperation op = new CacheEvictOperation();
op.setCacheNames(cacheEvict.cacheNames());
op.setCondition(cacheEvict.condition());
op.setKey(cacheEvict.key());
op.setKeyGenerator(cacheEvict.keyGenerator());
op.setCacheManager(cacheEvict.cacheManager());
op.setCacheResolver(cacheEvict.cacheResolver());
op.setCacheWide(cacheEvict.allEntries());
op.setBeforeInvocation(cacheEvict.beforeInvocation());
op.setName(ae.toString());
defaultConfig.applyDefault(op);
validateCacheOperation(ae, op);
return op;
}
CacheOperation parsePutAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, CachePut cachePut) {
CachePutOperation op = new CachePutOperation();
op.setCacheNames(cachePut.cacheNames());
op.setCondition(cachePut.condition());
op.setUnless(cachePut.unless());
op.setKey(cachePut.key());
op.setKeyGenerator(cachePut.keyGenerator());
op.setCacheManager(cachePut.cacheManager());
op.setCacheResolver(cachePut.cacheResolver());
op.setName(ae.toString());
defaultConfig.applyDefault(op);
validateCacheOperation(ae, op);
return op;
}
Collection parseCachingAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, Caching caching) {
Collection ops = null;
Cacheable[] cacheables = caching.cacheable();
if (!ObjectUtils.isEmpty(cacheables)) {
ops = lazyInit(ops);
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));
}
}
CachePut[] cachePuts = caching.put();
if (!ObjectUtils.isEmpty(cachePuts)) {
ops = lazyInit(ops);
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 = AnnotationUtils.getAnnotation(target, CacheConfig.class);
if (annotation != null) {
return new DefaultCacheConfig(annotation.cacheNames(), annotation.keyGenerator(),
annotation.cacheManager(), annotation.cacheResolver());
}
return new DefaultCacheConfig();
}
private Collection getAnnotations(AnnotatedElement ae, Class annotationType) {
Collection anns = new ArrayList(2);
// look at raw annotation
T ann = ae.getAnnotation(annotationType);
if (ann != null) {
anns.add(AnnotationUtils.synthesizeAnnotation(ann, ae));
}
// scan meta-annotations
for (Annotation metaAnn : ae.getAnnotations()) {
ann = metaAnn.annotationType().getAnnotation(annotationType);
if (ann != null) {
anns.add(AnnotationUtils.synthesizeAnnotation(ann, ae));
}
}
return (anns.isEmpty() ? null : anns);
}
/**
* Validates the specified {@link CacheOperation}.
* Throws an {@link IllegalStateException} if the state of the operation is
* invalid. As there might be multiple sources for default values, this ensure
* that the operation is in a proper state before being returned.
* @param ae the annotated element of the cache operation
* @param operation the {@link CacheOperation} to validate
*/
private void validateCacheOperation(AnnotatedElement ae, CacheOperation operation) {
if (StringUtils.hasText(operation.getKey()) && StringUtils.hasText(operation.getKeyGenerator())) {
throw new IllegalStateException("Invalid cache annotation configuration on '" +
ae.toString() + "'. Both 'key' and 'keyGenerator' attributes have been set. " +
"These attributes are mutually exclusive: either set the SpEL expression used to" +
"compute the key at runtime or set the name of the KeyGenerator bean to use.");
}
if (StringUtils.hasText(operation.getCacheManager()) && StringUtils.hasText(operation.getCacheResolver())) {
throw new IllegalStateException("Invalid cache annotation configuration on '" +
ae.toString() + "'. Both 'cacheManager' and 'cacheResolver' attributes have been set. " +
"These attributes are mutually exclusive: the cache manager is used to configure a" +
"default cache resolver if none is set. If a cache resolver is set, the cache manager" +
"won't be used.");
}
}
@Override
public boolean equals(Object other) {
return (this == other || other instanceof SpringCacheAnnotationParser);
}
@Override
public int hashCode() {
return SpringCacheAnnotationParser.class.hashCode();
}
/**
* Provides default settings for a given set of cache operations.
*/
static class DefaultCacheConfig {
private final String[] cacheNames;
private final String keyGenerator;
private final String cacheManager;
private final String cacheResolver;
public DefaultCacheConfig() {
this(null, null, null, null);
}
private DefaultCacheConfig(String[] cacheNames, String keyGenerator, String cacheManager, String cacheResolver) {
this.cacheNames = cacheNames;
this.keyGenerator = keyGenerator;
this.cacheManager = cacheManager;
this.cacheResolver = cacheResolver;
}
/**
* Apply the defaults to the specified {@link CacheOperation}.
* @param operation the operation to update
*/
public void applyDefault(CacheOperation operation) {
if (operation.getCacheNames().isEmpty() && this.cacheNames != null) {
operation.setCacheNames(this.cacheNames);
}
if (!StringUtils.hasText(operation.getKey()) && !StringUtils.hasText(operation.getKeyGenerator()) &&
StringUtils.hasText(this.keyGenerator)) {
operation.setKeyGenerator(this.keyGenerator);
}
if (StringUtils.hasText(operation.getCacheManager()) || StringUtils.hasText(operation.getCacheResolver())) {
// One of these is set so we should not inherit anything
}
else if (StringUtils.hasText(this.cacheResolver)) {
operation.setCacheResolver(this.cacheResolver);
}
else if (StringUtils.hasText(this.cacheManager)) {
operation.setCacheManager(this.cacheManager);
}
}
}
}