Skip to content

Commit f91f778

Browse files
author
Costin Leau
committed
+ align @CacheEvict behaviour with @Cacheable and @cACHEpUT
+ add flag for post method execution + add integration tests
1 parent b2bc753 commit f91f778

File tree

11 files changed

+263
-55
lines changed

11 files changed

+263
-55
lines changed

org.springframework.context/src/main/java/org/springframework/cache/annotation/CacheEvict.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,10 @@
6363
*/
6464
boolean allEntries() default false;
6565

66+
/**
67+
* Whether the eviction should occur after the method is successfully invoked (default)
68+
* or before. The latter causes the eviction to occur irrespective of the method outcome (whether
69+
* it threw an exception or not) while the former does not.
70+
*/
71+
boolean afterInvocation() default true;
6672
}

org.springframework.context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ CacheEvictOperation parseEvictAnnotation(AnnotatedElement ae, CacheEvict caching
8585
ceo.setCondition(caching.condition());
8686
ceo.setKey(caching.key());
8787
ceo.setCacheWide(caching.allEntries());
88+
ceo.setAfterInvocation(caching.afterInvocation());
8889
ceo.setName(ae.toString());
8990
return ceo;
9091
}

org.springframework.context/src/main/java/org/springframework/cache/config/CacheAdviceParser.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ private static class Props {
6868
}
6969
}
7070

