001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.cache.annotation; 018 019import java.io.Serializable; 020import java.lang.annotation.Annotation; 021import java.lang.reflect.AnnotatedElement; 022import java.lang.reflect.Method; 023import java.util.ArrayList; 024import java.util.Collection; 025 026import org.springframework.cache.interceptor.CacheEvictOperation; 027import org.springframework.cache.interceptor.CacheOperation; 028import org.springframework.cache.interceptor.CachePutOperation; 029import org.springframework.cache.interceptor.CacheableOperation; 030import org.springframework.core.annotation.AnnotatedElementUtils; 031import org.springframework.util.ObjectUtils; 032import org.springframework.util.StringUtils; 033 034/** 035 * Strategy implementation for parsing Spring's {@link Caching}, {@link Cacheable}, 036 * {@link CacheEvict}, and {@link CachePut} annotations. 037 * 038 * @author Costin Leau 039 * @author Juergen Hoeller 040 * @author Chris Beams 041 * @author Phillip Webb 042 * @author Stephane Nicoll 043 * @author Sam Brannen 044 * @since 3.1 045 */ 046@SuppressWarnings("serial") 047public class SpringCacheAnnotationParser implements CacheAnnotationParser, Serializable { 048 049 @Override 050 public Collection<CacheOperation> parseCacheAnnotations(Class<?> type) { 051 DefaultCacheConfig defaultConfig = getDefaultCacheConfig(type); 052 return parseCacheAnnotations(defaultConfig, type); 053 } 054 055 @Override 056 public Collection<CacheOperation> parseCacheAnnotations(Method method) { 057 DefaultCacheConfig defaultConfig = getDefaultCacheConfig(method.getDeclaringClass()); 058 return parseCacheAnnotations(defaultConfig, method); 059 } 060 061 private Collection<CacheOperation> parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) { 062 Collection<CacheOperation> ops = null; 063 064 Collection<Cacheable> cacheables = AnnotatedElementUtils.getAllMergedAnnotations(ae, Cacheable.class); 065 if (!cacheables.isEmpty()) { 066 ops = lazyInit(ops); 067 for (Cacheable cacheable : cacheables) { 068 ops.add(parseCacheableAnnotation(ae, cachingConfig, cacheable)); 069 } 070 } 071 072 Collection<CacheEvict> evicts = AnnotatedElementUtils.getAllMergedAnnotations(ae, CacheEvict.class); 073 if (!evicts.isEmpty()) { 074 ops = lazyInit(ops); 075 for (CacheEvict evict : evicts) { 076 ops.add(parseEvictAnnotation(ae, cachingConfig, evict)); 077 } 078 } 079 080 Collection<CachePut> puts = AnnotatedElementUtils.getAllMergedAnnotations(ae, CachePut.class); 081 if (!puts.isEmpty()) { 082 ops = lazyInit(ops); 083 for (CachePut put : puts) { 084 ops.add(parsePutAnnotation(ae, cachingConfig, put)); 085 } 086 } 087 088 Collection<Caching> cachings = AnnotatedElementUtils.getAllMergedAnnotations(ae, Caching.class); 089 if (!cachings.isEmpty()) { 090 ops = lazyInit(ops); 091 for (Caching caching : cachings) { 092 Collection<CacheOperation> cachingOps = parseCachingAnnotation(ae, cachingConfig, caching); 093 if (cachingOps != null) { 094 ops.addAll(cachingOps); 095 } 096 } 097 } 098 099 return ops; 100 } 101 102 private <T extends Annotation> Collection<CacheOperation> lazyInit(Collection<CacheOperation> ops) { 103 return (ops != null ? ops : new ArrayList<CacheOperation>(1)); 104 } 105 106 CacheableOperation parseCacheableAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, Cacheable cacheable) { 107 CacheableOperation.Builder builder = new CacheableOperation.Builder(); 108 109 builder.setName(ae.toString()); 110 builder.setCacheNames(cacheable.cacheNames()); 111 builder.setCondition(cacheable.condition()); 112 builder.setUnless(cacheable.unless()); 113 builder.setKey(cacheable.key()); 114 builder.setKeyGenerator(cacheable.keyGenerator()); 115 builder.setCacheManager(cacheable.cacheManager()); 116 builder.setCacheResolver(cacheable.cacheResolver()); 117 builder.setSync(cacheable.sync()); 118 119 defaultConfig.applyDefault(builder); 120 CacheableOperation op = builder.build(); 121 validateCacheOperation(ae, op); 122 123 return op; 124 } 125 126 CacheEvictOperation parseEvictAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, CacheEvict cacheEvict) { 127 CacheEvictOperation.Builder builder = new CacheEvictOperation.Builder(); 128 129 builder.setName(ae.toString()); 130 builder.setCacheNames(cacheEvict.cacheNames()); 131 builder.setCondition(cacheEvict.condition()); 132 builder.setKey(cacheEvict.key()); 133 builder.setKeyGenerator(cacheEvict.keyGenerator()); 134 builder.setCacheManager(cacheEvict.cacheManager()); 135 builder.setCacheResolver(cacheEvict.cacheResolver()); 136 builder.setCacheWide(cacheEvict.allEntries()); 137 builder.setBeforeInvocation(cacheEvict.beforeInvocation()); 138 139 defaultConfig.applyDefault(builder); 140 CacheEvictOperation op = builder.build(); 141 validateCacheOperation(ae, op); 142 143 return op; 144 } 145 146 CacheOperation parsePutAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, CachePut cachePut) { 147 CachePutOperation.Builder builder = new CachePutOperation.Builder(); 148 149 builder.setName(ae.toString()); 150 builder.setCacheNames(cachePut.cacheNames()); 151 builder.setCondition(cachePut.condition()); 152 builder.setUnless(cachePut.unless()); 153 builder.setKey(cachePut.key()); 154 builder.setKeyGenerator(cachePut.keyGenerator()); 155 builder.setCacheManager(cachePut.cacheManager()); 156 builder.setCacheResolver(cachePut.cacheResolver()); 157 158 defaultConfig.applyDefault(builder); 159 CachePutOperation op = builder.build(); 160 validateCacheOperation(ae, op); 161 162 return op; 163 } 164 165 Collection<CacheOperation> parseCachingAnnotation(AnnotatedElement ae, DefaultCacheConfig defaultConfig, Caching caching) { 166 Collection<CacheOperation> ops = null; 167 168 Cacheable[] cacheables = caching.cacheable(); 169 if (!ObjectUtils.isEmpty(cacheables)) { 170 ops = lazyInit(ops); 171 for (Cacheable cacheable : cacheables) { 172 ops.add(parseCacheableAnnotation(ae, defaultConfig, cacheable)); 173 } 174 } 175 CacheEvict[] cacheEvicts = caching.evict(); 176 if (!ObjectUtils.isEmpty(cacheEvicts)) { 177 ops = lazyInit(ops); 178 for (CacheEvict cacheEvict : cacheEvicts) { 179 ops.add(parseEvictAnnotation(ae, defaultConfig, cacheEvict)); 180 } 181 } 182 CachePut[] cachePuts = caching.put(); 183 if (!ObjectUtils.isEmpty(cachePuts)) { 184 ops = lazyInit(ops); 185 for (CachePut cachePut : cachePuts) { 186 ops.add(parsePutAnnotation(ae, defaultConfig, cachePut)); 187 } 188 } 189 190 return ops; 191 } 192 193 /** 194 * Provides the {@link DefaultCacheConfig} instance for the specified {@link Class}. 195 * @param target the class-level to handle 196 * @return the default config (never {@code null}) 197 */ 198 DefaultCacheConfig getDefaultCacheConfig(Class<?> target) { 199 CacheConfig annotation = AnnotatedElementUtils.getMergedAnnotation(target, CacheConfig.class); 200 if (annotation != null) { 201 return new DefaultCacheConfig(annotation.cacheNames(), annotation.keyGenerator(), 202 annotation.cacheManager(), annotation.cacheResolver()); 203 } 204 return new DefaultCacheConfig(); 205 } 206 207 /** 208 * Validates the specified {@link CacheOperation}. 209 * <p>Throws an {@link IllegalStateException} if the state of the operation is 210 * invalid. As there might be multiple sources for default values, this ensure 211 * that the operation is in a proper state before being returned. 212 * @param ae the annotated element of the cache operation 213 * @param operation the {@link CacheOperation} to validate 214 */ 215 private void validateCacheOperation(AnnotatedElement ae, CacheOperation operation) { 216 if (StringUtils.hasText(operation.getKey()) && StringUtils.hasText(operation.getKeyGenerator())) { 217 throw new IllegalStateException("Invalid cache annotation configuration on '" + 218 ae.toString() + "'. Both 'key' and 'keyGenerator' attributes have been set. " + 219 "These attributes are mutually exclusive: either set the SpEL expression used to" + 220 "compute the key at runtime or set the name of the KeyGenerator bean to use."); 221 } 222 if (StringUtils.hasText(operation.getCacheManager()) && StringUtils.hasText(operation.getCacheResolver())) { 223 throw new IllegalStateException("Invalid cache annotation configuration on '" + 224 ae.toString() + "'. Both 'cacheManager' and 'cacheResolver' attributes have been set. " + 225 "These attributes are mutually exclusive: the cache manager is used to configure a" + 226 "default cache resolver if none is set. If a cache resolver is set, the cache manager" + 227 "won't be used."); 228 } 229 } 230 231 @Override 232 public boolean equals(Object other) { 233 return (this == other || other instanceof SpringCacheAnnotationParser); 234 } 235 236 @Override 237 public int hashCode() { 238 return SpringCacheAnnotationParser.class.hashCode(); 239 } 240 241 242 /** 243 * Provides default settings for a given set of cache operations. 244 */ 245 static class DefaultCacheConfig { 246 247 private final String[] cacheNames; 248 249 private final String keyGenerator; 250 251 private final String cacheManager; 252 253 private final String cacheResolver; 254 255 public DefaultCacheConfig() { 256 this(null, null, null, null); 257 } 258 259 private DefaultCacheConfig(String[] cacheNames, String keyGenerator, String cacheManager, String cacheResolver) { 260 this.cacheNames = cacheNames; 261 this.keyGenerator = keyGenerator; 262 this.cacheManager = cacheManager; 263 this.cacheResolver = cacheResolver; 264 } 265 266 /** 267 * Apply the defaults to the specified {@link CacheOperation.Builder}. 268 * @param builder the operation builder to update 269 */ 270 public void applyDefault(CacheOperation.Builder builder) { 271 if (builder.getCacheNames().isEmpty() && this.cacheNames != null) { 272 builder.setCacheNames(this.cacheNames); 273 } 274 if (!StringUtils.hasText(builder.getKey()) && !StringUtils.hasText(builder.getKeyGenerator()) && 275 StringUtils.hasText(this.keyGenerator)) { 276 builder.setKeyGenerator(this.keyGenerator); 277 } 278 279 if (StringUtils.hasText(builder.getCacheManager()) || StringUtils.hasText(builder.getCacheResolver())) { 280 // One of these is set so we should not inherit anything 281 } 282 else if (StringUtils.hasText(this.cacheResolver)) { 283 builder.setCacheResolver(this.cacheResolver); 284 } 285 else if (StringUtils.hasText(this.cacheManager)) { 286 builder.setCacheManager(this.cacheManager); 287 } 288 } 289 } 290 291}