Cache provider related exceptions handling
This commit adds the necessary infrastructure to handle exceptions thrown by a cache provider in both Spring's and JCache's caching abstractions. Both interceptors can be configured with a CacheErrorHandler that defines several callbacks on typical cache operations. In particular, handleCacheGetError can be implemented in such a way that an exception thrown by the provider is handled as a cache miss by the caching abstraction. The handler can be configured with both CachingConfigurer and the XML namespace (error-handler property) Issue: SPR-9275
This commit is contained in:
parent
c7d1c49d6d
commit
05e96ee448
|
|
@ -55,6 +55,9 @@ public class ProxyJCacheConfiguration extends AbstractJCacheConfiguration {
|
|||
public JCacheInterceptor cacheInterceptor() {
|
||||
JCacheInterceptor interceptor = new JCacheInterceptor();
|
||||
interceptor.setCacheOperationSource(cacheOperationSource());
|
||||
if (this.errorHandler != null) {
|
||||
interceptor.setErrorHandler(this.errorHandler);
|
||||
}
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ import org.apache.commons.logging.Log;
|
|||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.interceptor.AbstractCacheInvoker;
|
||||
import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
import org.springframework.cache.jcache.model.BaseCacheOperation;
|
||||
|
|
@ -36,10 +38,14 @@ import org.springframework.cache.jcache.model.BaseCacheOperation;
|
|||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public abstract class AbstractCacheInterceptor<O extends BaseCacheOperation<A>, A extends Annotation>
|
||||
implements Serializable {
|
||||
extends AbstractCacheInvoker implements Serializable {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
protected AbstractCacheInterceptor(CacheErrorHandler errorHandler) {
|
||||
super(errorHandler);
|
||||
}
|
||||
|
||||
protected abstract Object invoke(CacheOperationInvocationContext<O> context,
|
||||
CacheOperationInvoker invoker) throws Throwable;
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import java.lang.annotation.Annotation;
|
|||
|
||||
import javax.cache.annotation.CacheKeyInvocationContext;
|
||||
|
||||
import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.cache.jcache.model.BaseKeyCacheOperation;
|
||||
|
|
@ -34,6 +35,10 @@ import org.springframework.cache.jcache.model.BaseKeyCacheOperation;
|
|||
public abstract class AbstractKeyCacheInterceptor<O extends BaseKeyCacheOperation<A>, A extends Annotation>
|
||||
extends AbstractCacheInterceptor<O, A> {
|
||||
|
||||
protected AbstractKeyCacheInterceptor(CacheErrorHandler errorHandler) {
|
||||
super(errorHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a key for the specified invocation.
|
||||
* @param context the context of the invocation
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import javax.cache.annotation.CacheKeyInvocationContext;
|
|||
import javax.cache.annotation.CachePut;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
import org.springframework.cache.jcache.model.CachePutOperation;
|
||||
|
|
@ -33,6 +34,10 @@ import org.springframework.cache.jcache.model.CachePutOperation;
|
|||
@SuppressWarnings("serial")
|
||||
public class CachePutInterceptor extends AbstractKeyCacheInterceptor<CachePutOperation, CachePut> {
|
||||
|
||||
public CachePutInterceptor(CacheErrorHandler errorHandler) {
|
||||
super(errorHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object invoke(CacheOperationInvocationContext<CachePutOperation> context,
|
||||
CacheOperationInvoker invoker) {
|
||||
|
|
@ -65,7 +70,7 @@ public class CachePutInterceptor extends AbstractKeyCacheInterceptor<CachePutOpe
|
|||
protected void cacheValue(CacheOperationInvocationContext<CachePutOperation> context, Object value) {
|
||||
Object key = generateKey(context);
|
||||
Cache cache = resolveCache(context);
|
||||
cache.put(key, value);
|
||||
doPut(cache, key, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package org.springframework.cache.jcache.interceptor;
|
|||
import javax.cache.annotation.CacheRemoveAll;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
import org.springframework.cache.jcache.model.CacheRemoveAllOperation;
|
||||
|
|
@ -33,6 +34,10 @@ import org.springframework.cache.jcache.model.CacheRemoveAllOperation;
|
|||
public class CacheRemoveAllInterceptor
|
||||
extends AbstractCacheInterceptor<CacheRemoveAllOperation, CacheRemoveAll> {
|
||||
|
||||
protected CacheRemoveAllInterceptor(CacheErrorHandler errorHandler) {
|
||||
super(errorHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object invoke(CacheOperationInvocationContext<CacheRemoveAllOperation> context,
|
||||
CacheOperationInvoker invoker) {
|
||||
|
|
@ -66,7 +71,7 @@ public class CacheRemoveAllInterceptor
|
|||
logger.trace("Invalidating entire cache '" + cache.getName() + "' for operation "
|
||||
+ context.getOperation());
|
||||
}
|
||||
cache.clear();
|
||||
doClear(cache);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package org.springframework.cache.jcache.interceptor;
|
|||
import javax.cache.annotation.CacheRemove;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
import org.springframework.cache.jcache.model.CacheRemoveOperation;
|
||||
|
|
@ -32,6 +33,10 @@ import org.springframework.cache.jcache.model.CacheRemoveOperation;
|
|||
@SuppressWarnings("serial")
|
||||
public class CacheRemoveEntryInterceptor extends AbstractKeyCacheInterceptor<CacheRemoveOperation, CacheRemove> {
|
||||
|
||||
protected CacheRemoveEntryInterceptor(CacheErrorHandler errorHandler) {
|
||||
super(errorHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object invoke(CacheOperationInvocationContext<CacheRemoveOperation> context,
|
||||
CacheOperationInvoker invoker) {
|
||||
|
|
@ -66,7 +71,7 @@ public class CacheRemoveEntryInterceptor extends AbstractKeyCacheInterceptor<Cac
|
|||
logger.trace("Invalidating key [" + key + "] on cache '" + cache.getName()
|
||||
+ "' for operation " + context.getOperation());
|
||||
}
|
||||
cache.evict(key);
|
||||
doEvict(cache, key);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package org.springframework.cache.jcache.interceptor;
|
|||
import javax.cache.annotation.CacheResult;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
|
|
@ -35,6 +36,10 @@ import org.springframework.util.filter.ExceptionTypeFilter;
|
|||
@SuppressWarnings("serial")
|
||||
public class CacheResultInterceptor extends AbstractKeyCacheInterceptor<CacheResultOperation, CacheResult> {
|
||||
|
||||
public CacheResultInterceptor(CacheErrorHandler errorHandler) {
|
||||
super(errorHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object invoke(CacheOperationInvocationContext<CacheResultOperation> context,
|
||||
CacheOperationInvoker invoker) {
|
||||
|
|
@ -46,7 +51,7 @@ public class CacheResultInterceptor extends AbstractKeyCacheInterceptor<CacheRes
|
|||
Cache exceptionCache = resolveExceptionCache(context);
|
||||
|
||||
if (!operation.isAlwaysInvoked()) {
|
||||
Cache.ValueWrapper cachedValue = cache.get(cacheKey);
|
||||
Cache.ValueWrapper cachedValue = doGet(cache, cacheKey);
|
||||
if (cachedValue != null) {
|
||||
return cachedValue.get();
|
||||
}
|
||||
|
|
@ -74,7 +79,7 @@ public class CacheResultInterceptor extends AbstractKeyCacheInterceptor<CacheRes
|
|||
if (exceptionCache == null) {
|
||||
return;
|
||||
}
|
||||
Cache.ValueWrapper result = exceptionCache.get(cacheKey);
|
||||
Cache.ValueWrapper result = doGet(exceptionCache, cacheKey);
|
||||
if (result != null) {
|
||||
throw rewriteCallStack((Throwable) result.get(), getClass().getName(), "invoke");
|
||||
}
|
||||
|
|
@ -111,7 +116,6 @@ public class CacheResultInterceptor extends AbstractKeyCacheInterceptor<CacheRes
|
|||
* @param exception the exception to merge with the current call stack
|
||||
* @param className the class name of the common ancestor
|
||||
* @param methodName the method name of the common ancestor
|
||||
* @param <T> the type of the exception
|
||||
* @return a clone exception with a rewritten call stack composed of the current
|
||||
* call stack up to (included) the common ancestor specified by the {@code className} and
|
||||
* {@code methodName} arguments, followed by stack trace elements of the specified
|
||||
|
|
|
|||
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright 2002-2014 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.jcache.interceptor;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
|
@ -8,6 +24,7 @@ import org.apache.commons.logging.LogFactory;
|
|||
|
||||
import org.springframework.aop.framework.AopProxyUtils;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.cache.interceptor.AbstractCacheInvoker;
|
||||
import org.springframework.cache.interceptor.BasicCacheOperation;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
|
|
@ -36,7 +53,7 @@ import org.springframework.util.Assert;
|
|||
* @see KeyGeneratorAdapter
|
||||
* @see CacheResolverAdapter
|
||||
*/
|
||||
public class JCacheAspectSupport implements InitializingBean {
|
||||
public class JCacheAspectSupport extends AbstractCacheInvoker implements InitializingBean {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
|
|
@ -44,13 +61,13 @@ public class JCacheAspectSupport implements InitializingBean {
|
|||
|
||||
private boolean initialized = false;
|
||||
|
||||
private final CacheResultInterceptor cacheResultInterceptor = new CacheResultInterceptor();
|
||||
private CacheResultInterceptor cacheResultInterceptor;
|
||||
|
||||
private final CachePutInterceptor cachePutInterceptor = new CachePutInterceptor();
|
||||
private CachePutInterceptor cachePutInterceptor;
|
||||
|
||||
private final CacheRemoveEntryInterceptor cacheRemoveEntryInterceptor = new CacheRemoveEntryInterceptor();
|
||||
private CacheRemoveEntryInterceptor cacheRemoveEntryInterceptor;
|
||||
|
||||
private final CacheRemoveAllInterceptor cacheRemoveAllInterceptor = new CacheRemoveAllInterceptor();
|
||||
private CacheRemoveAllInterceptor cacheRemoveAllInterceptor;
|
||||
|
||||
public void setCacheOperationSource(JCacheOperationSource cacheOperationSource) {
|
||||
Assert.notNull(cacheOperationSource);
|
||||
|
|
@ -67,6 +84,13 @@ public class JCacheAspectSupport implements InitializingBean {
|
|||
public void afterPropertiesSet() {
|
||||
Assert.state(this.cacheOperationSource != null, "The 'cacheOperationSource' property is required: " +
|
||||
"If there are no cacheable methods, then don't use a cache aspect.");
|
||||
Assert.state(this.getErrorHandler() != null, "The 'errorHandler' is required.");
|
||||
|
||||
this.cacheResultInterceptor = new CacheResultInterceptor(getErrorHandler());
|
||||
this.cachePutInterceptor = new CachePutInterceptor(getErrorHandler());
|
||||
this.cacheRemoveEntryInterceptor = new CacheRemoveEntryInterceptor(getErrorHandler());
|
||||
this.cacheRemoveAllInterceptor = new CacheRemoveAllInterceptor(getErrorHandler());
|
||||
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ public abstract class AbstractJCacheAnnotationTests {
|
|||
@Rule
|
||||
public final TestName name = new TestName();
|
||||
|
||||
protected ApplicationContext ctx;
|
||||
|
||||
private JCacheableService<?> service;
|
||||
|
||||
private CacheManager cacheManager;
|
||||
|
|
@ -51,9 +53,9 @@ public abstract class AbstractJCacheAnnotationTests {
|
|||
|
||||
@Before
|
||||
public void setUp() {
|
||||
ApplicationContext context = getApplicationContext();
|
||||
service = context.getBean(JCacheableService.class);
|
||||
cacheManager = context.getBean("cacheManager", CacheManager.class);
|
||||
ctx = getApplicationContext();
|
||||
service = ctx.getBean(JCacheableService.class);
|
||||
cacheManager = ctx.getBean("cacheManager", CacheManager.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -27,13 +27,16 @@ import org.springframework.cache.CacheManager;
|
|||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCache;
|
||||
import org.springframework.cache.config.SomeKeyGenerator;
|
||||
import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.cache.interceptor.NamedCacheResolver;
|
||||
import org.springframework.cache.interceptor.SimpleCacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.SimpleCacheResolver;
|
||||
import org.springframework.cache.interceptor.SimpleKeyGenerator;
|
||||
import org.springframework.cache.jcache.interceptor.AnnotatedJCacheableService;
|
||||
import org.springframework.cache.jcache.interceptor.DefaultJCacheOperationSource;
|
||||
import org.springframework.cache.jcache.interceptor.JCacheInterceptor;
|
||||
import org.springframework.cache.jcache.interceptor.SimpleExceptionCacheResolver;
|
||||
import org.springframework.cache.support.NoOpCacheManager;
|
||||
import org.springframework.cache.support.SimpleCacheManager;
|
||||
|
|
@ -63,6 +66,8 @@ public class JCacheJavaConfigTests extends AbstractJCacheAnnotationTests {
|
|||
cos.getDefaultCacheResolver());
|
||||
assertSame(context.getBean("exceptionCacheResolver", CacheResolver.class),
|
||||
cos.getDefaultExceptionCacheResolver());
|
||||
JCacheInterceptor interceptor = context.getBean(JCacheInterceptor.class);
|
||||
assertSame(context.getBean("errorHandler", CacheErrorHandler.class), interceptor.getErrorHandler());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -138,6 +143,12 @@ public class JCacheJavaConfigTests extends AbstractJCacheAnnotationTests {
|
|||
return new SimpleKeyGenerator();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public CacheErrorHandler errorHandler() {
|
||||
return new SimpleCacheErrorHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public CacheResolver cacheResolver() {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,12 @@
|
|||
|
||||
package org.springframework.cache.jcache.config;
|
||||
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.jcache.interceptor.JCacheInterceptor;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.GenericXmlApplicationContext;
|
||||
|
||||
|
|
@ -30,4 +36,10 @@ public class JCacheNamespaceDrivenTests extends AbstractJCacheAnnotationTests {
|
|||
"/org/springframework/cache/jcache/config/jCacheNamespaceDriven.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCacheErrorHandler() {
|
||||
JCacheInterceptor ci = ctx.getBean(JCacheInterceptor.class);
|
||||
assertSame(ctx.getBean("errorHandler", CacheErrorHandler.class), ci.getErrorHandler());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* Copyright 2002-2014 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.jcache.interceptor;
|
||||
|
||||
import static org.mockito.BDDMockito.*;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import javax.cache.annotation.CacheDefaults;
|
||||
import javax.cache.annotation.CachePut;
|
||||
import javax.cache.annotation.CacheRemove;
|
||||
import javax.cache.annotation.CacheRemoveAll;
|
||||
import javax.cache.annotation.CacheResult;
|
||||
import javax.cache.annotation.CacheValue;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.support.SimpleCacheManager;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class JCacheErrorHandlerTests {
|
||||
|
||||
@Rule
|
||||
public final ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private Cache cache;
|
||||
|
||||
private CacheErrorHandler errorHandler;
|
||||
|
||||
private SimpleService simpleService;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
|
||||
this.cache = context.getBean("mockCache", Cache.class);
|
||||
this.errorHandler = context.getBean(CacheErrorHandler.class);
|
||||
this.simpleService = context.getBean(SimpleService.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFail() {
|
||||
UnsupportedOperationException exception = new UnsupportedOperationException("Test exception on get");
|
||||
SimpleGeneratedCacheKey key = new SimpleGeneratedCacheKey(0L);
|
||||
doThrow(exception).when(cache).get(key);
|
||||
|
||||
this.simpleService.get(0L);
|
||||
verify(errorHandler).handleCacheGetError(exception, cache, key);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void putFail() {
|
||||
UnsupportedOperationException exception = new UnsupportedOperationException("Test exception on put");
|
||||
SimpleGeneratedCacheKey key = new SimpleGeneratedCacheKey(0L);
|
||||
doThrow(exception).when(cache).put(key, 234L);
|
||||
|
||||
this.simpleService.put(0L, 234L);
|
||||
verify(errorHandler).handleCachePutError(exception, cache, key, 234L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void evictFail() {
|
||||
UnsupportedOperationException exception = new UnsupportedOperationException("Test exception on evict");
|
||||
SimpleGeneratedCacheKey key = new SimpleGeneratedCacheKey(0L);
|
||||
doThrow(exception).when(cache).evict(key);
|
||||
|
||||
this.simpleService.evict(0L);
|
||||
verify(errorHandler).handleCacheEvictError(exception, cache, key);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearFail() {
|
||||
UnsupportedOperationException exception = new UnsupportedOperationException("Test exception on evict");
|
||||
doThrow(exception).when(cache).clear();
|
||||
|
||||
this.simpleService.clear();
|
||||
verify(errorHandler).handleCacheClearError(exception, cache);
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
static class Config {
|
||||
|
||||
@Bean(name = "jCacheInterceptor")
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public JCacheInterceptor cacheInterceptor() {
|
||||
JCacheInterceptor interceptor = new JCacheInterceptor();
|
||||
interceptor.setCacheOperationSource(cacheOperationSource());
|
||||
interceptor.setErrorHandler(errorHandler());
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CacheErrorHandler errorHandler() {
|
||||
return mock(CacheErrorHandler.class);
|
||||
}
|
||||
|
||||
@Bean(name = "jCacheOperationSource")
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public JCacheOperationSource cacheOperationSource() {
|
||||
DefaultJCacheOperationSource source = new DefaultJCacheOperationSource();
|
||||
source.setCacheManager(cacheManager());
|
||||
return source;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SimpleService simpleService() {
|
||||
return new SimpleService();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CacheManager cacheManager() {
|
||||
SimpleCacheManager cacheManager = new SimpleCacheManager();
|
||||
cacheManager.setCaches(Arrays.asList(mockCache()));
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Cache mockCache() {
|
||||
Cache cache = mock(Cache.class);
|
||||
given(cache.getName()).willReturn("test");
|
||||
return cache;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@CacheDefaults(cacheName = "test")
|
||||
public static class SimpleService {
|
||||
private AtomicLong counter = new AtomicLong();
|
||||
|
||||
@CacheResult
|
||||
public Object get(long id) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@CachePut
|
||||
public void put(long id, @CacheValue Object object) {
|
||||
}
|
||||
|
||||
@CacheRemove
|
||||
public void evict(long id) {
|
||||
}
|
||||
|
||||
@CacheRemoveAll
|
||||
public void clear() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
http://www.springframework.org/schema/cache
|
||||
http://www.springframework.org/schema/cache/spring-cache.xsd">
|
||||
|
||||
<cache:annotation-driven proxy-target-class="false" order="0"/>
|
||||
<cache:annotation-driven proxy-target-class="false" order="0" error-handler="errorHandler"/>
|
||||
|
||||
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
|
||||
<property name="caches">
|
||||
|
|
@ -26,6 +26,8 @@
|
|||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="errorHandler" class="org.springframework.cache.interceptor.SimpleCacheErrorHandler"/>
|
||||
|
||||
<bean id="defaultCache"
|
||||
class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
|
||||
<property name="name" value="default"/>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import javax.annotation.PostConstruct;
|
|||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
|
@ -51,6 +52,8 @@ public abstract class AbstractCachingConfiguration<C extends CachingConfigurer>
|
|||
|
||||
protected KeyGenerator keyGenerator;
|
||||
|
||||
protected CacheErrorHandler errorHandler;
|
||||
|
||||
@Autowired(required=false)
|
||||
private Collection<CacheManager> cacheManagerBeans;
|
||||
|
||||
|
|
@ -115,6 +118,7 @@ public abstract class AbstractCachingConfiguration<C extends CachingConfigurer>
|
|||
this.cacheManager = config.cacheManager();
|
||||
this.cacheResolver = config.cacheResolver();
|
||||
this.keyGenerator = config.keyGenerator();
|
||||
this.errorHandler = config.errorHandler();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.cache.annotation;
|
||||
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
|
||||
|
|
@ -104,4 +105,26 @@ public interface CachingConfigurer {
|
|||
*/
|
||||
KeyGenerator keyGenerator();
|
||||
|
||||
/**
|
||||
* Return the {@link CacheErrorHandler} to use to handle cache-related errors.
|
||||
* <p>By default,{@link org.springframework.cache.interceptor.SimpleCacheErrorHandler}
|
||||
* is used and simply throws the exception back at the client.
|
||||
* <p>Implementations must explicitly declare
|
||||
* {@link org.springframework.context.annotation.Bean @Bean}, e.g.
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableCaching
|
||||
* public class AppConfig extends CachingConfigurerSupport {
|
||||
* @Bean // important!
|
||||
* @Override
|
||||
* public CacheErrorHandler errorHandler() {
|
||||
* // configure and return CacheErrorHandler instance
|
||||
* }
|
||||
* // ...
|
||||
* }
|
||||
* </pre>
|
||||
* See @{@link EnableCaching} for more complete examples.
|
||||
*/
|
||||
CacheErrorHandler errorHandler();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.cache.annotation;
|
||||
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
|
||||
|
|
@ -45,4 +46,8 @@ public class CachingConfigurerSupport implements CachingConfigurer {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheErrorHandler errorHandler() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,11 +91,11 @@ import org.springframework.core.Ordered;
|
|||
* <p>For those that wish to establish a more direct relationship between
|
||||
* {@code @EnableCaching} and the exact cache manager bean to be used,
|
||||
* the {@link CachingConfigurer} callback interface may be implemented - notice the
|
||||
* {@code implements} clause and the {@code @Override}-annotated methods below:
|
||||
* the {@code @Override}-annotated methods below:
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableCaching
|
||||
* public class AppConfig implements CachingConfigurer {
|
||||
* public class AppConfig extends CachingConfigurerSupport {
|
||||
* @Bean
|
||||
* public MyService myService() {
|
||||
* // configure and return a class having @Cacheable methods
|
||||
|
|
@ -128,9 +128,14 @@ import org.springframework.core.Ordered;
|
|||
* {@code @EnableCaching} will configure Spring's
|
||||
* {@link org.springframework.cache.interceptor.SimpleKeyGenerator SimpleKeyGenerator}
|
||||
* for this purpose, but when implementing {@code CachingConfigurer}, a key generator
|
||||
* must be provided explicitly. Return {@code new SimpleKeyGenerator()} from this method
|
||||
* if no customization is necessary. See {@link CachingConfigurer} Javadoc for further
|
||||
* details.
|
||||
* must be provided explicitly. Return {@code null} or {@code new SimpleKeyGenerator()}
|
||||
* from this method if no customization is necessary.
|
||||
*
|
||||
* <p>{@link CachingConfigurer} offers additional customization options: it is recommended
|
||||
* to extend from {@link org.springframework.cache.annotation.CachingConfigurerSupport
|
||||
* CachingConfigurerSupport} that provides a default implementation for all methods which
|
||||
* can be useful if you do not need to customize everything. See {@link CachingConfigurer}
|
||||
* Javadoc for further details.
|
||||
*
|
||||
* <p>The {@link #mode()} attribute controls how advice is applied; if the mode is
|
||||
* {@link AdviceMode#PROXY} (the default), then the other attributes such as
|
||||
|
|
|
|||
|
|
@ -68,6 +68,9 @@ public class ProxyCachingConfiguration extends AbstractCachingConfiguration<Cach
|
|||
if (this.keyGenerator != null) {
|
||||
interceptor.setKeyGenerator(this.keyGenerator);
|
||||
}
|
||||
if (this.errorHandler != null) {
|
||||
interceptor.setErrorHandler(this.errorHandler);
|
||||
}
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import org.springframework.cache.annotation.AnnotationCacheOperationSource;
|
|||
import org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvisor;
|
||||
import org.springframework.cache.interceptor.CacheInterceptor;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser}
|
||||
|
|
@ -102,6 +103,13 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser
|
|||
new RuntimeBeanReference(CacheNamespaceHandler.extractCacheManager(element)));
|
||||
}
|
||||
|
||||
private static BeanDefinition parseErrorHandler(Element element, BeanDefinition def) {
|
||||
String name = element.getAttribute("error-handler");
|
||||
if (StringUtils.hasText(name)) {
|
||||
def.getPropertyValues().add("errorHandler", new RuntimeBeanReference(name.trim()));
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the necessary infrastructure to support the Spring's caching annotations.
|
||||
|
|
@ -123,6 +131,7 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser
|
|||
interceptorDef.setSource(eleSource);
|
||||
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
parseCacheManagerProperty(element, interceptorDef);
|
||||
parseErrorHandler(element, interceptorDef);
|
||||
CacheNamespaceHandler.parseKeyGenerator(element, interceptorDef);
|
||||
interceptorDef.getPropertyValues().add("cacheOperationSources", new RuntimeBeanReference(sourceName));
|
||||
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
|
||||
|
|
@ -186,6 +195,7 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser
|
|||
interceptorDef.setSource(eleSource);
|
||||
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
interceptorDef.getPropertyValues().add("cacheOperationSource", new RuntimeBeanReference(sourceName));
|
||||
parseErrorHandler(element, interceptorDef);
|
||||
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
|
||||
|
||||
// Create the CacheAdvisor definition.
|
||||
|
|
|
|||
115
spring-context/src/main/java/org/springframework/cache/interceptor/AbstractCacheInvoker.java
vendored
Normal file
115
spring-context/src/main/java/org/springframework/cache/interceptor/AbstractCacheInvoker.java
vendored
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Copyright 2002-2014 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.interceptor;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A base component for invoking {@link Cache} operations and using a
|
||||
* configurable {@link CacheErrorHandler} when an exception occurs.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see org.springframework.cache.interceptor.CacheErrorHandler
|
||||
*/
|
||||
public abstract class AbstractCacheInvoker {
|
||||
|
||||
private CacheErrorHandler errorHandler;
|
||||
|
||||
protected AbstractCacheInvoker(CacheErrorHandler errorHandler) {
|
||||
Assert.notNull("ErrorHandler must not be null");
|
||||
this.errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
protected AbstractCacheInvoker() {
|
||||
this(new SimpleCacheErrorHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link CacheErrorHandler} instance to use to handle errors
|
||||
* thrown by the cache provider. By default, a {@link SimpleCacheErrorHandler}
|
||||
* is used who throws any exception as is.
|
||||
*/
|
||||
public void setErrorHandler(CacheErrorHandler errorHandler) {
|
||||
this.errorHandler = errorHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link CacheErrorHandler} to use.
|
||||
*/
|
||||
public CacheErrorHandler getErrorHandler() {
|
||||
return this.errorHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute {@link Cache#get(Object)} on the specified {@link Cache} and
|
||||
* invoke the error handler if an exception occurs. Return {@code null}
|
||||
* if the handler does not throw any exception, which simulates a cache
|
||||
* miss in case of error.
|
||||
* @see Cache#get(Object)
|
||||
*/
|
||||
protected Cache.ValueWrapper doGet(Cache cache, Object key) {
|
||||
try {
|
||||
return cache.get(key);
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
getErrorHandler().handleCacheGetError(e, cache, key);
|
||||
return null; // If the exception is handled, return a cache miss
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute {@link Cache#put(Object, Object)} on the specified {@link Cache}
|
||||
* and invoke the error handler if an exception occurs.
|
||||
*/
|
||||
protected void doPut(Cache cache, Object key, Object result) {
|
||||
try {
|
||||
cache.put(key, result);
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
getErrorHandler().handleCachePutError(e, cache, key, result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute {@link Cache#evict(Object)} on the specified {@link Cache} and
|
||||
* invoke the error handler if an exception occurs.
|
||||
*/
|
||||
protected void doEvict(Cache cache, Object key) {
|
||||
try {
|
||||
cache.evict(key);
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
getErrorHandler().handleCacheEvictError(e, cache, key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute {@link Cache#clear()} on the specified {@link Cache} and
|
||||
* invoke the error handler if an exception occurs.
|
||||
*/
|
||||
protected void doClear(Cache cache) {
|
||||
try {
|
||||
cache.clear();
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
getErrorHandler().handleCacheClearError(e, cache);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -69,7 +69,8 @@ import org.springframework.util.StringUtils;
|
|||
* @author Stephane Nicoll
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class CacheAspectSupport implements InitializingBean, ApplicationContextAware {
|
||||
public abstract class CacheAspectSupport extends AbstractCacheInvoker
|
||||
implements InitializingBean, ApplicationContextAware {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
|
|
@ -168,6 +169,7 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
|||
"to use or set the cache manager to create a default cache resolver based on it.");
|
||||
Assert.state(this.cacheOperationSource != null, "The 'cacheOperationSources' property is required: " +
|
||||
"If there are no cacheable methods, then don't use a cache aspect.");
|
||||
Assert.state(this.getErrorHandler() != null, "The 'errorHandler' is required.");
|
||||
Assert.state(this.applicationContext != null, "The application context was not injected as it should.");
|
||||
this.initialized = true;
|
||||
}
|
||||
|
|
@ -343,14 +345,14 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
|||
for (Cache cache : context.getCaches()) {
|
||||
if (operation.isCacheWide()) {
|
||||
logInvalidating(context, operation, null);
|
||||
cache.clear();
|
||||
doClear(cache);
|
||||
}
|
||||
else {
|
||||
if (key == null) {
|
||||
key = context.generateKey(result);
|
||||
}
|
||||
logInvalidating(context, operation, key);
|
||||
cache.evict(key);
|
||||
doEvict(cache, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -402,7 +404,7 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
|||
|
||||
private Cache.ValueWrapper findInCaches(CacheOperationContext context, Object key) {
|
||||
for (Cache cache : context.getCaches()) {
|
||||
Cache.ValueWrapper wrapper = cache.get(key);
|
||||
Cache.ValueWrapper wrapper = doGet(cache, key);
|
||||
if (wrapper != null) {
|
||||
return wrapper;
|
||||
}
|
||||
|
|
@ -571,7 +573,7 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
|||
}
|
||||
|
||||
|
||||
private static class CachePutRequest {
|
||||
private class CachePutRequest {
|
||||
|
||||
private final CacheOperationContext context;
|
||||
|
||||
|
|
@ -585,7 +587,7 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
|||
public void apply(Object result) {
|
||||
if (this.context.canPutToCache(result)) {
|
||||
for (Cache cache : this.context.getCaches()) {
|
||||
cache.put(this.key, result);
|
||||
doPut(cache, this.key, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
78
spring-context/src/main/java/org/springframework/cache/interceptor/CacheErrorHandler.java
vendored
Normal file
78
spring-context/src/main/java/org/springframework/cache/interceptor/CacheErrorHandler.java
vendored
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright 2002-2014 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.interceptor;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
|
||||
/**
|
||||
* A strategy for handling cache-related errors. In most cases, any
|
||||
* exception thrown by the provider should simply be thrown back at
|
||||
* the client but, in some circumstances, the infrastructure may need
|
||||
* to handle cache-provider exceptions in a different way.
|
||||
*
|
||||
* <p>Typically, failing to retrieve an object from the cache with
|
||||
* a given id can be transparently managed as a cache miss by not
|
||||
* throwing back such exception.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public interface CacheErrorHandler {
|
||||
|
||||
/**
|
||||
* Handle the given runtime exception thrown by the cache provider when
|
||||
* retrieving an item with the specified {@code key}, possibly
|
||||
* rethrowing it as a fatal exception.
|
||||
* @param exception the exception thrown by the cache provider
|
||||
* @param cache the cache
|
||||
* @param key the key used to get the item
|
||||
* @see Cache#get(Object)
|
||||
*/
|
||||
void handleCacheGetError(RuntimeException exception, Cache cache, Object key);
|
||||
|
||||
/**
|
||||
* Handle the given runtime exception thrown by the cache provider when
|
||||
* updating an item with the specified {@code key} and {@code value},
|
||||
* possibly rethrowing it as a fatal exception.
|
||||
* @param exception the exception thrown by the cache provider
|
||||
* @param cache the cache
|
||||
* @param key the key used to update the item
|
||||
* @param value the value to associate with the key
|
||||
* @see Cache#put(Object, Object)
|
||||
*/
|
||||
void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value);
|
||||
|
||||
/**
|
||||
* Handle the given runtime exception thrown by the cache provider when
|
||||
* clearing an item with the specified {@code key}, possibly rethrowing
|
||||
* it as a fatal exception.
|
||||
* @param exception the exception thrown by the cache provider
|
||||
* @param cache the cache
|
||||
* @param key the key used to clear the item
|
||||
*/
|
||||
void handleCacheEvictError(RuntimeException exception, Cache cache, Object key);
|
||||
|
||||
/**
|
||||
* Handle the given runtime exception thrown by the cache provider when
|
||||
* clearing the specified {@link Cache}, possibly rethrowing it as a
|
||||
* fatal exception.
|
||||
* @param exception the exception thrown by the cache provider
|
||||
* @param cache the cache to clear
|
||||
*/
|
||||
void handleCacheClearError(RuntimeException exception, Cache cache);
|
||||
|
||||
}
|
||||
49
spring-context/src/main/java/org/springframework/cache/interceptor/SimpleCacheErrorHandler.java
vendored
Normal file
49
spring-context/src/main/java/org/springframework/cache/interceptor/SimpleCacheErrorHandler.java
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2002-2014 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.interceptor;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
|
||||
/**
|
||||
* A simple {@link CacheErrorHandler} that does not handle the
|
||||
* exception at all, simply throwing it back at the client.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
*/
|
||||
public class SimpleCacheErrorHandler implements CacheErrorHandler {
|
||||
|
||||
@Override
|
||||
public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
|
||||
throw exception;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
|
||||
throw exception;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
|
||||
throw exception;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCacheClearError(RuntimeException exception, Cache cache) {
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
|
@ -27,4 +27,5 @@ http\://www.springframework.org/schema/task/spring-task.xsd=org/springframework/
|
|||
http\://www.springframework.org/schema/cache/spring-cache-3.1.xsd=org/springframework/cache/config/spring-cache-3.1.xsd
|
||||
http\://www.springframework.org/schema/cache/spring-cache-3.2.xsd=org/springframework/cache/config/spring-cache-3.2.xsd
|
||||
http\://www.springframework.org/schema/cache/spring-cache-4.0.xsd=org/springframework/cache/config/spring-cache-4.0.xsd
|
||||
http\://www.springframework.org/schema/cache/spring-cache.xsd=org/springframework/cache/config/spring-cache-4.0.xsd
|
||||
http\://www.springframework.org/schema/cache/spring-cache-4.1.xsd=org/springframework/cache/config/spring-cache-4.1.xsd
|
||||
http\://www.springframework.org/schema/cache/spring-cache.xsd=org/springframework/cache/config/spring-cache-4.1.xsd
|
||||
|
|
|
|||
288
spring-context/src/main/resources/org/springframework/cache/config/spring-cache-4.1.xsd
vendored
Normal file
288
spring-context/src/main/resources/org/springframework/cache/config/spring-cache-4.1.xsd
vendored
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
|
||||
<xsd:schema xmlns="http://www.springframework.org/schema/cache"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:tool="http://www.springframework.org/schema/tool"
|
||||
targetNamespace="http://www.springframework.org/schema/cache"
|
||||
elementFormDefault="qualified"
|
||||
attributeFormDefault="unqualified">
|
||||
|
||||
<xsd:import namespace="http://www.springframework.org/schema/beans" schemaLocation="http://www.springframework.org/schema/beans/spring-beans-4.1.xsd"/>
|
||||
<xsd:import namespace="http://www.springframework.org/schema/tool" schemaLocation="http://www.springframework.org/schema/tool/spring-tool-4.1.xsd"/>
|
||||
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Defines the elements used in the Spring Framework's declarative
|
||||
cache management infrastructure.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
<xsd:element name="annotation-driven">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.cache.annotation.AnnotationCacheOperationDefinitionSource"><![CDATA[
|
||||
Indicates that cache configuration is defined by Java 5
|
||||
annotations on bean classes, and that proxies are automatically
|
||||
to be created for the relevant annotated beans.
|
||||
|
||||
The default annotations supported are Spring's @Cacheable and @CacheEvict.
|
||||
|
||||
See org.springframework.cache.annotation.EnableCaching Javadoc
|
||||
for information on code-based alternatives to this XML element.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:attribute name="cache-manager" type="xsd:string" default="cacheManager">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.cache.CacheManager"><![CDATA[
|
||||
The bean name of the CacheManager that is to be used to retrieve the backing caches.
|
||||
|
||||
This attribute is not required, and only needs to be specified
|
||||
explicitly if the bean name of the desired CacheManager
|
||||
is not 'cacheManager'.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.cache.CacheManager"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="key-generator" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.cache.interceptor.KeyGenerator"><![CDATA[
|
||||
The bean name of the KeyGenerator that is to be used to retrieve the backing caches.
|
||||
|
||||
This attribute is not required, and only needs to be specified
|
||||
explicitly if the default strategy (DefaultKeyGenerator) is not sufficient.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.cache.interceptor.KeyGenerator"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="error-handler" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.cache.interceptor.CacheErrorHandler"><![CDATA[
|
||||
The bean name of the CacheErrorHandler that is to be used to handle cache-related errors.
|
||||
|
||||
This attribute is not required, and only needs to be specified
|
||||
explicitly if the default strategy (SimpleCacheErrorHandler) is not sufficient.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.cache.interceptor.CacheErrorHandler"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="mode" default="proxy">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Should annotated beans be proxied using Spring's AOP framework,
|
||||
or should they rather be weaved with an AspectJ transaction aspect?
|
||||
|
||||
AspectJ weaving requires spring-aspects.jar on the classpath,
|
||||
as well as load-time weaving (or compile-time weaving) enabled.
|
||||
|
||||
Note: The weaving-based aspect requires the @Cacheable and @CacheInvalidate
|
||||
annotations to be defined on the concrete class. Annotations in interfaces
|
||||
will not work in that case (they will rather only work with interface-based proxies)!
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:restriction base="xsd:string">
|
||||
<xsd:enumeration value="proxy"/>
|
||||
<xsd:enumeration value="aspectj"/>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="proxy-target-class" type="xsd:boolean" default="false">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Are class-based (CGLIB) proxies to be created? By default, standard
|
||||
Java interface-based proxies are created.
|
||||
|
||||
Note: Class-based proxies require the @Cacheable and @CacheInvalidate annotations
|
||||
to be defined on the concrete class. Annotations in interfaces will not work
|
||||
in that case (they will rather only work with interface-based proxies)!
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="order" type="xsd:token">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.core.Ordered"><![CDATA[
|
||||
Controls the ordering of the execution of the cache advisor
|
||||
when multiple advice executes at a specific joinpoint.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="advice">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.cache.interceptor.CacheInterceptor"><![CDATA[
|
||||
Defines the cache semantics of the AOP advice that is to be
|
||||
executed.
|
||||
|
||||
That is, this advice element is where the cacheable semantics of
|
||||
any number of methods are defined (where cacheable semantics
|
||||
includes the backing cache(s), the key, cache condition rules, and suchlike).
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation>
|
||||
<tool:exports type="java:org.springframework.cache.interceptor.CacheInterceptor"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="beans:identifiedType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="caching" type="definitionsType" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="cache-manager" type="xsd:string" default="cacheManager">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.cache.CacheManager"><![CDATA[
|
||||
The bean name of the CacheManager that is to be used
|
||||
for storing and retrieving data.
|
||||
|
||||
This attribute is not required, and only needs to be specified
|
||||
explicitly if the bean name of the desired CacheManager
|
||||
is not 'cacheManager'.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.cache.CacheManager"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="key-generator" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.cache.interceptor.KeyGenerator"><![CDATA[
|
||||
The bean name of the KeyGenerator that is to be used to retrieve the backing caches.
|
||||
|
||||
This attribute is not required, and only needs to be specified
|
||||
explicitly if the default strategy (DefaultKeyGenerator) is not sufficient.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.cache.interceptor.KeyGenerator"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:complexType name="basedefinitionType">
|
||||
<xsd:attribute name="cache" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The name of the backing cache(s). Multiple caches can be specified by separating them using comma: 'orders, books']]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="key" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The SpEL expression used for computing the cache key, mutually exclusive with the key-generator parameter.]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="key-generator" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The name of the KeyGenerator bean responsible to compute the key, mutually exclusive with the key parameter.]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="cache-manager" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The name of the CacheManager bean responsible to manage the operation.]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="condition" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The SpEL expression used for conditioning the method caching.]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="method" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The method name(s) with which the cache attributes are to be
|
||||
associated. The wildcard (*) character can be used to associate the
|
||||
same cache attribute settings with a number of methods; for
|
||||
example, 'get*', 'handle*', '*Order', 'on*Event', etc.]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="definitionsType">
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="basedefinitionType">
|
||||
<xsd:sequence>
|
||||
<xsd:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:element name="cacheable" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="basedefinitionType">
|
||||
<xsd:attribute name="unless" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The SpEL expression used to veto the method caching.]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="cache-put" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="basedefinitionType">
|
||||
<xsd:attribute name="unless" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The SpEL expression used to veto the method caching.]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="cache-evict" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="basedefinitionType">
|
||||
<xsd:attribute name="all-entries" type="xsd:boolean" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Whether all the entries should be evicted.]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="before-invocation" type="xsd:boolean" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Whether the eviction should occur after the method is successfully
|
||||
invoked (default) or before.]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:sequence>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
</xsd:schema>
|
||||
|
|
@ -16,9 +16,11 @@
|
|||
|
||||
package org.springframework.cache.config;
|
||||
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.CacheInterceptor;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.GenericXmlApplicationContext;
|
||||
|
|
@ -36,9 +38,16 @@ public class AnnotationNamespaceDrivenTests extends AbstractAnnotationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testKeyStrategy() throws Exception {
|
||||
public void testKeyStrategy() {
|
||||
CacheInterceptor ci = ctx.getBean("org.springframework.cache.interceptor.CacheInterceptor#0",
|
||||
CacheInterceptor.class);
|
||||
assertSame(ctx.getBean("keyGenerator"), ci.getKeyGenerator());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCacheErrorHandler() {
|
||||
CacheInterceptor ci = ctx.getBean("org.springframework.cache.interceptor.CacheInterceptor#0",
|
||||
CacheInterceptor.class);
|
||||
assertSame(ctx.getBean("errorHandler", CacheErrorHandler.class), ci.getErrorHandler());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,10 +24,12 @@ import org.springframework.cache.CacheManager;
|
|||
import org.springframework.cache.CacheTestUtils;
|
||||
import org.springframework.cache.annotation.CachingConfigurerSupport;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.cache.interceptor.CacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.CacheInterceptor;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.cache.interceptor.NamedCacheResolver;
|
||||
import org.springframework.cache.interceptor.SimpleCacheErrorHandler;
|
||||
import org.springframework.cache.interceptor.SimpleCacheResolver;
|
||||
import org.springframework.cache.support.NoOpCacheManager;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
|
@ -50,11 +52,17 @@ public class EnableCachingTests extends AbstractAnnotationTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testKeyStrategy() throws Exception {
|
||||
public void testKeyStrategy() {
|
||||
CacheInterceptor ci = ctx.getBean(CacheInterceptor.class);
|
||||
assertSame(ctx.getBean("keyGenerator", KeyGenerator.class), ci.getKeyGenerator());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCacheErrorHandler() {
|
||||
CacheInterceptor ci = ctx.getBean(CacheInterceptor.class);
|
||||
assertSame(ctx.getBean("errorHandler", CacheErrorHandler.class), ci.getErrorHandler());
|
||||
}
|
||||
|
||||
// --- local tests -------
|
||||
|
||||
@Test
|
||||
|
|
@ -160,6 +168,12 @@ public class EnableCachingTests extends AbstractAnnotationTests {
|
|||
return new SomeKeyGenerator();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public CacheErrorHandler errorHandler() {
|
||||
return new SimpleCacheErrorHandler();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public KeyGenerator customKeyGenerator() {
|
||||
return new SomeCustomKeyGenerator();
|
||||
|
|
|
|||
241
spring-context/src/test/java/org/springframework/cache/interceptor/CacheErrorHandlerTests.java
vendored
Normal file
241
spring-context/src/test/java/org/springframework/cache/interceptor/CacheErrorHandlerTests.java
vendored
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
/*
|
||||
* Copyright 2002-2014 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.interceptor;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.BDDMockito.doReturn;
|
||||
import static org.mockito.BDDMockito.doThrow;
|
||||
import static org.mockito.BDDMockito.*;
|
||||
import static org.mockito.BDDMockito.verify;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.annotation.AnnotationCacheOperationSource;
|
||||
import org.springframework.cache.annotation.CacheConfig;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.CachePut;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.cache.support.SimpleCacheManager;
|
||||
import org.springframework.cache.support.SimpleValueWrapper;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class CacheErrorHandlerTests {
|
||||
|
||||
@Rule
|
||||
public final ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private Cache cache;
|
||||
|
||||
private CacheInterceptor cacheInterceptor;
|
||||
|
||||
private CacheErrorHandler errorHandler;
|
||||
|
||||
private SimpleService simpleService;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
|
||||
this.cache = context.getBean("mockCache", Cache.class);
|
||||
this.cacheInterceptor = context.getBean(CacheInterceptor.class);
|
||||
this.errorHandler = context.getBean(CacheErrorHandler.class);
|
||||
this.simpleService = context.getBean(SimpleService.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFail() {
|
||||
UnsupportedOperationException exception = new UnsupportedOperationException("Test exception on get");
|
||||
doThrow(exception).when(cache).get(0L);
|
||||
|
||||
Object result = this.simpleService.get(0L);
|
||||
verify(errorHandler).handleCacheGetError(exception, cache, 0L);
|
||||
verify(cache).get(0L);
|
||||
verify(cache).put(0L, result); // result of the invocation
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAndPutFail() {
|
||||
UnsupportedOperationException exception = new UnsupportedOperationException("Test exception on get");
|
||||
doThrow(exception).when(cache).get(0L);
|
||||
doThrow(exception).when(cache).put(0L, 0L); // Update of the cache will fail as well
|
||||
|
||||
Object counter = this.simpleService.get(0L);
|
||||
|
||||
doReturn(new SimpleValueWrapper(2L)).when(cache).get(0L);
|
||||
Object counter2 = this.simpleService.get(0L);
|
||||
Object counter3 = this.simpleService.get(0L);
|
||||
assertNotSame(counter, counter2);
|
||||
assertEquals(counter2, counter3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFailProperException() {
|
||||
UnsupportedOperationException exception = new UnsupportedOperationException("Test exception on get");
|
||||
doThrow(exception).when(cache).get(0L);
|
||||
|
||||
cacheInterceptor.setErrorHandler(new SimpleCacheErrorHandler());
|
||||
|
||||
thrown.expect(is(exception));
|
||||
this.simpleService.get(0L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void putFail() {
|
||||
UnsupportedOperationException exception = new UnsupportedOperationException("Test exception on put");
|
||||
doThrow(exception).when(cache).put(0L, 0L);
|
||||
|
||||
this.simpleService.put(0L);
|
||||
verify(errorHandler).handleCachePutError(exception, cache, 0L, 0L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void putFailProperException() {
|
||||
UnsupportedOperationException exception = new UnsupportedOperationException("Test exception on put");
|
||||
doThrow(exception).when(cache).put(0L, 0L);
|
||||
|
||||
cacheInterceptor.setErrorHandler(new SimpleCacheErrorHandler());
|
||||
|
||||
thrown.expect(is(exception));
|
||||
this.simpleService.put(0L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void evictFail() {
|
||||
UnsupportedOperationException exception = new UnsupportedOperationException("Test exception on evict");
|
||||
doThrow(exception).when(cache).evict(0L);
|
||||
|
||||
this.simpleService.evict(0L);
|
||||
verify(errorHandler).handleCacheEvictError(exception, cache, 0L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void evictFailProperException() {
|
||||
UnsupportedOperationException exception = new UnsupportedOperationException("Test exception on evict");
|
||||
doThrow(exception).when(cache).evict(0L);
|
||||
|
||||
cacheInterceptor.setErrorHandler(new SimpleCacheErrorHandler());
|
||||
|
||||
thrown.expect(is(exception));
|
||||
this.simpleService.evict(0L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearFail() {
|
||||
UnsupportedOperationException exception = new UnsupportedOperationException("Test exception on evict");
|
||||
doThrow(exception).when(cache).clear();
|
||||
|
||||
this.simpleService.clear();
|
||||
verify(errorHandler).handleCacheClearError(exception, cache);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearFailProperException() {
|
||||
UnsupportedOperationException exception = new UnsupportedOperationException("Test exception on evict");
|
||||
doThrow(exception).when(cache).clear();
|
||||
|
||||
cacheInterceptor.setErrorHandler(new SimpleCacheErrorHandler());
|
||||
|
||||
thrown.expect(is(exception));
|
||||
this.simpleService.clear();
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public CacheInterceptor cacheInterceptor() {
|
||||
CacheInterceptor cacheInterceptor = new CacheInterceptor();
|
||||
cacheInterceptor.setCacheManager(cacheManager());
|
||||
cacheInterceptor.setCacheOperationSources(cacheOperationSource());
|
||||
cacheInterceptor.setErrorHandler(errorHandler());
|
||||
return cacheInterceptor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CacheErrorHandler errorHandler() {
|
||||
return mock(CacheErrorHandler.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CacheOperationSource cacheOperationSource() {
|
||||
return new AnnotationCacheOperationSource();
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SimpleService simpleService() {
|
||||
return new SimpleService();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CacheManager cacheManager() {
|
||||
SimpleCacheManager cacheManager = new SimpleCacheManager();
|
||||
cacheManager.setCaches(Arrays.asList(mockCache()));
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Cache mockCache() {
|
||||
Cache cache = mock(Cache.class);
|
||||
given(cache.getName()).willReturn("test");
|
||||
return cache;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@CacheConfig(cacheNames = "test")
|
||||
public static class SimpleService {
|
||||
private AtomicLong counter = new AtomicLong();
|
||||
|
||||
@Cacheable
|
||||
public Object get(long id) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@CachePut
|
||||
public Object put(long id) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@CacheEvict
|
||||
public void evict(long id) {
|
||||
}
|
||||
|
||||
@CacheEvict(allEntries = true)
|
||||
public void clear() {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -7,7 +7,8 @@
|
|||
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
|
||||
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
|
||||
|
||||
<cache:annotation-driven proxy-target-class="false" order="0" key-generator="keyGenerator"/>
|
||||
<cache:annotation-driven proxy-target-class="false" order="0"
|
||||
key-generator="keyGenerator" error-handler="errorHandler"/>
|
||||
|
||||
<aop:config>
|
||||
<aop:advisor advice-ref="debugInterceptor" pointcut="execution(* *..CacheableService.*(..))" order="1"/>
|
||||
|
|
@ -31,6 +32,8 @@
|
|||
|
||||
<bean id="keyGenerator" class="org.springframework.cache.config.SomeKeyGenerator"/>
|
||||
|
||||
<bean id="errorHandler" class="org.springframework.cache.interceptor.SimpleCacheErrorHandler"/>
|
||||
|
||||
<bean id="customKeyGenerator" class="org.springframework.cache.config.SomeCustomKeyGenerator"/>
|
||||
|
||||
<bean id="customCacheManager" class="org.springframework.cache.support.SimpleCacheManager">
|
||||
|
|
|
|||
|
|
@ -47196,6 +47196,17 @@ application through AOP. The configuration is intentionally similar with that of
|
|||
| Name of cache manager to use. Only required if the name of the cache manager is not
|
||||
`cacheManager`.
|
||||
|
||||
| `key-generator`
|
||||
| N/A (See `CachingConfigurer` javadocs)
|
||||
| `SimpleKeyGenerator`
|
||||
| Name of the custom key generator to use.
|
||||
|
||||
| `error-handler`
|
||||
| N/A (See `CachingConfigurer` javadocs)
|
||||
| `SimpleCacheErrorHandler`
|
||||
| Name of the custom cache error handler to use. By default, any exception throw during
|
||||
a cache related operations are thrown back at the client.
|
||||
|
||||
| `mode`
|
||||
| `mode`
|
||||
| proxy
|
||||
|
|
|
|||
Loading…
Reference in New Issue