71-
CacheOperation merge(Element element, ReaderContext readerCtx, CacheOperation op) {
71+
<T extends CacheOperation> T merge(Element element, ReaderContext readerCtx, T op) {
7272
String cache = element.getAttribute("cache");
7373
String k = element.getAttribute("key");
7474
String c = element.getAttribute("condition");
@@ -181,7 +181,17 @@ private RootBeanDefinition parseDefinitionSource(Element definition, ParserConte
181181
String name = prop.merge(opElement, parserContext.getReaderContext());
182182
TypedStringValue nameHolder = new TypedStringValue(name);
183183
nameHolder.setSource(parserContext.extractSource(opElement));
184-
CacheOperation op = prop.merge(opElement, parserContext.getReaderContext(), new CacheEvictOperation());
184+
CacheEvictOperation op = prop.merge(opElement, parserContext.getReaderContext(), new CacheEvictOperation());
185+
186+
String wide = opElement.getAttribute("all-entries");
187+
if (StringUtils.hasText(wide)) {
188+
op.setCacheWide(Boolean.valueOf(wide.trim()));
189+
}
190+
191+
String after = opElement.getAttribute("after-invocation");
192+
if (StringUtils.hasText(after)) {
193+
op.setAfterInvocation(Boolean.valueOf(after.trim()));
194+
}
185195

186196
Collection<CacheOperation> col = cacheOpMap.get(nameHolder);
187197
if (col == null) {

org.springframework.context/src/main/java/org/springframework/cache/interceptor/CacheAspectSupport.java

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,10 @@ protected Object execute(Invoker invoker, Object target, Method method, Object[]
189189

190190
// analyze caching information
191191
if (!CollectionUtils.isEmpty(cacheOp)) {
192-
Map<String, Collection<CacheOperationContext>> ops = createOperationContext(cacheOp, method, args, target,
193-
targetClass);
192+
Map<String, Collection<CacheOperationContext>> ops = createOperationContext(cacheOp, method, args, target, targetClass);
194193

195194
// start with evictions
196-
inspectCacheEvicts(ops.get(EVICT));
195+
inspectBeforeCacheEvicts(ops.get(EVICT));
197196

198197
// follow up with cacheable
199198
CacheStatus status = inspectCacheables(ops.get(CACHEABLE));
@@ -213,6 +212,8 @@ protected Object execute(Invoker invoker, Object target, Method method, Object[]
213212

214213
retVal = invoker.invoke();
215214

215+
inspectAfterCacheEvicts(ops.get(EVICT));
216+
216217
if (!updates.isEmpty()) {
217218
update(updates, retVal);
218219
}
@@ -223,42 +224,51 @@ protected Object execute(Invoker invoker, Object target, Method method, Object[]
223224
return invoker.invoke();
224225
}
225226

226-
private void inspectCacheEvicts(Collection<CacheOperationContext> evictions) {
227+
private void inspectBeforeCacheEvicts(Collection<CacheOperationContext> evictions) {
228+
inspectAfterCacheEvicts(evictions, false);
229+
}
230+
231+
private void inspectAfterCacheEvicts(Collection<CacheOperationContext> evictions) {
232+
inspectAfterCacheEvicts(evictions, true);
233+
}
234+
235+
private void inspectAfterCacheEvicts(Collection<CacheOperationContext> evictions, boolean afterInvocation) {
227236

228237
if (!evictions.isEmpty()) {
229238

230239
boolean log = logger.isTraceEnabled();
231240

232241
for (CacheOperationContext context : evictions) {
233-
if (context.isConditionPassing()) {
234-
CacheEvictOperation evictOp = (CacheEvictOperation) context.operation;
235-
236-
// for each cache
237-
// lazy key initialization
238-
Object key = null;
239-
240-
for (Cache cache : context.getCaches()) {
241-
// cache-wide flush
242-
if (evictOp.isCacheWide()) {
243-
cache.clear();
244-
if (log) {
245-
logger.trace("Invalidating entire cache for operation " + evictOp + " on method " + context.method);
246-
}
247-
} else {
248-
// check key
249-
if (key == null) {
250-
key = context.generateKey();
251-
}
252-
if (log) {
253-
logger.trace("Invalidating cache key " + key + " for operation " + evictOp + " on method " + context.method);
242+
CacheEvictOperation evictOp = (CacheEvictOperation) context.operation;
243+
244+
if (afterInvocation == evictOp.isAfterInvocation()) {
245+
if (context.isConditionPassing()) {
246+
// for each cache
247+
// lazy key initialization
248+
Object key = null;
249+
250+
for (Cache cache : context.getCaches()) {
251+
// cache-wide flush
252+
if (evictOp.isCacheWide()) {
253+
cache.clear();
254+
if (log) {
255+
logger.trace("Invalidating entire cache for operation " + evictOp + " on method " + context.method);
256+
}
257+
} else {
258+
// check key
259+
if (key == null) {
260+
key = context.generateKey();
261+
}
262+
if (log) {
263+
logger.trace("Invalidating cache key " + key + " for operation " + evictOp + " on method " + context.method);
264+
}
265+
cache.evict(key);
254266
}
255-
cache.evict(key);
256267
}
257-
}
258-
}
259-
else {
260-
if (log) {
261-
logger.trace("Cache condition failed on method " + context.method + " for operation " + context.operation);
268+
} else {
269+
if (log) {
270+
logger.trace("Cache condition failed on method " + context.method + " for operation " + context.operation);
271+
}
262272
}
263273
}
264274
}

org.springframework.context/src/main/java/org/springframework/cache/interceptor/CacheEvictOperation.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
public class CacheEvictOperation extends CacheOperation {
2626

2727
private boolean cacheWide = false;
28+
private boolean afterInvocation = true;
2829

2930
public void setCacheWide(boolean cacheWide) {
3031
this.cacheWide = cacheWide;
@@ -34,11 +35,21 @@ public boolean isCacheWide() {
3435
return this.cacheWide;
3536
}
3637

38+
public void setAfterInvocation(boolean afterInvocation) {
39+
this.afterInvocation = afterInvocation;
40+
}
41+
42+
public boolean isAfterInvocation() {
43+
return this.afterInvocation;
44+
}
45+
3746
@Override
3847
protected StringBuilder getOperationDescription() {
3948
StringBuilder sb = super.getOperationDescription();
4049
sb.append(",");
4150
sb.append(this.cacheWide);
51+
sb.append(",");
52+
sb.append(this.afterInvocation);
4253
return sb;
4354
}
4455
}

org.springframework.context/src/main/resources/org/springframework/cache/config/spring-cache-3.1.xsd

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,9 +211,17 @@
211211
<xsd:attribute name="all-entries" type="xsd:boolean" use="optional">
212212
<xsd:annotation>
213213
<xsd:documentation><![CDATA[
214-
The name of the backing cache.]]></xsd:documentation>
214+
Whether all the entries should be evicted.]]></xsd:documentation>
215215
</xsd:annotation>
216216
</xsd:attribute>
217+
<xsd:attribute name="after-invocation" type="xsd:boolean" use="optional">
218+
<xsd:annotation>
219+
<xsd:documentation><![CDATA[
220+
Whether the eviction should occur after the method is successfully
221+
invoked (default) or before.]]></xsd:documentation>
222+
</xsd:annotation>
223+
</xsd:attribute>
224+
217225
</xsd:extension>
218226
</xsd:complexContent>
219227
</xsd:complexType>

0 commit comments

Comments
 (0)