Skip to content

Commit ffc1f24

Browse files
committed
Introduce evictIfPresent/invalidate operations on Cache abstraction
@CacheEvict.beforeInvocation suggests immediate execution even in case of transactional caches. The cache interceptor delegates to the new evictIfPresent/invalidate operations now which imply immediate execution semantics (and also provide an indication for whether any corresponding entries where present when programmatically called). Closes gh-23192
1 parent 2c33c11 commit ffc1f24

File tree

15 files changed

+284
-85
lines changed

15 files changed

+284
-85
lines changed

spring-context-support/src/main/java/org/springframework/cache/caffeine/CaffeineCache.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -123,11 +123,23 @@ public void evict(Object key) {
123123
this.cache.invalidate(key);
124124
}
125125

126+
@Override
127+
public boolean evictIfPresent(Object key) {
128+
return (this.cache.asMap().remove(key) != null);
129+
}
130+
126131
@Override
127132
public void clear() {
128133
this.cache.invalidateAll();
129134
}
130135

136+
@Override
137+
public boolean invalidate() {
138+
boolean notEmpty = !this.cache.asMap().isEmpty();
139+
this.cache.invalidateAll();
140+
return notEmpty;
141+
}
142+
131143

132144
private class PutIfAbsentFunction implements Function<Object, Object> {
133145

spring-context-support/src/main/java/org/springframework/cache/ehcache/EhCacheCache.java

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -72,6 +72,19 @@ public ValueWrapper get(Object key) {
7272
return toValueWrapper(element);
7373
}
7474

75+
@SuppressWarnings("unchecked")
76+
@Override
77+
@Nullable
78+
public <T> T get(Object key, @Nullable Class<T> type) {
79+
Element element = this.cache.get(key);
80+
Object value = (element != null ? element.getObjectValue() : null);
81+
if (value != null && type != null && !type.isInstance(value)) {
82+
throw new IllegalStateException(
83+
"Cached value is not of required type [" + type.getName() + "]: " + value);
84+
}
85+
return (T) value;
86+
}
87+
7588
@SuppressWarnings("unchecked")
7689
@Override
7790
@Nullable
@@ -95,7 +108,6 @@ public <T> T get(Object key, Callable<T> valueLoader) {
95108
this.cache.releaseWriteLockOnKey(key);
96109
}
97110
}
98-
99111
}
100112

