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
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -55,6 +55,7 @@ public interface Cache {
|
||||
* a cached {@code null} value. A straight {@code null} being
|
||||
* returned means that the cache contains no mapping for this key.
|
||||
* @see #get(Object, Class)
|
||||
* @see #get(Object, Callable)
|
||||
*/
|
||||
@Nullable
|
||||
ValueWrapper get(Object key);
|
||||
@@ -94,6 +95,7 @@ public interface Cache {
|
||||
* @return the value to which this cache maps the specified key
|
||||
* @throws ValueRetrievalException if the {@code valueLoader} throws an exception
|
||||
* @since 4.3
|
||||
* @see #get(Object)
|
||||
*/
|
||||
@Nullable
|
||||
<T> T get(Object key, Callable<T> valueLoader);
|
||||
@@ -102,8 +104,13 @@ public interface Cache {
|
||||
* Associate the specified value with the specified key in this cache.
|
||||
* <p>If the cache previously contained a mapping for this key, the old
|
||||
* value is replaced by the specified value.
|
||||
* <p>Actual registration may be performed in an asynchronous or deferred
|
||||
* fashion, with subsequent lookups possibly not seeing the entry yet.
|
||||
* This may for example be the case with transactional cache decorators.
|
||||
* Use {@link #putIfAbsent} for guaranteed immediate registration.
|
||||
* @param key the key with which the specified value is to be associated
|
||||
* @param value the value to be associated with the specified key
|
||||
* @see #putIfAbsent(Object, Object)
|
||||
*/
|
||||
void put(Object key, @Nullable Object value);
|
||||
|
||||
@@ -112,19 +119,19 @@ public interface Cache {
|
||||
* if it is not set already.
|
||||
* <p>This is equivalent to:
|
||||
* <pre><code>
|
||||
* Object existingValue = cache.get(key);
|
||||
* ValueWrapper existingValue = cache.get(key);
|
||||
* if (existingValue == null) {
|
||||
* cache.put(key, value);
|
||||
* return null;
|
||||
* } else {
|
||||
* return existingValue;
|
||||
* }
|
||||
* return existingValue;
|
||||
* </code></pre>
|
||||
* except that the action is performed atomically. While all out-of-the-box
|
||||
* {@link CacheManager} implementations are able to perform the put atomically,
|
||||
* the operation may also be implemented in two steps, e.g. with a check for
|
||||
* presence and a subsequent put, in a non-atomic way. Check the documentation
|
||||
* of the native cache implementation that you are using for more details.
|
||||
* <p>The default implementation delegates to {@link #get(Object)} and
|
||||
* {@link #put(Object, Object)} along the lines of the code snippet above.
|
||||
* @param key the key with which the specified value is to be associated
|
||||
* @param value the value to be associated with the specified key
|
||||
* @return the value to which this cache maps the specified key (which may be
|
||||
@@ -132,21 +139,73 @@ public interface Cache {
|
||||
* mapping for that key prior to this call. Returning {@code null} is therefore
|
||||
* an indicator that the given {@code value} has been associated with the key.
|
||||
* @since 4.1
|
||||
* @see #put(Object, Object)
|
||||
*/
|
||||
@Nullable
|
||||
ValueWrapper putIfAbsent(Object key, @Nullable Object value);
|
||||
default ValueWrapper putIfAbsent(Object key, @Nullable Object value) {
|
||||
ValueWrapper existingValue = get(key);
|
||||
if (existingValue == null) {
|
||||
put(key, value);
|
||||
}
|
||||
return existingValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evict the mapping for this key from this cache if it is present.
|
||||
* <p>Actual eviction may be performed in an asynchronous or deferred
|
||||
* fashion, with subsequent lookups possibly still seeing the entry.
|
||||
* This may for example be the case with transactional cache decorators.
|
||||
* Use {@link #evictIfPresent} for guaranteed immediate removal.
|
||||
* @param key the key whose mapping is to be removed from the cache
|
||||
* @see #evictIfPresent(Object)
|
||||
*/
|
||||
void evict(Object key);
|
||||
|
||||
/**
|
||||
* Remove all mappings from the cache.
|
||||
* Evict the mapping for this key from this cache if it is present,
|
||||
* expecting the key to be immediately invisible for subsequent lookups.
|
||||
* <p>The default implementation delegates to {@link #evict(Object)},
|
||||
* returning {@code false} for not-determined prior presence of the key.
|
||||
* Cache providers and in particular cache decorators are encouraged
|
||||
* to perform immediate eviction if possible (e.g. in case of generally
|
||||
* deferred cache operations within a transaction) and to reliably
|
||||
* determine prior presence of the given key.
|
||||
* @param key the key whose mapping is to be removed from the cache
|
||||
* @return {@code true} if the cache was known to have a mapping for
|
||||
* this key before, {@code false} if it did not (or if prior presence
|
||||
* could not be determined)
|
||||
* @since 5.2
|
||||
* @see #evict(Object)
|
||||
*/
|
||||
default boolean evictIfPresent(Object key) {
|
||||
evict(key);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cache through removing all mappings.
|
||||
* <p>Actual clearing may be performed in an asynchronous or deferred
|
||||
* fashion, with subsequent lookups possibly still seeing the entries.
|
||||
* This may for example be the case with transactional cache decorators.
|
||||
* Use {@link #invalidate()} for guaranteed immediate removal of entries.
|
||||
* @see #invalidate()
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Invalidate the cache through removing all mappings, expecting all
|
||||
* entries to be immediately invisible for subsequent lookups.
|
||||
* @return {@code true} if the cache was known to have mappings before,
|
||||
* {@code false} if it did not (or if prior presence of entries could
|
||||
* not be determined)
|
||||
* @since 5.2
|
||||
* @see #clear()
|
||||
*/
|
||||
default boolean invalidate() {
|
||||
clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A (wrapper) object representing a cache value.
|
||||
|
||||
@@ -142,7 +142,7 @@ public @interface CacheEvict {
|
||||
* occur irrespective of the method outcome (i.e., whether it threw an
|
||||
* exception or not).
|
||||
* <p>Defaults to {@code false}, meaning that the cache eviction operation
|
||||
* will occur <em>after</em> the advised method is invoked successfully (i.e.,
|
||||
* will occur <em>after</em> the advised method is invoked successfully (i.e.
|
||||
* only if the invocation did not throw an exception).
|
||||
*/
|
||||
boolean beforeInvocation() default false;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -141,7 +141,7 @@ public class ConcurrentMapCache extends AbstractValueAdaptingCache {
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> T get(Object key, Callable<T> valueLoader) {
|
||||
return (T) fromStoreValue(this.store.computeIfAbsent(key, r -> {
|
||||
return (T) fromStoreValue(this.store.computeIfAbsent(key, k -> {
|
||||
try {
|
||||
return toStoreValue(valueLoader.call());
|
||||
}
|
||||
@@ -168,11 +168,23 @@ public class ConcurrentMapCache extends AbstractValueAdaptingCache {
|
||||
this.store.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evictIfPresent(Object key) {
|
||||
return (this.store.remove(key) != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.store.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean invalidate() {
|
||||
boolean notEmpty = !this.store.isEmpty();
|
||||
this.store.clear();
|
||||
return notEmpty;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object toStoreValue(@Nullable Object userValue) {
|
||||
Object storeValue = super.toStoreValue(userValue);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -92,12 +92,17 @@ public abstract class AbstractCacheInvoker {
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute {@link Cache#evict(Object)} on the specified {@link Cache} and
|
||||
* invoke the error handler if an exception occurs.
|
||||
* Execute {@link Cache#evict(Object)}/{@link Cache#evictIfPresent(Object)} on the
|
||||
* specified {@link Cache} and invoke the error handler if an exception occurs.
|
||||
*/
|
||||
protected void doEvict(Cache cache, Object key) {
|
||||
protected void doEvict(Cache cache, Object key, boolean immediate) {
|
||||
try {
|
||||
cache.evict(key);
|
||||
if (immediate) {
|
||||
cache.evictIfPresent(key);
|
||||
}
|
||||
else {
|
||||
cache.evict(key);
|
||||
}
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
getErrorHandler().handleCacheEvictError(ex, cache, key);
|
||||
@@ -108,9 +113,14 @@ public abstract class AbstractCacheInvoker {
|
||||
* Execute {@link Cache#clear()} on the specified {@link Cache} and
|
||||
* invoke the error handler if an exception occurs.
|
||||
*/
|
||||
protected void doClear(Cache cache) {
|
||||
protected void doClear(Cache cache, boolean immediate) {
|
||||
try {
|
||||
cache.clear();
|
||||
if (immediate) {
|
||||
cache.invalidate();
|
||||
}
|
||||
else {
|
||||
cache.clear();
|
||||
}
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
getErrorHandler().handleCacheClearError(ex, cache);
|
||||
|
||||
@@ -485,14 +485,14 @@ public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
for (Cache cache : context.getCaches()) {
|
||||
if (operation.isCacheWide()) {
|
||||
logInvalidating(context, operation, null);
|
||||
doClear(cache);
|
||||
doClear(cache, operation.isBeforeInvocation());
|
||||
}
|
||||
else {
|
||||
if (key == null) {
|
||||
key = generateKey(context, result);
|
||||
}
|
||||
logInvalidating(context, operation, key);
|
||||
doEvict(cache, key);
|
||||
doEvict(cache, key, operation.isBeforeInvocation());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -93,8 +93,18 @@ public class NoOpCache implements Cache {
|
||||
public void evict(Object key) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evictIfPresent(Object key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean invalidate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user