101113
private <T> T loadValue(Object key, Callable<T> valueLoader) {
@@ -110,19 +122,6 @@ private <T> T loadValue(Object key, Callable<T> valueLoader) {
110122
return value;
111123
}
112124

113-
@Override
114-
@SuppressWarnings("unchecked")
115-
@Nullable
116-
public <T> T get(Object key, @Nullable Class<T> type) {
117-
Element element = this.cache.get(key);
118-
Object value = (element != null ? element.getObjectValue() : null);
119-
if (value != null && type != null && !type.isInstance(value)) {
120-
throw new IllegalStateException(
121-
"Cached value is not of required type [" + type.getName() + "]: " + value);
122-
}
123-
return (T) value;
124-
}
125-
126125
@Override
127126
public void put(Object key, @Nullable Object value) {
128127
this.cache.put(new Element(key, value));
@@ -140,11 +139,23 @@ public void evict(Object key) {
140139
this.cache.remove(key);
141140
}
142141

142+
@Override
143+
public boolean evictIfPresent(Object key) {
144+
return this.cache.remove(key);
145+
}
146+
143147
@Override
144148
public void clear() {
145149
this.cache.removeAll();
146150
}
147151

152+
@Override
153+
public boolean invalidate() {
154+
boolean notEmpty = (this.cache.getSize() > 0);
155+
this.cache.removeAll();
156+
return notEmpty;
157+
}
158+
148159

149160
@Nullable
150161
private Element lookup(Object key) {

spring-context-support/src/main/java/org/springframework/cache/jcache/JCacheCache.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -105,11 +105,23 @@ public void evict(Object key) {
105105
this.cache.remove(key);
106106
}
107107

108+
@Override
109+
public boolean evictIfPresent(Object key) {
110+
return this.cache.remove(key);
111+
}
112+
108113
@Override
109114
public void clear() {
110115
this.cache.removeAll();
111116
}
112117

118+
@Override
119+
public boolean invalidate() {
120+
boolean notEmpty = this.cache.iterator().hasNext();
121+
this.cache.removeAll();
122+
return notEmpty;
123+
}
124+
113125

114126
private class ValueLoaderEntryProcessor<T> implements EntryProcessor<Object, Object, T> {
115127

spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/AbstractCacheInterceptor.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -59,7 +59,7 @@ protected abstract Object invoke(CacheOperationInvocationContext<O> context, Cac
5959
/**
6060
* Resolve the cache to use.
6161
* @param context the invocation context
62-
* @return the cache to use (never null)
62+
* @return the cache to use (never {@code null})
6363
*/
6464
protected Cache resolveCache(CacheOperationInvocationContext<O> context) {
6565
Collection<? extends Cache> caches = context.getOperation().getCacheResolver().resolveCaches(context);
@@ -73,7 +73,7 @@ protected Cache resolveCache(CacheOperationInvocationContext<O> context) {
7373
/**
7474
* Convert the collection of caches in a single expected element.
7575
* <p>Throw an {@link IllegalStateException} if the collection holds more than one element
76-
* @return the single element or {@code null} if the collection is empty
76+
* @return the single element, or {@code null} if the collection is empty
7777
*/
7878
@Nullable
7979
static Cache extractFrom(Collection<? extends Cache> caches) {

spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/CacheRemoveAllInterceptor.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -42,7 +42,6 @@ protected Object invoke(
4242
CacheOperationInvocationContext<CacheRemoveAllOperation> context, CacheOperationInvoker invoker) {
4343

4444
CacheRemoveAllOperation operation = context.getOperation();
45-
4645
boolean earlyRemove = operation.isEarlyRemove();
4746
if (earlyRemove) {
4847
removeAll(context);
@@ -67,10 +66,10 @@ protected Object invoke(
6766
protected void removeAll(CacheOperationInvocationContext<CacheRemoveAllOperation> context) {
6867
Cache cache = resolveCache(context);
6968
if (logger.isTraceEnabled()) {
70-
logger.trace("Invalidating entire cache '" + cache.getName() + "' for operation "
71-
+ context.getOperation());
69+
logger.trace("Invalidating entire cache '" + cache.getName() + "' for operation " +
70+
context.getOperation());
7271
}
73-
doClear(cache);
72+
doClear(cache, context.getOperation().isEarlyRemove());
7473
}
7574

7675
}

spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/CacheRemoveEntryInterceptor.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -42,7 +42,6 @@ protected Object invoke(
4242
CacheOperationInvocationContext<CacheRemoveOperation> context, CacheOperationInvoker invoker) {
4343

4444
CacheRemoveOperation operation = context.getOperation();
45-
4645
boolean earlyRemove = operation.isEarlyRemove();
4746
if (earlyRemove) {
4847
removeValue(context);
@@ -68,10 +67,10 @@ private void removeValue(CacheOperationInvocationContext<CacheRemoveOperation> c
6867
Object key = generateKey(context);
6968
Cache cache = resolveCache(context);
7069
if (logger.isTraceEnabled()) {
71-
logger.trace("Invalidating key [" + key + "] on cache '" + cache.getName()
72-
+ "' for operation " + context.getOperation());
70+
logger.trace("Invalidating key [" + key + "] on cache '" + cache.getName() +
71+
"' for operation " + context.getOperation());
7372
}
74-
doEvict(cache, key);
73+
doEvict(cache, key, context.getOperation().isEarlyRemove());
7574
}
7675

7776
}

spring-context-support/src/main/java/org/springframework/cache/jcache/interceptor/CacheRemoveOperation.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -50,8 +50,8 @@ public ExceptionTypeFilter getExceptionTypeFilter() {
5050
}
5151

5252
/**
53-
* Specify if the cache entry should be remove before invoking the method. By default, the
54-
* cache entry is removed after the method invocation.
53+
* Specify if the cache entry should be removed before invoking the method.
54+
* <p>By default, the cache entry is removed after the method invocation.
5555
* @see javax.cache.annotation.CacheRemove#afterInvocation()
5656
*/
5757
public boolean isEarlyRemove() {

spring-context-support/src/main/java/org/springframework/cache/transaction/TransactionAwareCacheDecorator.java

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,14 +25,16 @@
2525
import org.springframework.util.Assert;
2626

2727
/**
28-
* Cache decorator which synchronizes its {@link #put}, {@link #evict} and {@link #clear}
29-
* operations with Spring-managed transactions (through Spring's {@link TransactionSynchronizationManager},
30-
* performing the actual cache put/evict/clear operation only in the after-commit phase of a
31-
* successful transaction. If no transaction is active, {@link #put}, {@link #evict} and
28+
* Cache decorator which synchronizes its {@link #put}, {@link #evict} and
29+
* {@link #clear} operations with Spring-managed transactions (through Spring's
30+
* {@link TransactionSynchronizationManager}, performing the actual cache
31+
* put/evict/clear operation only in the after-commit phase of a successful
32+
* transaction. If no transaction is active, {@link #put}, {@link #evict} and
3233
* {@link #clear} operations will be performed immediately, as usual.
3334
*
34-
* <p>Use of more aggressive operations such as {@link #putIfAbsent} cannot be deferred
35-
* to the after-commit phase of a running transaction. Use these with care.
35+
* <p><b>Note:</b> Use of immediate operations such as {@link #putIfAbsent} and
36+
* {@link #evictIfPresent} cannot be deferred to the after-commit phase of a
37+
* running transaction. Use these with care in a transactional environment.
3638
*
3739
* @author Juergen Hoeller
3840
* @author Stephane Nicoll
@@ -54,6 +56,7 @@ public TransactionAwareCacheDecorator(Cache targetCache) {
5456
this.targetCache = targetCache;
5557
}
5658

59+
5760
/**
5861
* Return the target Cache that this Cache should delegate to.
5962
*/
@@ -124,6 +127,11 @@ public void afterCommit() {
124127
}
125128
}
126129

130+
@Override
131+
public boolean evictIfPresent(Object key) {
132+
return this.targetCache.evictIfPresent(key);
133+
}
134+
127135
@Override
128136
public void clear() {
129137
if (TransactionSynchronizationManager.isSynchronizationActive()) {
@@ -139,4 +147,9 @@ public void afterCommit() {
139147
}
140148
}
141149

150+
@Override
151+
public boolean invalidate() {
152+
return this.targetCache.invalidate();
153+
}
154+
142155
}

0 commit comments

Comments
 (0)