Use CacheResolver in Spring abstraction
Prior to this commit, the CacheResolver was not used by Spring's caching abstraction. This commit provides the necessary configuration options to tune how a cache is resolved for a given operation. CacheResolver can be customized globally, at the operation level or at the class level. This breaks the CachingConfigurer class and a support implementation is provided that implements all methods so that the default is taken if it's not overridden. The JSR-107 support has been updated as well, with a similar support class. In particular, the static and runtime information of a cache operation were mixed which prevents any forms of caching. As the CacheResolver and the KeyGenerator can be customized, every operation call lead to a lookup in the context for the bean. This commit adds CacheOperationMetadata, a static holder of all the non-runtime metadata about a cache operation. This is used as an input source for the existing CacheOperationContext. Caching the operation metadata in an AspectJ aspect can have side effects as the aspect is static instance for the current ClassLoader. The metadata cache needs to be cleared when the context shutdowns. This is essentially a test issue only as in practice each application runs in its class loader. Tests are now closing the context properly to honor the DisposableBean callback. Issue: SPR-11490
This commit is contained in:
parent
47a4327193
commit
f3b8a4103e
|
|
@ -20,6 +20,7 @@ import java.lang.reflect.Method;
|
|||
|
||||
import org.aspectj.lang.annotation.SuppressAjWarnings;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.cache.interceptor.CacheAspectSupport;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
import org.springframework.cache.interceptor.CacheOperationSource;
|
||||
|
|
@ -36,9 +37,10 @@ import org.springframework.cache.interceptor.CacheOperationSource;
|
|||
* relevant Spring cache definition will <i>not</i> be resolved.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Stephane Nicoll
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract aspect AbstractCacheAspect extends CacheAspectSupport {
|
||||
public abstract aspect AbstractCacheAspect extends CacheAspectSupport implements DisposableBean {
|
||||
|
||||
protected AbstractCacheAspect() {
|
||||
}
|
||||
|
|
@ -52,6 +54,11 @@ public abstract aspect AbstractCacheAspect extends CacheAspectSupport {
|
|||
setCacheOperationSources(cos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
clearMetadataCache(); // An aspect is basically a singleton
|
||||
}
|
||||
|
||||
@SuppressAjWarnings("adviceDidNotMatch")
|
||||
Object around(final Object cachedObject) : cacheMethodExecution(cachedObject) {
|
||||
MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -25,7 +25,7 @@ import org.junit.Test;
|
|||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.config.AbstractAnnotationTests;
|
||||
import org.springframework.cache.config.CacheableService;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.GenericXmlApplicationContext;
|
||||
|
||||
/**
|
||||
|
|
@ -35,7 +35,7 @@ public class AspectJAnnotationTests extends AbstractAnnotationTests {
|
|||
|
||||
|
||||
@Override
|
||||
protected ApplicationContext getApplicationContext() {
|
||||
protected ConfigurableApplicationContext getApplicationContext() {
|
||||
return new GenericXmlApplicationContext("/org/springframework/cache/config/annotation-cache-aspectj.xml");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,13 +36,11 @@ import org.springframework.context.annotation.Role;
|
|||
@Configuration
|
||||
public class AbstractJCacheConfiguration extends AbstractCachingConfiguration<JCacheConfigurer> {
|
||||
|
||||
protected CacheResolver cacheResolver;
|
||||
protected CacheResolver exceptionCacheResolver;
|
||||
|
||||
@Override
|
||||
protected void useCachingConfigurer(JCacheConfigurer config) {
|
||||
super.useCachingConfigurer(config);
|
||||
this.cacheResolver = config.cacheResolver();
|
||||
this.exceptionCacheResolver = config.exceptionCacheResolver();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,42 +21,25 @@ import org.springframework.cache.interceptor.CacheResolver;
|
|||
|
||||
/**
|
||||
* Extension of {@link CachingConfigurer} for the JSR-107 implementation.
|
||||
|
||||
* <p>To be implemented by classes annotated with @{@link org.springframework.cache.annotation.EnableCaching}
|
||||
* that wish or need to specify explicitly the {@link CacheResolver} bean(s) to be used for
|
||||
* annotation-driven cache management.
|
||||
*
|
||||
* <p>See @{@link org.springframework.cache.annotation.EnableCaching} for general examples and
|
||||
* context; see {@link #cacheResolver()} and {@link #exceptionCacheResolver()} for detailed
|
||||
* <p>To be implemented by classes annotated with
|
||||
* @{@link org.springframework.cache.annotation.EnableCaching} that wish or
|
||||
* need to specify explicitly how exception caches are resolved for
|
||||
* annotation-driven cache management. Consider extending {@link JCacheConfigurerSupport},
|
||||
* which provides a stub implementation of all interface methods.
|
||||
*
|
||||
* <p>See @{@link org.springframework.cache.annotation.EnableCaching} for
|
||||
* general examples and context; see {@link #exceptionCacheResolver()} for detailed
|
||||
* instructions.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see CachingConfigurer
|
||||
* @see JCacheConfigurerSupport
|
||||
* @see org.springframework.cache.annotation.EnableCaching
|
||||
*/
|
||||
public interface JCacheConfigurer extends CachingConfigurer {
|
||||
|
||||
/**
|
||||
* Return the {@link CacheResolver} bean to use to resolve regular caches for
|
||||
* annotation-driven cache management. Implementations must explicitly declare
|
||||
* {@link org.springframework.context.annotation.Bean @Bean}, e.g.
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableCaching
|
||||
* public class AppConfig implements JCacheConfigurer {
|
||||
* @Bean // important!
|
||||
* @Override
|
||||
* public CacheResolver cacheResolver() {
|
||||
* // configure and return CacheResolver instance
|
||||
* }
|
||||
* // ...
|
||||
* }
|
||||
* </pre>
|
||||
* See {@link org.springframework.cache.annotation.EnableCaching} for more complete examples.
|
||||
*/
|
||||
CacheResolver cacheResolver();
|
||||
|
||||
/**
|
||||
* Return the {@link CacheResolver} bean to use to resolve exception caches for
|
||||
* annotation-driven cache management. Implementations must explicitly declare
|
||||
|
|
@ -64,7 +47,7 @@ public interface JCacheConfigurer extends CachingConfigurer {
|
|||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableCaching
|
||||
* public class AppConfig implements JCacheConfigurer {
|
||||
* public class AppConfig extends JCacheConfigurerSupport {
|
||||
* @Bean // important!
|
||||
* @Override
|
||||
* public CacheResolver exceptionCacheResolver() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.config;
|
||||
|
||||
import org.springframework.cache.annotation.CachingConfigurerSupport;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
|
||||
/**
|
||||
* An extension of {@link CachingConfigurerSupport} that also implements
|
||||
* {@link JCacheConfigurer}.
|
||||
*
|
||||
* <p>Users of JSR-107 annotations may extend from this class rather than
|
||||
* implementing from {@link JCacheConfigurer} directly.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see JCacheConfigurer
|
||||
* @see CachingConfigurerSupport
|
||||
*/
|
||||
public class JCacheConfigurerSupport extends CachingConfigurerSupport implements JCacheConfigurer {
|
||||
|
||||
@Override
|
||||
public CacheResolver exceptionCacheResolver() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -19,13 +19,12 @@ package org.springframework.cache.jcache.interceptor;
|
|||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.interceptor.BaseCacheResolver;
|
||||
import org.springframework.cache.interceptor.BasicCacheOperation;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.jcache.model.CacheResultOperation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A simple {@link CacheResolver} that resolves the exception cache
|
||||
|
|
@ -36,16 +35,14 @@ import org.springframework.util.Assert;
|
|||
* @since 4.1
|
||||
* @see org.springframework.cache.jcache.model.CacheResultOperation#getExceptionCacheName()
|
||||
*/
|
||||
public class SimpleExceptionCacheResolver implements CacheResolver {
|
||||
|
||||
private final CacheManager cacheManager;
|
||||
public class SimpleExceptionCacheResolver extends BaseCacheResolver {
|
||||
|
||||
public SimpleExceptionCacheResolver(CacheManager cacheManager) {
|
||||
this.cacheManager = cacheManager;
|
||||
super(cacheManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
|
||||
protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> context) {
|
||||
BasicCacheOperation operation = context.getOperation();
|
||||
if (!(operation instanceof CacheResultOperation)) {
|
||||
throw new IllegalStateException("Could not extract exception cache name from " + operation);
|
||||
|
|
@ -53,9 +50,7 @@ public class SimpleExceptionCacheResolver implements CacheResolver {
|
|||
CacheResultOperation cacheResultOperation = (CacheResultOperation) operation;
|
||||
String exceptionCacheName = cacheResultOperation.getExceptionCacheName();
|
||||
if (exceptionCacheName != null) {
|
||||
Cache cache = cacheManager.getCache(exceptionCacheName);
|
||||
Assert.notNull(cache, "Cannot find cache named '" + exceptionCacheName + "' for " + operation);
|
||||
return Collections.singleton(cache);
|
||||
return Collections.singleton(exceptionCacheName);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,15 +26,19 @@ import org.springframework.cache.Cache;
|
|||
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.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.cache.interceptor.NamedCacheResolver;
|
||||
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.SimpleExceptionCacheResolver;
|
||||
import org.springframework.cache.support.NoOpCacheManager;
|
||||
import org.springframework.cache.support.SimpleCacheManager;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
|
@ -61,6 +65,35 @@ public class JCacheJavaConfigTests extends AbstractJCacheAnnotationTests {
|
|||
cos.getDefaultExceptionCacheResolver());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyConfigSupport() {
|
||||
ConfigurableApplicationContext context =
|
||||
new AnnotationConfigApplicationContext(EmptyConfigSupportConfig.class);
|
||||
|
||||
DefaultJCacheOperationSource cos = context.getBean(DefaultJCacheOperationSource.class);
|
||||
assertNotNull(cos.getDefaultCacheResolver());
|
||||
assertEquals(SimpleCacheResolver.class, cos.getDefaultCacheResolver().getClass());
|
||||
assertSame(context.getBean(CacheManager.class),
|
||||
((SimpleCacheResolver) cos.getDefaultCacheResolver()).getCacheManager());
|
||||
assertNotNull(cos.getDefaultExceptionCacheResolver());
|
||||
assertEquals(SimpleExceptionCacheResolver.class, cos.getDefaultExceptionCacheResolver().getClass());
|
||||
assertSame(context.getBean(CacheManager.class),
|
||||
((SimpleExceptionCacheResolver) cos.getDefaultExceptionCacheResolver()).getCacheManager());
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bothSetOnlyResolverIsUsed() {
|
||||
ConfigurableApplicationContext context =
|
||||
new AnnotationConfigApplicationContext(FullCachingConfigSupport.class);
|
||||
|
||||
DefaultJCacheOperationSource cos = context.getBean(DefaultJCacheOperationSource.class);
|
||||
assertSame(context.getBean("cacheResolver"), cos.getDefaultCacheResolver());
|
||||
assertSame(context.getBean("keyGenerator"), cos.getDefaultKeyGenerator());
|
||||
assertSame(context.getBean("exceptionCacheResolver"), cos.getDefaultExceptionCacheResolver());
|
||||
context.close();
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
|
|
@ -118,4 +151,42 @@ public class JCacheJavaConfigTests extends AbstractJCacheAnnotationTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
public static class EmptyConfigSupportConfig extends JCacheConfigurerSupport {
|
||||
@Bean
|
||||
public CacheManager cm() {
|
||||
return new NoOpCacheManager();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
static class FullCachingConfigSupport extends JCacheConfigurerSupport {
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public CacheManager cacheManager() {
|
||||
return new NoOpCacheManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public KeyGenerator keyGenerator() {
|
||||
return new SomeKeyGenerator();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public CacheResolver cacheResolver() {
|
||||
return new NamedCacheResolver(cacheManager(), "foo");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public CacheResolver exceptionCacheResolver() {
|
||||
return new NamedCacheResolver(cacheManager(), "exception");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,18 +19,14 @@ package org.springframework.cache.jcache.interceptor;
|
|||
import static org.junit.Assert.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||
import org.springframework.cache.interceptor.CacheOperationInvoker;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.cache.interceptor.NamedCacheResolver;
|
||||
import org.springframework.cache.jcache.AbstractJCacheTests;
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
|
@ -45,7 +41,7 @@ public class JCacheInterceptorTests extends AbstractJCacheTests {
|
|||
@Test
|
||||
public void severalCachesNotSupported() {
|
||||
JCacheInterceptor interceptor = createInterceptor(createOperationSource(
|
||||
cacheManager, new TestCacheResolver("default", "exception"),
|
||||
cacheManager, new NamedCacheResolver(cacheManager, "default", "simpleCache"),
|
||||
defaultExceptionCacheResolver, defaultKeyGenerator));
|
||||
|
||||
AnnotatedJCacheableService service = new AnnotatedJCacheableService(cacheManager.getCache("default"));
|
||||
|
|
@ -65,7 +61,7 @@ public class JCacheInterceptorTests extends AbstractJCacheTests {
|
|||
@Test
|
||||
public void noCacheCouldBeResolved() {
|
||||
JCacheInterceptor interceptor = createInterceptor(createOperationSource(
|
||||
cacheManager, new TestCacheResolver(), // Returns empty list
|
||||
cacheManager, new NamedCacheResolver(cacheManager), // Returns empty list
|
||||
defaultExceptionCacheResolver, defaultKeyGenerator));
|
||||
|
||||
AnnotatedJCacheableService service = new AnnotatedJCacheableService(cacheManager.getCache("default"));
|
||||
|
|
@ -132,25 +128,6 @@ public class JCacheInterceptorTests extends AbstractJCacheTests {
|
|||
return interceptor;
|
||||
}
|
||||
|
||||
|
||||
private class TestCacheResolver implements CacheResolver {
|
||||
|
||||
private final String[] cacheNames;
|
||||
|
||||
private TestCacheResolver(String... cacheNames) {
|
||||
this.cacheNames = cacheNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
|
||||
List<Cache> result = new ArrayList<Cache>();
|
||||
for (String cacheName : cacheNames) {
|
||||
result.add(cacheManager.getCache(cacheName));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private static class DummyInvoker implements CacheOperationInvoker {
|
||||
|
||||
private final Object result;
|
||||
|
|
|
|||
|
|
@ -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.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportAware;
|
||||
|
|
@ -46,6 +47,8 @@ public abstract class AbstractCachingConfiguration<C extends CachingConfigurer>
|
|||
|
||||
protected CacheManager cacheManager;
|
||||
|
||||
protected CacheResolver cacheResolver;
|
||||
|
||||
protected KeyGenerator keyGenerator;
|
||||
|
||||
@Autowired(required=false)
|
||||
|
|
@ -86,7 +89,7 @@ public abstract class AbstractCachingConfiguration<C extends CachingConfigurer>
|
|||
C cachingConfigurer = cachingConfigurers.iterator().next();
|
||||
useCachingConfigurer(cachingConfigurer);
|
||||
}
|
||||
else if (!CollectionUtils.isEmpty(cacheManagerBeans)) {
|
||||
if (this.cacheManager == null && !CollectionUtils.isEmpty(cacheManagerBeans)) {
|
||||
int nManagers = cacheManagerBeans.size();
|
||||
if (nManagers > 1) {
|
||||
throw new IllegalStateException(nManagers + " beans of type CacheManager " +
|
||||
|
|
@ -98,7 +101,7 @@ public abstract class AbstractCachingConfiguration<C extends CachingConfigurer>
|
|||
this.cacheManager = cacheManagerBeans.iterator().next();
|
||||
// keyGenerator remains null; will fall back to default within CacheInterceptor
|
||||
}
|
||||
else {
|
||||
if (this.cacheManager == null) {
|
||||
throw new IllegalStateException("No bean of type CacheManager could be found. " +
|
||||
"Register a CacheManager bean or remove the @EnableCaching annotation " +
|
||||
"from your configuration.");
|
||||
|
|
@ -110,6 +113,7 @@ public abstract class AbstractCachingConfiguration<C extends CachingConfigurer>
|
|||
*/
|
||||
protected void useCachingConfigurer(C config) {
|
||||
this.cacheManager = config.cacheManager();
|
||||
this.cacheResolver = config.cacheResolver();
|
||||
this.keyGenerator = config.keyGenerator();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,10 +53,21 @@ public @interface CacheConfig {
|
|||
String keyGenerator() default "";
|
||||
|
||||
/**
|
||||
* The bean name of the custom {@link org.springframework.cache.CacheManager} to use.
|
||||
* <p>If none is set at the operation level, this one is used instead of the default.
|
||||
* The bean name of the custom {@link org.springframework.cache.CacheManager} to use to
|
||||
* create a default {@link org.springframework.cache.interceptor.CacheResolver} if none
|
||||
* is set already.
|
||||
* <p>If no resolver and no cache manager are set at the operation level, and no cache
|
||||
* resolver is set on this instance, this one is used instead of the default.
|
||||
* @see org.springframework.cache.interceptor.SimpleCacheResolver
|
||||
*/
|
||||
String cacheManager() default "";
|
||||
|
||||
/**
|
||||
* The bean name of the custom {@link org.springframework.cache.interceptor.CacheResolver} to use.
|
||||
* <p>If no resolver and no cache manager are set at the operation level, this one is used
|
||||
* instead of the default.
|
||||
*/
|
||||
String cacheResolver() default "";
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -59,10 +59,19 @@ public @interface CacheEvict {
|
|||
String keyGenerator() default "";
|
||||
|
||||
/**
|
||||
* The bean name of the custom {@link org.springframework.cache.CacheManager} to use.
|
||||
* The bean name of the custom {@link org.springframework.cache.CacheManager} to use to
|
||||
* create a default {@link org.springframework.cache.interceptor.CacheResolver} if none
|
||||
* is set already.
|
||||
* <p>Mutually exclusive with the {@link #cacheResolver()} attribute.
|
||||
* @see org.springframework.cache.interceptor.SimpleCacheResolver
|
||||
*/
|
||||
String cacheManager() default "";
|
||||
|
||||
/**
|
||||
* The bean name of the custom {@link org.springframework.cache.interceptor.CacheResolver} to use.
|
||||
*/
|
||||
String cacheResolver() default "";
|
||||
|
||||
/**
|
||||
* Spring Expression Language (SpEL) attribute used for conditioning the method caching.
|
||||
* <p>Default is "", meaning the method is always cached.
|
||||
|
|
|
|||
|
|
@ -64,10 +64,19 @@ public @interface CachePut {
|
|||
String keyGenerator() default "";
|
||||
|
||||
/**
|
||||
* The bean name of the custom {@link org.springframework.cache.CacheManager} to use.
|
||||
* The bean name of the custom {@link org.springframework.cache.CacheManager} to use to
|
||||
* create a default {@link org.springframework.cache.interceptor.CacheResolver} if none
|
||||
* is set already.
|
||||
* <p>Mutually exclusive with the {@link #cacheResolver()} attribute.
|
||||
* @see org.springframework.cache.interceptor.SimpleCacheResolver
|
||||
*/
|
||||
String cacheManager() default "";
|
||||
|
||||
/**
|
||||
* The bean name of the custom {@link org.springframework.cache.interceptor.CacheResolver} to use.
|
||||
*/
|
||||
String cacheResolver() default "";
|
||||
|
||||
/**
|
||||
* Spring Expression Language (SpEL) attribute used for conditioning the cache update.
|
||||
* <p>Default is "", meaning the method result is always cached.
|
||||
|
|
|
|||
|
|
@ -62,10 +62,19 @@ public @interface Cacheable {
|
|||
String keyGenerator() default "";
|
||||
|
||||
/**
|
||||
* The bean name of the custom {@link org.springframework.cache.CacheManager} to use.
|
||||
* The bean name of the custom {@link org.springframework.cache.CacheManager} to use to
|
||||
* create a default {@link org.springframework.cache.interceptor.CacheResolver} if none
|
||||
* is set already.
|
||||
* <p>Mutually exclusive with the {@link #cacheResolver()} attribute.
|
||||
* @see org.springframework.cache.interceptor.SimpleCacheResolver
|
||||
*/
|
||||
String cacheManager() default "";
|
||||
|
||||
/**
|
||||
* The bean name of the custom {@link org.springframework.cache.interceptor.CacheResolver} to use.
|
||||
*/
|
||||
String cacheResolver() default "";
|
||||
|
||||
/**
|
||||
* Spring Expression Language (SpEL) attribute used for conditioning the method caching.
|
||||
* <p>Default is "", meaning the method is always cached.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -17,31 +17,39 @@
|
|||
package org.springframework.cache.annotation;
|
||||
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
|
||||
/**
|
||||
* Interface to be implemented by @{@link org.springframework.context.annotation.Configuration
|
||||
* Configuration} classes annotated with @{@link EnableCaching} that wish or need to
|
||||
* specify explicitly the {@link CacheManager} and {@link KeyGenerator} beans to be used
|
||||
* for annotation-driven cache management.
|
||||
* specify explicitly how caches are resolved and how keys are generated for annotation-driven
|
||||
* cache management. Consider extending {@link CachingConfigurerSupport}, which provides a
|
||||
* stub implementation of all interface methods.
|
||||
*
|
||||
* <p>See @{@link EnableCaching} for general examples and context; see
|
||||
* {@link #cacheManager()} and {@link #keyGenerator()} for detailed instructions.
|
||||
* {@link #cacheManager()}, {@link #cacheResolver()} and {@link #keyGenerator()}
|
||||
* for detailed instructions.
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see EnableCaching
|
||||
* @see CachingConfigurerSupport
|
||||
*/
|
||||
public interface CachingConfigurer {
|
||||
|
||||
/**
|
||||
* Return the cache manager bean to use for annotation-driven cache management.
|
||||
* Implementations must explicitly declare
|
||||
* Return the cache manager bean to use for annotation-driven cache
|
||||
* management. A default {@link CacheResolver} will be initialized
|
||||
* behind the scene with this cache manager. For more fine-grained
|
||||
* management of the cache resolution, consider setting the
|
||||
* {@link CacheResolver} directly.
|
||||
* <p>Implementations must explicitly declare
|
||||
* {@link org.springframework.context.annotation.Bean @Bean}, e.g.
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableCaching
|
||||
* public class AppConfig implements CachingConfigurer {
|
||||
* public class AppConfig extends CachingConfigurerSupport {
|
||||
* @Bean // important!
|
||||
* @Override
|
||||
* public CacheManager cacheManager() {
|
||||
|
|
@ -54,6 +62,28 @@ public interface CachingConfigurer {
|
|||
*/
|
||||
CacheManager cacheManager();
|
||||
|
||||
/**
|
||||
* Return the {@link CacheResolver} bean to use to resolve regular caches for
|
||||
* annotation-driven cache management. This is an alternative option to set
|
||||
* the {@link CacheManager} to use.
|
||||
* <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 CacheResolver cacheResolver() {
|
||||
* // configure and return CacheResolver instance
|
||||
* }
|
||||
* // ...
|
||||
* }
|
||||
* </pre>
|
||||
* See {@link EnableCaching} for more complete examples.
|
||||
*/
|
||||
CacheResolver cacheResolver();
|
||||
|
||||
/**
|
||||
* Return the key generator bean to use for annotation-driven cache management.
|
||||
* Implementations must explicitly declare
|
||||
|
|
@ -61,7 +91,7 @@ public interface CachingConfigurer {
|
|||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableCaching
|
||||
* public class AppConfig implements CachingConfigurer {
|
||||
* public class AppConfig extends CachingConfigurerSupport {
|
||||
* @Bean // important!
|
||||
* @Override
|
||||
* public KeyGenerator keyGenerator() {
|
||||
|
|
|
|||
48
spring-context/src/main/java/org/springframework/cache/annotation/CachingConfigurerSupport.java
vendored
Normal file
48
spring-context/src/main/java/org/springframework/cache/annotation/CachingConfigurerSupport.java
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.annotation;
|
||||
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.interceptor.CacheResolver;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
|
||||
/**
|
||||
* An implementation of {@link CachingConfigurer} with empty methods allowing
|
||||
* sub-classes to override only the methods they're interested in.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see CachingConfigurer
|
||||
*/
|
||||
public class CachingConfigurerSupport implements CachingConfigurer {
|
||||
|
||||
@Override
|
||||
public CacheManager cacheManager() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyGenerator keyGenerator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheResolver cacheResolver() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -59,7 +59,10 @@ public class ProxyCachingConfiguration extends AbstractCachingConfiguration<Cach
|
|||
public CacheInterceptor cacheInterceptor() {
|
||||
CacheInterceptor interceptor = new CacheInterceptor();
|
||||
interceptor.setCacheOperationSources(cacheOperationSource());
|
||||
if (this.cacheManager != null) {
|
||||
if (this.cacheResolver != null) {
|
||||
interceptor.setCacheResolver(this.cacheResolver);
|
||||
}
|
||||
else if (this.cacheManager != null) {
|
||||
interceptor.setCacheManager(this.cacheManager);
|
||||
}
|
||||
if (this.keyGenerator != null) {
|
||||
|
|
|
|||
|
|
@ -105,6 +105,7 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
|
|||
cuo.setKey(caching.key());
|
||||
cuo.setKeyGenerator(caching.keyGenerator());
|
||||
cuo.setCacheManager(caching.cacheManager());
|
||||
cuo.setCacheResolver(caching.cacheResolver());
|
||||
cuo.setName(ae.toString());
|
||||
|
||||
defaultConfig.applyDefault(cuo);
|
||||
|
|
@ -121,6 +122,7 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
|
|||
ceo.setKey(caching.key());
|
||||
ceo.setKeyGenerator(caching.keyGenerator());
|
||||
ceo.setCacheManager(caching.cacheManager());
|
||||
ceo.setCacheResolver(caching.cacheResolver());
|
||||
ceo.setCacheWide(caching.allEntries());
|
||||
ceo.setBeforeInvocation(caching.beforeInvocation());
|
||||
ceo.setName(ae.toString());
|
||||
|
|
@ -140,6 +142,7 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
|
|||
cuo.setKey(caching.key());
|
||||
cuo.setKeyGenerator(caching.keyGenerator());
|
||||
cuo.setCacheManager(caching.cacheManager());
|
||||
cuo.setCacheResolver(caching.cacheResolver());
|
||||
cuo.setName(ae.toString());
|
||||
|
||||
defaultConfig.applyDefault(cuo);
|
||||
|
|
@ -186,8 +189,8 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
|
|||
DefaultCacheConfig getDefaultCacheConfig(Class<?> target) {
|
||||
final CacheConfig annotation = AnnotationUtils.getAnnotation(target, CacheConfig.class);
|
||||
if (annotation != null) {
|
||||
return new DefaultCacheConfig(annotation.cacheManager(),
|
||||
annotation.keyGenerator(), annotation.cacheNames());
|
||||
return new DefaultCacheConfig(annotation.cacheNames(), annotation.keyGenerator(),
|
||||
annotation.cacheManager(), annotation.cacheResolver());
|
||||
}
|
||||
return new DefaultCacheConfig();
|
||||
}
|
||||
|
|
@ -228,9 +231,16 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
|
|||
"These attributes are mutually exclusive: either set the SpEL expression used to" +
|
||||
"compute the key at runtime or set the name of the KeyGenerator bean to use.");
|
||||
}
|
||||
if (StringUtils.hasText(operation.getCacheManager()) && StringUtils.hasText(operation.getCacheResolver())) {
|
||||
throw new IllegalStateException("Invalid cache annotation configuration on '"
|
||||
+ ae.toString() + "'. Both 'cacheManager' and 'cacheResolver' attributes have been set. " +
|
||||
"These attributes are mutually exclusive: the cache manager is used to configure a" +
|
||||
"default cache resolver if none is set. If a cache resolver is set, the cache manager" +
|
||||
"won't be used.");
|
||||
}
|
||||
if (operation.getCacheNames().isEmpty()) {
|
||||
throw new IllegalStateException("No cache names could be detected on '"
|
||||
+ ae.toString()+ "'. Make sure to set the value parameter on the annotation or" +
|
||||
+ ae.toString() + "'. Make sure to set the value parameter on the annotation or " +
|
||||
"declare a @CacheConfig at the class-level with the default cache name(s) to use.");
|
||||
}
|
||||
}
|
||||
|
|
@ -245,22 +255,29 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
|
|||
return SpringCacheAnnotationParser.class.hashCode();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provides default settings for a given set of cache operations.
|
||||
*/
|
||||
static class DefaultCacheConfig {
|
||||
private final String cacheManager;
|
||||
private final String keyGenerator;
|
||||
private final String[] cacheNames;
|
||||
|
||||
private DefaultCacheConfig(String cacheManager, String keyGenerator, String[] cacheNames) {
|
||||
this.cacheManager = cacheManager;
|
||||
this.keyGenerator = keyGenerator;
|
||||
private final String keyGenerator;
|
||||
|
||||
private final String cacheManager;
|
||||
|
||||
private final String cacheResolver;
|
||||
|
||||
private DefaultCacheConfig(String[] cacheNames, String keyGenerator,
|
||||
String cacheManager, String cacheResolver) {
|
||||
this.cacheNames = cacheNames;
|
||||
this.keyGenerator = keyGenerator;
|
||||
this.cacheManager = cacheManager;
|
||||
this.cacheResolver = cacheResolver;
|
||||
}
|
||||
|
||||
public DefaultCacheConfig() {
|
||||
this(null, null, null);
|
||||
this(null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -269,17 +286,29 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
|
|||
* @param operation the operation to update
|
||||
*/
|
||||
public void applyDefault(CacheOperation operation) {
|
||||
if (!StringUtils.hasText(operation.getCacheManager()) && StringUtils.hasText(cacheManager)) {
|
||||
operation.setCacheManager(cacheManager);
|
||||
if (operation.getCacheNames().isEmpty() && cacheNames != null) {
|
||||
operation.setCacheNames(cacheNames);
|
||||
}
|
||||
if (!StringUtils.hasText(operation.getKey()) && !StringUtils.hasText(operation.getKeyGenerator())
|
||||
&& StringUtils.hasText(keyGenerator)) {
|
||||
operation.setKeyGenerator(keyGenerator);
|
||||
}
|
||||
if (operation.getCacheNames().isEmpty() && cacheNames != null) {
|
||||
operation.setCacheNames(cacheNames);
|
||||
|
||||
if (isSet(operation.getCacheManager()) || isSet(operation.getCacheResolver())) {
|
||||
// One of these is set so we should not inherit anything
|
||||
}
|
||||
else if (isSet(cacheResolver)) {
|
||||
operation.setCacheResolver(cacheResolver);
|
||||
}
|
||||
else if (isSet(cacheManager)) {
|
||||
operation.setCacheManager(cacheManager);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSet(String s) {
|
||||
return StringUtils.hasText(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
92
spring-context/src/main/java/org/springframework/cache/interceptor/BaseCacheResolver.java
vendored
Normal file
92
spring-context/src/main/java/org/springframework/cache/interceptor/BaseCacheResolver.java
vendored
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* 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 java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A base {@link CacheResolver} implementation that requires the concrete
|
||||
* implementation to provide the collection of cache name(s) based on the
|
||||
* invocation context.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public abstract class BaseCacheResolver implements CacheResolver, InitializingBean {
|
||||
|
||||
private CacheManager cacheManager;
|
||||
|
||||
protected BaseCacheResolver(CacheManager cacheManager) {
|
||||
this.cacheManager = cacheManager;
|
||||
}
|
||||
|
||||
protected BaseCacheResolver() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link CacheManager} that this instance should use.
|
||||
*/
|
||||
public void setCacheManager(CacheManager cacheManager) {
|
||||
this.cacheManager = cacheManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link CacheManager} that this instance use.
|
||||
*/
|
||||
public CacheManager getCacheManager() {
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(cacheManager, "CacheManager must not be null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
|
||||
Collection<String> cacheNames = getCacheNames(context);
|
||||
if (cacheNames == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
else {
|
||||
Collection<Cache> result = new ArrayList<Cache>();
|
||||
for (String cacheName : cacheNames) {
|
||||
Cache cache = cacheManager.getCache(cacheName);
|
||||
Assert.notNull(cache, "Cannot find cache named '" + cacheName + "' for " + context.getOperation());
|
||||
result.add(cache);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide the name of the cache(s) to resolve against the current cache manager.
|
||||
* <p>It is acceptable to return {@code null} to indicate that no cache could
|
||||
* be resolved for this invocation.
|
||||
*
|
||||
* @param context the context of the particular invocation
|
||||
* @return the cache name(s) to resolve or {@code null} if no cache should be resolved
|
||||
*/
|
||||
protected abstract Collection<String> getCacheNames(CacheOperationInvocationContext<?> context);
|
||||
|
||||
}
|
||||
|
|
@ -17,16 +17,15 @@
|
|||
package org.springframework.cache.interceptor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.aop.framework.AopProxyUtils;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
|
||||
|
|
@ -54,12 +53,12 @@ import org.springframework.util.StringUtils;
|
|||
* <p>Subclasses are responsible for calling methods in this class in
|
||||
* the correct order.
|
||||
*
|
||||
* <p>Uses the <b>Strategy</b> design pattern. A {@link CacheManager}
|
||||
* implementation will perform the actual cache management, and a
|
||||
* <p>Uses the <b>Strategy</b> design pattern. A {@link CacheResolver}
|
||||
* implementation will resolve the actual cache(s) to use, and a
|
||||
* {@link CacheOperationSource} is used for determining caching
|
||||
* operations.
|
||||
*
|
||||
* <p>A cache aspect is serializable if its {@code CacheManager} and
|
||||
* <p>A cache aspect is serializable if its {@code CacheResolver} and
|
||||
* {@code CacheOperationSource} are serializable.
|
||||
*
|
||||
* @author Costin Leau
|
||||
|
|
@ -74,34 +73,25 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
|||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final ExpressionEvaluator evaluator = new ExpressionEvaluator();
|
||||
/**
|
||||
* Cache of CacheOperationMetadata, keyed by {@link CacheOperationCacheKey}.
|
||||
*/
|
||||
private final Map<CacheOperationCacheKey, CacheOperationMetadata> metadataCache =
|
||||
new ConcurrentHashMap<CacheOperationCacheKey, CacheOperationMetadata>(1024);
|
||||
|
||||
private CacheManager cacheManager;
|
||||
private final ExpressionEvaluator evaluator = new ExpressionEvaluator();
|
||||
|
||||
private CacheOperationSource cacheOperationSource;
|
||||
|
||||
private KeyGenerator keyGenerator = new SimpleKeyGenerator();
|
||||
|
||||
private CacheResolver cacheResolver;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private boolean initialized = false;
|
||||
|
||||
|
||||
/**
|
||||
* Set the default {@link CacheManager} that this cache aspect should delegate to
|
||||
* if no specific cache manager has been set for the operation.
|
||||
*/
|
||||
public void setCacheManager(CacheManager cacheManager) {
|
||||
this.cacheManager = cacheManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default {@link CacheManager} that this cache aspect delegates to.
|
||||
*/
|
||||
public CacheManager getCacheManager() {
|
||||
return this.cacheManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set one or more cache operation sources which are used to find the cache
|
||||
* attributes. If more than one source is provided, they will be aggregated using a
|
||||
|
|
@ -137,14 +127,46 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
|||
return this.keyGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link CacheManager} to use to create a default {@link CacheResolver}. Replace
|
||||
* the current {@link CacheResolver}, if any.
|
||||
*
|
||||
* @see #setCacheResolver(CacheResolver)
|
||||
* @see SimpleCacheResolver
|
||||
*/
|
||||
public void setCacheManager(CacheManager cacheManager) {
|
||||
this.cacheResolver = new SimpleCacheResolver(cacheManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default {@link CacheResolver} that this cache aspect should delegate
|
||||
* to if no specific cache resolver has been set for the operation.
|
||||
* <p>The default resolver resolves the caches against their names and the
|
||||
* default cache manager.
|
||||
* @see #setCacheManager(org.springframework.cache.CacheManager)
|
||||
* @see SimpleCacheResolver
|
||||
*/
|
||||
public void setCacheResolver(CacheResolver cacheResolver) {
|
||||
Assert.notNull(cacheResolver);
|
||||
this.cacheResolver = cacheResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default {@link CacheResolver} that this cache aspect delegates to.
|
||||
*/
|
||||
public CacheResolver getCacheResolver() {
|
||||
return cacheResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
Assert.state(this.cacheManager != null, "Property 'cacheManager' is required");
|
||||
Assert.state(this.cacheOperationSource != null, "Property 'cacheOperationSources' is required: " +
|
||||
Assert.state(this.cacheResolver != null, "'cacheResolver' is required. Either set the cache resolver " +
|
||||
"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.applicationContext != null, "The application context was not injected as it should.");
|
||||
this.initialized = true;
|
||||
|
|
@ -165,21 +187,85 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
|||
return ClassUtils.getQualifiedMethodName(specificMethod);
|
||||
}
|
||||
|
||||
protected Collection<? extends Cache> getCaches(CacheOperation operation, CacheManager cacheManager) {
|
||||
Set<String> cacheNames = operation.getCacheNames();
|
||||
Collection<Cache> caches = new ArrayList<Cache>(cacheNames.size());
|
||||
for (String cacheName : cacheNames) {
|
||||
Cache cache = cacheManager.getCache(cacheName);
|
||||
Assert.notNull(cache, "Cannot find cache named '" + cacheName + "' for " + operation);
|
||||
caches.add(cache);
|
||||
protected Collection<? extends Cache> getCaches(CacheOperationInvocationContext<CacheOperation> context,
|
||||
CacheResolver cacheResolver) {
|
||||
Collection<? extends Cache> caches = cacheResolver.resolveCaches(context);
|
||||
if (caches.isEmpty()) {
|
||||
throw new IllegalStateException("No cache could be resolved for '" + context.getOperation()
|
||||
+ "' using resolver '" + cacheResolver
|
||||
+ "'. At least one cache should be provided per cache operation.");
|
||||
}
|
||||
return caches;
|
||||
}
|
||||
|
||||
protected CacheOperationContext getOperationContext(CacheOperation operation, Method method, Object[] args,
|
||||
Object target, Class<?> targetClass) {
|
||||
Object target, Class<?> targetClass) {
|
||||
|
||||
return new CacheOperationContext(operation, method, args, target, targetClass);
|
||||
CacheOperationMetadata metadata = getCacheOperationMetadata(operation, method, targetClass);
|
||||
return new CacheOperationContext(metadata, args, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link CacheOperationMetadata} for the specified operation.
|
||||
* <p>Resolve the {@link CacheResolver} and the {@link KeyGenerator} to be
|
||||
* used for the operation.
|
||||
* @param operation the operation
|
||||
* @param method the method on which the operation is invoked
|
||||
* @param targetClass the target type
|
||||
* @return the resolved metadata for the operation
|
||||
*/
|
||||
protected CacheOperationMetadata getCacheOperationMetadata(CacheOperation operation,
|
||||
Method method, Class<?> targetClass) {
|
||||
final CacheOperationCacheKey cacheKey = new CacheOperationCacheKey(operation, method, targetClass);
|
||||
CacheOperationMetadata metadata = metadataCache.get(cacheKey);
|
||||
if (metadata == null) {
|
||||
KeyGenerator operationKeyGenerator;
|
||||
if (StringUtils.hasText(operation.getKeyGenerator())) {
|
||||
operationKeyGenerator = getBean(operation.getKeyGenerator(), KeyGenerator.class);
|
||||
}
|
||||
else {
|
||||
operationKeyGenerator = getKeyGenerator();
|
||||
}
|
||||
CacheResolver operationCacheResolver;
|
||||
if (StringUtils.hasText(operation.getCacheResolver())) {
|
||||
operationCacheResolver = getBean(operation.getCacheResolver(), CacheResolver.class);
|
||||
}
|
||||
else if (StringUtils.hasText(operation.getCacheManager())) {
|
||||
CacheManager cacheManager = getBean(operation.getCacheManager(), CacheManager.class);
|
||||
operationCacheResolver = new SimpleCacheResolver(cacheManager);
|
||||
}
|
||||
else {
|
||||
operationCacheResolver = getCacheResolver();
|
||||
}
|
||||
metadata = new CacheOperationMetadata(operation, method, targetClass,
|
||||
operationKeyGenerator, operationCacheResolver);
|
||||
metadataCache.put(cacheKey, metadata);
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a bean with the specified name and type. Used to resolve services that
|
||||
* are referenced by name in a {@link CacheOperation}.
|
||||
*
|
||||
* @param beanName the name of the bean, as defined by the operation
|
||||
* @param expectedType type type for the bean
|
||||
* @return the bean matching that name
|
||||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException if such bean does not exist
|
||||
* @see CacheOperation#keyGenerator
|
||||
* @see CacheOperation#cacheManager
|
||||
* @see CacheOperation#cacheResolver
|
||||
*/
|
||||
protected <T> T getBean(String beanName, Class<T> expectedType) {
|
||||
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(
|
||||
applicationContext, expectedType, beanName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the cached metadata.
|
||||
*/
|
||||
protected void clearMetadataCache() {
|
||||
metadataCache.clear();
|
||||
}
|
||||
|
||||
protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
|
||||
|
|
@ -245,7 +331,7 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
|||
|
||||
private void processCacheEvicts(Collection<CacheOperationContext> contexts, boolean beforeInvocation, Object result) {
|
||||
for (CacheOperationContext context : contexts) {
|
||||
CacheEvictOperation operation = (CacheEvictOperation) context.operation;
|
||||
CacheEvictOperation operation = (CacheEvictOperation) context.metadata.operation;
|
||||
if (beforeInvocation == operation.isBeforeInvocation() && isConditionPassing(context, result)) {
|
||||
performCacheEvict(context, operation, result);
|
||||
}
|
||||
|
|
@ -272,7 +358,7 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
|||
private void logInvalidating(CacheOperationContext context, CacheEvictOperation operation, Object key) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Invalidating " + (key != null ? "cache key [" + key + "]" : "entire cache") +
|
||||
" for operation " + operation + " on method " + context.method);
|
||||
" for operation " + operation + " on method " + context.metadata.method);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -327,7 +413,8 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
|||
private boolean isConditionPassing(CacheOperationContext context, Object result) {
|
||||
boolean passing = context.isConditionPassing(result);
|
||||
if (!passing && logger.isTraceEnabled()) {
|
||||
logger.trace("Cache condition failed on method " + context.method + " for operation " + context.operation);
|
||||
logger.trace("Cache condition failed on method " + context.metadata.method +
|
||||
" for operation " + context.metadata.operation);
|
||||
}
|
||||
return passing;
|
||||
}
|
||||
|
|
@ -335,9 +422,9 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
|||
private Object generateKey(CacheOperationContext context, Object result) {
|
||||
Object key = context.generateKey(result);
|
||||
Assert.notNull(key, "Null key returned for cache operation (maybe you are using named params " +
|
||||
"on classes without debug info?) " + context.operation);
|
||||
"on classes without debug info?) " + context.metadata.operation);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Computed cache key " + key + " for operation " + context.operation);
|
||||
logger.trace("Computed cache key " + key + " for operation " + context.metadata.operation);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
|
@ -349,7 +436,7 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
|||
new LinkedMultiValueMap<Class<? extends CacheOperation>, CacheOperationContext>();
|
||||
|
||||
public CacheOperationContexts(Collection<? extends CacheOperation> operations,
|
||||
Method method, Object[] args, Object target, Class<?> targetClass) {
|
||||
Method method, Object[] args, Object target, Class<?> targetClass) {
|
||||
|
||||
for (CacheOperation operation : operations) {
|
||||
this.contexts.add(operation.getClass(), getOperationContext(operation, method, args, target, targetClass));
|
||||
|
|
@ -358,51 +445,70 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
|||
|
||||
public Collection<CacheOperationContext> get(Class<? extends CacheOperation> operationClass) {
|
||||
Collection<CacheOperationContext> result = this.contexts.get(operationClass);
|
||||
return (result != null ? result : Collections.<CacheOperationContext> emptyList());
|
||||
return (result != null ? result : Collections.<CacheOperationContext>emptyList());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected class CacheOperationContext {
|
||||
/**
|
||||
* Metadata of a cache operation that does not depend on a particular invocation
|
||||
* which makes it a good candidate for caching.
|
||||
*/
|
||||
protected static class CacheOperationMetadata {
|
||||
|
||||
private final CacheOperation operation;
|
||||
|
||||
private final Method method;
|
||||
private final Class<?> targetClass;
|
||||
private final KeyGenerator keyGenerator;
|
||||
private final CacheResolver cacheResolver;
|
||||
|
||||
public CacheOperationMetadata(CacheOperation operation, Method method,
|
||||
Class<?> targetClass, KeyGenerator keyGenerator,
|
||||
CacheResolver cacheResolver) {
|
||||
this.operation = operation;
|
||||
this.method = method;
|
||||
this.targetClass = targetClass;
|
||||
this.keyGenerator = keyGenerator;
|
||||
this.cacheResolver = cacheResolver;
|
||||
}
|
||||
}
|
||||
|
||||
protected class CacheOperationContext implements CacheOperationInvocationContext<CacheOperation> {
|
||||
|
||||
private final CacheOperationMetadata metadata;
|
||||
|
||||
private final Object[] args;
|
||||
|
||||
private final Object target;
|
||||
|
||||
private final Class<?> targetClass;
|
||||
|
||||
private final Collection<? extends Cache> caches;
|
||||
|
||||
private final KeyGenerator operationKeyGenerator;
|
||||
|
||||
private final CacheManager operationCacheManager;
|
||||
|
||||
public CacheOperationContext(CacheOperation operation, Method method,
|
||||
Object[] args, Object target, Class<?> targetClass) {
|
||||
this.operation = operation;
|
||||
this.method = method;
|
||||
this.args = extractArgs(method, args);
|
||||
public CacheOperationContext(CacheOperationMetadata metadata,
|
||||
Object[] args, Object target) {
|
||||
this.metadata = metadata;
|
||||
this.args = extractArgs(metadata.method, args);
|
||||
this.target = target;
|
||||
this.targetClass = targetClass;
|
||||
if (StringUtils.hasText(operation.getKeyGenerator())) { // TODO: exception mgt?
|
||||
this.operationKeyGenerator = BeanFactoryAnnotationUtils.qualifiedBeanOfType(
|
||||
applicationContext, KeyGenerator.class, operation.getKeyGenerator());
|
||||
}
|
||||
else {
|
||||
this.operationKeyGenerator = keyGenerator;
|
||||
}
|
||||
if (StringUtils.hasText(operation.getCacheManager())) {
|
||||
this.operationCacheManager = BeanFactoryAnnotationUtils.qualifiedBeanOfType(
|
||||
applicationContext, CacheManager.class, operation.getCacheManager());
|
||||
}
|
||||
else {
|
||||
this.operationCacheManager = cacheManager;
|
||||
}
|
||||
this.caches = CacheAspectSupport.this.getCaches(operation, operationCacheManager);
|
||||
this.caches = CacheAspectSupport.this.getCaches(this, metadata.cacheResolver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheOperation getOperation() {
|
||||
return metadata.operation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getMethod() {
|
||||
return metadata.method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
private Object[] extractArgs(Method method, Object[] args) {
|
||||
|
|
@ -417,24 +523,24 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
|||
}
|
||||
|
||||
protected boolean isConditionPassing(Object result) {
|
||||
if (StringUtils.hasText(this.operation.getCondition())) {
|
||||
if (StringUtils.hasText(this.metadata.operation.getCondition())) {
|
||||
EvaluationContext evaluationContext = createEvaluationContext(result);
|
||||
return evaluator.condition(this.operation.getCondition(), this.method, evaluationContext);
|
||||
return evaluator.condition(this.metadata.operation.getCondition(), this.metadata.method, evaluationContext);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean canPutToCache(Object value) {
|
||||
String unless = "";
|
||||
if (this.operation instanceof CacheableOperation) {
|
||||
unless = ((CacheableOperation) this.operation).getUnless();
|
||||
if (this.metadata.operation instanceof CacheableOperation) {
|
||||
unless = ((CacheableOperation) this.metadata.operation).getUnless();
|
||||
}
|
||||
else if (this.operation instanceof CachePutOperation) {
|
||||
unless = ((CachePutOperation) this.operation).getUnless();
|
||||
else if (this.metadata.operation instanceof CachePutOperation) {
|
||||
unless = ((CachePutOperation) this.metadata.operation).getUnless();
|
||||
}
|
||||
if (StringUtils.hasText(unless)) {
|
||||
EvaluationContext evaluationContext = createEvaluationContext(value);
|
||||
return !evaluator.unless(unless, this.method, evaluationContext);
|
||||
return !evaluator.unless(unless, this.metadata.method, evaluationContext);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -444,16 +550,16 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
|||
* @return generated key (null if none can be generated)
|
||||
*/
|
||||
protected Object generateKey(Object result) {
|
||||
if (StringUtils.hasText(this.operation.getKey())) {
|
||||
if (StringUtils.hasText(this.metadata.operation.getKey())) {
|
||||
EvaluationContext evaluationContext = createEvaluationContext(result);
|
||||
return evaluator.key(this.operation.getKey(), this.method, evaluationContext);
|
||||
return evaluator.key(this.metadata.operation.getKey(), this.metadata.method, evaluationContext);
|
||||
}
|
||||
return operationKeyGenerator.generate(this.target, this.method, this.args);
|
||||
return metadata.keyGenerator.generate(this.target, this.metadata.method, this.args);
|
||||
}
|
||||
|
||||
private EvaluationContext createEvaluationContext(Object result) {
|
||||
return evaluator.createEvaluationContext(
|
||||
this.caches, this.method, this.args, this.target, this.targetClass, result);
|
||||
this.caches, this.metadata.method, this.args, this.target, this.metadata.targetClass, result);
|
||||
}
|
||||
|
||||
protected Collection<? extends Cache> getCaches() {
|
||||
|
|
@ -482,4 +588,31 @@ public abstract class CacheAspectSupport implements InitializingBean, Applicatio
|
|||
}
|
||||
}
|
||||
|
||||
private static class CacheOperationCacheKey {
|
||||
|
||||
private final CacheOperation cacheOperation;
|
||||
private final MethodCacheKey methodCacheKey;
|
||||
|
||||
private CacheOperationCacheKey(CacheOperation cacheOperation, Method method, Class<?> targetClass) {
|
||||
this.cacheOperation = cacheOperation;
|
||||
this.methodCacheKey = new MethodCacheKey(method, targetClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
CacheOperationCacheKey that = (CacheOperationCacheKey) o;
|
||||
return cacheOperation.equals(that.cacheOperation)
|
||||
&& methodCacheKey.equals(that.methodCacheKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = cacheOperation.hashCode();
|
||||
result = 31 * result + methodCacheKey.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import org.springframework.util.Assert;
|
|||
* @author Costin Leau
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public abstract class CacheOperation {
|
||||
public abstract class CacheOperation implements BasicCacheOperation {
|
||||
|
||||
private Set<String> cacheNames = Collections.emptySet();
|
||||
|
||||
|
|
@ -40,9 +40,12 @@ public abstract class CacheOperation {
|
|||
|
||||
private String cacheManager = "";
|
||||
|
||||
private String cacheResolver = "";
|
||||
|
||||
private String name = "";
|
||||
|
||||
|
||||
@Override
|
||||
public Set<String> getCacheNames() {
|
||||
return cacheNames;
|
||||
}
|
||||
|
|
@ -63,6 +66,10 @@ public abstract class CacheOperation {
|
|||
return cacheManager;
|
||||
}
|
||||
|
||||
public String getCacheResolver() {
|
||||
return cacheResolver;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
|
@ -100,6 +107,11 @@ public abstract class CacheOperation {
|
|||
this.cacheManager = cacheManager;
|
||||
}
|
||||
|
||||
public void setCacheResolver(String cacheResolver) {
|
||||
Assert.notNull(cacheManager);
|
||||
this.cacheResolver = cacheResolver;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
Assert.hasText(name);
|
||||
this.name = name;
|
||||
|
|
@ -151,6 +163,8 @@ public abstract class CacheOperation {
|
|||
result.append(this.keyGenerator);
|
||||
result.append("' | cacheManager='");
|
||||
result.append(this.cacheManager);
|
||||
result.append("' | cacheResolver='");
|
||||
result.append(this.cacheResolver);
|
||||
result.append("' | condition='");
|
||||
result.append(this.condition);
|
||||
result.append("'");
|
||||
|
|
|
|||
|
|
@ -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.interceptor;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
|
@ -21,7 +37,6 @@ public final class MethodCacheKey {
|
|||
|
||||
public MethodCacheKey(Method method, Class<?> targetClass) {
|
||||
Assert.notNull(method, "method must be set.");
|
||||
Assert.notNull(targetClass, "targetClass must be set.");
|
||||
this.method = method;
|
||||
this.targetClass = targetClass;
|
||||
}
|
||||
|
|
|
|||
56
spring-context/src/main/java/org/springframework/cache/interceptor/NamedCacheResolver.java
vendored
Normal file
56
spring-context/src/main/java/org/springframework/cache/interceptor/NamedCacheResolver.java
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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 java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.cache.CacheManager;
|
||||
|
||||
/**
|
||||
* A {@link CacheResolver} that forces the resolution to a configurable
|
||||
* collection of name(s) against a given {@link CacheManager}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class NamedCacheResolver extends BaseCacheResolver {
|
||||
|
||||
private Collection<String> cacheNames;
|
||||
|
||||
public NamedCacheResolver(CacheManager cacheManager, String... cacheNames) {
|
||||
super(cacheManager);
|
||||
this.cacheNames = new ArrayList<String>(Arrays.asList(cacheNames));
|
||||
}
|
||||
|
||||
public NamedCacheResolver() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cache name(s) that this resolver should use.
|
||||
*/
|
||||
public void setCacheNames(Collection<String> cacheNames) {
|
||||
this.cacheNames = cacheNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> context) {
|
||||
return cacheNames;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -16,39 +16,29 @@
|
|||
|
||||
package org.springframework.cache.interceptor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A simple {@link CacheResolver} that resolves the {@link Cache} instance(s)
|
||||
* based on a configurable {@link CacheManager} and the name of the
|
||||
* cache(s): {@link BasicCacheOperation#getCacheNames()}
|
||||
* cache(s) as provided by {@link BasicCacheOperation#getCacheNames() getCacheNames()}
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 4.1
|
||||
* @see BasicCacheOperation#getCacheNames()
|
||||
*/
|
||||
public class SimpleCacheResolver implements CacheResolver {
|
||||
|
||||
private final CacheManager cacheManager;
|
||||
public class SimpleCacheResolver extends BaseCacheResolver {
|
||||
|
||||
public SimpleCacheResolver(CacheManager cacheManager) {
|
||||
this.cacheManager = cacheManager;
|
||||
super(cacheManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
|
||||
Collection<Cache> result = new ArrayList<Cache>();
|
||||
for (String cacheName : context.getOperation().getCacheNames()) {
|
||||
Cache cache = cacheManager.getCache(cacheName);
|
||||
Assert.notNull(cache, "Cannot find cache named '" + cacheName + "' for " + context.getOperation());
|
||||
result.add(cache);
|
||||
}
|
||||
return result;
|
||||
protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> context) {
|
||||
return context.getOperation().getCacheNames();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCache;
|
||||
import org.springframework.cache.support.SimpleCacheManager;
|
||||
|
||||
/**
|
||||
* General cache-related test utilities.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class CacheTestUtils {
|
||||
|
||||
/**
|
||||
* Create a {@link SimpleCacheManager} with the specified cache(s).
|
||||
* @param cacheNames the names of the caches to create
|
||||
*/
|
||||
public static CacheManager createSimpleCacheManager(String... cacheNames) {
|
||||
SimpleCacheManager result = new SimpleCacheManager();
|
||||
List<Cache> caches = new ArrayList<Cache>();
|
||||
for (String cacheName : cacheNames) {
|
||||
caches.add(new ConcurrentMapCache(cacheName));
|
||||
}
|
||||
result.setCaches(caches);
|
||||
result.afterPropertiesSet();
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -138,24 +138,55 @@ public class AnnotationCacheOperationSourceTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void fullClassLevelWithCustomKeyManager() {
|
||||
Collection<CacheOperation> ops = getOps(AnnotatedClassWithFullDefault.class, "methodLevelKeyGenerator", 1);
|
||||
public void testCustomCacheResolver() {
|
||||
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "customCacheResolver", 1);
|
||||
CacheOperation cacheOperation = ops.iterator().next();
|
||||
assertSharedConfig(cacheOperation, "classCacheManager", "custom", "classCacheName");
|
||||
assertEquals("Custom cache resolver not set", "custom", cacheOperation.getCacheResolver());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fullClassLevelWithCustomCacheManager() {
|
||||
Collection<CacheOperation> ops = getOps(AnnotatedClassWithFullDefault.class, "methodLevelCacheManager", 1);
|
||||
public void testCustomCacheResolverInherited() {
|
||||
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "customCacheResolverInherited", 1);
|
||||
CacheOperation cacheOperation = ops.iterator().next();
|
||||
assertSharedConfig(cacheOperation, "custom", "classKeyGenerator", "classCacheName");
|
||||
assertEquals("Custom cache resolver not set", "custom", cacheOperation.getCacheResolver());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCacheResolverAndCacheManagerCannotBeSetTogether() {
|
||||
try {
|
||||
getOps(AnnotatedClass.class, "invalidCacheResolverAndCacheManagerSet");
|
||||
fail("Should have failed to parse @Cacheable annotation");
|
||||
} catch (IllegalStateException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fullClassLevelWithCustomCacheName() {
|
||||
Collection<CacheOperation> ops = getOps(AnnotatedClassWithFullDefault.class, "methodLevelCacheName", 1);
|
||||
CacheOperation cacheOperation = ops.iterator().next();
|
||||
assertSharedConfig(cacheOperation, "classCacheManager", "classKeyGenerator", "custom");
|
||||
assertSharedConfig(cacheOperation, "classKeyGenerator", "", "classCacheResolver", "custom");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fullClassLevelWithCustomKeyManager() {
|
||||
Collection<CacheOperation> ops = getOps(AnnotatedClassWithFullDefault.class, "methodLevelKeyGenerator", 1);
|
||||
CacheOperation cacheOperation = ops.iterator().next();
|
||||
assertSharedConfig(cacheOperation, "custom", "", "classCacheResolver" , "classCacheName");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fullClassLevelWithCustomCacheManager() {
|
||||
Collection<CacheOperation> ops = getOps(AnnotatedClassWithFullDefault.class, "methodLevelCacheManager", 1);
|
||||
CacheOperation cacheOperation = ops.iterator().next();
|
||||
assertSharedConfig(cacheOperation, "classKeyGenerator", "custom", "", "classCacheName");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fullClassLevelWithCustomCacheResolver() {
|
||||
Collection<CacheOperation> ops = getOps(AnnotatedClassWithFullDefault.class, "methodLevelCacheResolver", 1);
|
||||
CacheOperation cacheOperation = ops.iterator().next();
|
||||
assertSharedConfig(cacheOperation, "classKeyGenerator", "", "custom" , "classCacheName");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -168,20 +199,42 @@ public class AnnotationCacheOperationSourceTests {
|
|||
public void customClassLevelWithCustomCacheName() {
|
||||
Collection<CacheOperation> ops = getOps(AnnotatedClassWithCustomDefault.class, "methodLevelCacheName", 1);
|
||||
CacheOperation cacheOperation = ops.iterator().next();
|
||||
assertSharedConfig(cacheOperation, "classCacheManager", "classKeyGenerator", "custom");
|
||||
assertSharedConfig(cacheOperation, "classKeyGenerator", "", "classCacheResolver", "custom");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void severalCacheConfigUseClosest() {
|
||||
Collection<CacheOperation> ops = getOps(MultipleCacheConfig.class, "multipleCacheConfig");
|
||||
CacheOperation cacheOperation = ops.iterator().next();
|
||||
assertSharedConfig(cacheOperation, "", "", "myCache");
|
||||
assertSharedConfig(cacheOperation, "", "", "", "myCache");
|
||||
}
|
||||
|
||||
private void assertSharedConfig(CacheOperation actual, String cacheManager,
|
||||
String keyGenerator, String... cacheNames) {
|
||||
assertEquals("Wrong cache manager", cacheManager, actual.getCacheManager());
|
||||
@Test
|
||||
public void partialClassLevelWithCustomCacheManager() {
|
||||
Collection<CacheOperation> ops = getOps(AnnotatedClassWithSomeDefault.class, "methodLevelCacheManager", 1);
|
||||
CacheOperation cacheOperation = ops.iterator().next();
|
||||
assertSharedConfig(cacheOperation, "classKeyGenerator", "custom", "", "classCacheName");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void partialClassLevelWithCustomCacheResolver() {
|
||||
Collection<CacheOperation> ops = getOps(AnnotatedClassWithSomeDefault.class, "methodLevelCacheResolver", 1);
|
||||
CacheOperation cacheOperation = ops.iterator().next();
|
||||
assertSharedConfig(cacheOperation, "classKeyGenerator", "", "custom", "classCacheName");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void partialClassLevelWithNoCustomization() {
|
||||
Collection<CacheOperation> ops = getOps(AnnotatedClassWithSomeDefault.class, "noCustomization", 1);
|
||||
CacheOperation cacheOperation = ops.iterator().next();
|
||||
assertSharedConfig(cacheOperation, "classKeyGenerator", "classCacheManager", "", "classCacheName");
|
||||
}
|
||||
|
||||
private void assertSharedConfig(CacheOperation actual, String keyGenerator, String cacheManager,
|
||||
String cacheResolver, String... cacheNames) {
|
||||
assertEquals("Wrong key manager", keyGenerator, actual.getKeyGenerator());
|
||||
assertEquals("Wrong cache manager", cacheManager, actual.getCacheManager());
|
||||
assertEquals("Wrong cache resolver", cacheResolver, actual.getCacheResolver());
|
||||
for (String cacheName : cacheNames) {
|
||||
assertTrue("Cache '"+cacheName+"' not found (got "+actual.getCacheNames(),
|
||||
actual.getCacheNames().contains(cacheName));
|
||||
|
|
@ -211,6 +264,10 @@ public class AnnotationCacheOperationSourceTests {
|
|||
public void customCacheManager() {
|
||||
}
|
||||
|
||||
@Cacheable(value = "test", cacheResolver = "custom")
|
||||
public void customCacheResolver() {
|
||||
}
|
||||
|
||||
@EvictFoo
|
||||
public void singleStereotype() {
|
||||
}
|
||||
|
|
@ -237,15 +294,28 @@ public class AnnotationCacheOperationSourceTests {
|
|||
public void customCacheManagerInherited() {
|
||||
}
|
||||
|
||||
@CacheableFooCustomCacheResolver
|
||||
public void customCacheResolverInherited() {
|
||||
}
|
||||
|
||||
@Cacheable(value = "test", cacheManager = "custom", cacheResolver = "custom")
|
||||
public void invalidCacheResolverAndCacheManagerSet() {
|
||||
}
|
||||
|
||||
@Cacheable // cache name can be inherited from CacheConfig. There's none here
|
||||
public void noCacheNameSpecified() {
|
||||
}
|
||||
}
|
||||
|
||||
@CacheConfig(cacheNames = "classCacheName",
|
||||
cacheManager = "classCacheManager", keyGenerator = "classKeyGenerator")
|
||||
keyGenerator = "classKeyGenerator",
|
||||
cacheManager = "classCacheManager", cacheResolver = "classCacheResolver")
|
||||
private static class AnnotatedClassWithFullDefault {
|
||||
|
||||
@Cacheable("custom")
|
||||
public void methodLevelCacheName() {
|
||||
}
|
||||
|
||||
@Cacheable(keyGenerator = "custom")
|
||||
public void methodLevelKeyGenerator() {
|
||||
}
|
||||
|
|
@ -254,8 +324,8 @@ public class AnnotationCacheOperationSourceTests {
|
|||
public void methodLevelCacheManager() {
|
||||
}
|
||||
|
||||
@Cacheable("custom")
|
||||
public void methodLevelCacheName() {
|
||||
@Cacheable(cacheResolver = "custom")
|
||||
public void methodLevelCacheResolver() {
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -267,6 +337,25 @@ public class AnnotationCacheOperationSourceTests {
|
|||
}
|
||||
}
|
||||
|
||||
@CacheConfig(cacheNames = "classCacheName",
|
||||
keyGenerator = "classKeyGenerator",
|
||||
cacheManager = "classCacheManager")
|
||||
private static class AnnotatedClassWithSomeDefault {
|
||||
|
||||
@Cacheable(cacheManager = "custom")
|
||||
public void methodLevelCacheManager() {
|
||||
}
|
||||
|
||||
@Cacheable(cacheResolver = "custom")
|
||||
public void methodLevelCacheResolver() {
|
||||
}
|
||||
|
||||
@Cacheable
|
||||
public void noCustomization() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@CacheConfigFoo
|
||||
@CacheConfig(cacheNames = "myCache") // multiple sources
|
||||
private static class MultipleCacheConfig {
|
||||
|
|
@ -292,7 +381,12 @@ public class AnnotationCacheOperationSourceTests {
|
|||
@Target(ElementType.METHOD)
|
||||
@Cacheable(value = "foo", cacheManager = "custom")
|
||||
public @interface CacheableFooCustomCacheManager {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@Cacheable(value = "foo", cacheResolver = "custom")
|
||||
public @interface CacheableFooCustomCacheResolver {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
|
|
@ -309,7 +403,8 @@ public class AnnotationCacheOperationSourceTests {
|
|||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@CacheConfig(cacheManager = "classCacheManager", keyGenerator = "classKeyGenerator")
|
||||
@CacheConfig(keyGenerator = "classKeyGenerator",
|
||||
cacheManager = "classCacheManager", cacheResolver = "classCacheResolver")
|
||||
public @interface CacheConfigFoo {
|
||||
}
|
||||
}
|
||||
|
|
@ -22,13 +22,14 @@ import static org.junit.Assert.*;
|
|||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.aop.framework.AopProxyUtils;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
/**
|
||||
* Abstract annotation test (containing several reusable methods).
|
||||
|
|
@ -40,7 +41,7 @@ import org.springframework.context.ApplicationContext;
|
|||
*/
|
||||
public abstract class AbstractAnnotationTests {
|
||||
|
||||
protected ApplicationContext ctx;
|
||||
protected ConfigurableApplicationContext ctx;
|
||||
|
||||
protected CacheableService<?> cs;
|
||||
|
||||
|
|
@ -51,7 +52,7 @@ public abstract class AbstractAnnotationTests {
|
|||
protected CacheManager customCm;
|
||||
|
||||
/** @return a refreshed application context */
|
||||
protected abstract ApplicationContext getApplicationContext();
|
||||
protected abstract ConfigurableApplicationContext getApplicationContext();
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
|
|
@ -67,6 +68,11 @@ public abstract class AbstractAnnotationTests {
|
|||
assertTrue(cn.contains("primary"));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
public void testCacheable(CacheableService<?> service) throws Exception {
|
||||
Object o1 = new Object();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -20,7 +20,7 @@ import static org.junit.Assert.assertSame;
|
|||
|
||||
import org.junit.Test;
|
||||
import org.springframework.cache.interceptor.CacheInterceptor;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.GenericXmlApplicationContext;
|
||||
|
||||
/**
|
||||
|
|
@ -30,7 +30,7 @@ import org.springframework.context.support.GenericXmlApplicationContext;
|
|||
public class AnnotationNamespaceDrivenTests extends AbstractAnnotationTests {
|
||||
|
||||
@Override
|
||||
protected ApplicationContext getApplicationContext() {
|
||||
protected ConfigurableApplicationContext getApplicationContext() {
|
||||
return new GenericXmlApplicationContext(
|
||||
"/org/springframework/cache/config/annotationDrivenCacheNamespace.xml");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package org.springframework.cache.config;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.GenericXmlApplicationContext;
|
||||
|
||||
/**
|
||||
|
|
@ -26,8 +26,9 @@ import org.springframework.context.support.GenericXmlApplicationContext;
|
|||
public class AnnotationTests extends AbstractAnnotationTests {
|
||||
|
||||
@Override
|
||||
protected ApplicationContext getApplicationContext() {
|
||||
protected ConfigurableApplicationContext getApplicationContext() {
|
||||
return new GenericXmlApplicationContext(
|
||||
"/org/springframework/cache/config/annotationDrivenCacheConfig.xml");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -19,7 +19,7 @@ package org.springframework.cache.config;
|
|||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cache.interceptor.CacheInterceptor;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.support.GenericXmlApplicationContext;
|
||||
|
||||
/**
|
||||
|
|
@ -29,7 +29,7 @@ import org.springframework.context.support.GenericXmlApplicationContext;
|
|||
public class CacheAdviceNamespaceTests extends AbstractAnnotationTests {
|
||||
|
||||
@Override
|
||||
protected ApplicationContext getApplicationContext() {
|
||||
protected ConfigurableApplicationContext getApplicationContext() {
|
||||
return new GenericXmlApplicationContext(
|
||||
"/org/springframework/cache/config/cache-advice.xml");
|
||||
}
|
||||
|
|
@ -39,4 +39,5 @@ public class CacheAdviceNamespaceTests extends AbstractAnnotationTests {
|
|||
CacheInterceptor bean = ctx.getBean("cacheAdviceClass", CacheInterceptor.class);
|
||||
Assert.assertSame(ctx.getBean("keyGenerator"), bean.getKeyGenerator());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,22 +16,21 @@
|
|||
|
||||
package org.springframework.cache.config;
|
||||
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Arrays;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.annotation.CachingConfigurer;
|
||||
import org.springframework.cache.CacheTestUtils;
|
||||
import org.springframework.cache.annotation.CachingConfigurerSupport;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCache;
|
||||
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.SimpleCacheResolver;
|
||||
import org.springframework.cache.support.NoOpCacheManager;
|
||||
import org.springframework.cache.support.SimpleCacheManager;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
|
@ -46,7 +45,7 @@ public class EnableCachingTests extends AbstractAnnotationTests {
|
|||
|
||||
/** hook into superclass suite of tests */
|
||||
@Override
|
||||
protected ApplicationContext getApplicationContext() {
|
||||
protected ConfigurableApplicationContext getApplicationContext() {
|
||||
return new AnnotationConfigApplicationContext(EnableCachingConfig.class);
|
||||
}
|
||||
|
||||
|
|
@ -111,19 +110,38 @@ public class EnableCachingTests extends AbstractAnnotationTests {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyConfigSupport() {
|
||||
ConfigurableApplicationContext context =
|
||||
new AnnotationConfigApplicationContext(EmptyConfigSupportConfig.class);
|
||||
|
||||
CacheInterceptor ci = context.getBean(CacheInterceptor.class);
|
||||
assertNotNull(ci.getCacheResolver());
|
||||
assertEquals(SimpleCacheResolver.class, ci.getCacheResolver().getClass());
|
||||
assertSame(context.getBean(CacheManager.class),
|
||||
((SimpleCacheResolver)ci.getCacheResolver()).getCacheManager());
|
||||
context.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bothSetOnlyResolverIsUsed() {
|
||||
ConfigurableApplicationContext context =
|
||||
new AnnotationConfigApplicationContext(FullCachingConfig.class);
|
||||
|
||||
CacheInterceptor ci = context.getBean(CacheInterceptor.class);
|
||||
assertSame(context.getBean("cacheResolver"), ci.getCacheResolver());
|
||||
assertSame(context.getBean("keyGenerator"), ci.getKeyGenerator());
|
||||
context.close();
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
static class EnableCachingConfig implements CachingConfigurer {
|
||||
static class EnableCachingConfig extends CachingConfigurerSupport {
|
||||
@Override
|
||||
@Bean
|
||||
public CacheManager cacheManager() {
|
||||
SimpleCacheManager cm = new SimpleCacheManager();
|
||||
cm.setCaches(Arrays.asList(
|
||||
new ConcurrentMapCache("default"),
|
||||
new ConcurrentMapCache("primary"),
|
||||
new ConcurrentMapCache("secondary")));
|
||||
return cm;
|
||||
return CacheTestUtils.createSimpleCacheManager("default", "primary", "secondary");
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
@ -149,9 +167,7 @@ public class EnableCachingTests extends AbstractAnnotationTests {
|
|||
|
||||
@Bean
|
||||
public CacheManager customCacheManager() {
|
||||
SimpleCacheManager cm = new SimpleCacheManager();
|
||||
cm.setCaches(Arrays.asList(new ConcurrentMapCache("default")));
|
||||
return cm;
|
||||
return CacheTestUtils.createSimpleCacheManager("default");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -182,7 +198,7 @@ public class EnableCachingTests extends AbstractAnnotationTests {
|
|||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
static class MultiCacheManagerConfigurer implements CachingConfigurer {
|
||||
static class MultiCacheManagerConfigurer extends CachingConfigurerSupport {
|
||||
@Bean
|
||||
public CacheManager cm1() { return new NoOpCacheManager(); }
|
||||
@Bean
|
||||
|
|
@ -197,4 +213,37 @@ public class EnableCachingTests extends AbstractAnnotationTests {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
static class EmptyConfigSupportConfig extends CachingConfigurerSupport {
|
||||
|
||||
@Bean
|
||||
public CacheManager cm() {
|
||||
return new NoOpCacheManager();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
static class FullCachingConfig extends CachingConfigurerSupport {
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public CacheManager cacheManager() {
|
||||
return new NoOpCacheManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public KeyGenerator keyGenerator() {
|
||||
return new SomeKeyGenerator();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public CacheResolver cacheResolver() {
|
||||
return new NamedCacheResolver(cacheManager(), "foo");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,290 @@
|
|||
/*
|
||||
* 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.junit.Assert.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.CacheTestUtils;
|
||||
import org.springframework.cache.annotation.CacheConfig;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.cache.annotation.CachingConfigurerSupport;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Provides various {@link CacheResolver} customisations scenario
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class CacheResolverCustomisationTests {
|
||||
|
||||
private CacheManager cacheManager;
|
||||
|
||||
private CacheManager anotherCacheManager;
|
||||
|
||||
private SimpleService simpleService;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
|
||||
this.cacheManager = context.getBean("cacheManager", CacheManager.class);
|
||||
this.anotherCacheManager = context.getBean("anotherCacheManager", CacheManager.class);
|
||||
|
||||
this.simpleService = context.getBean(SimpleService.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noCustomization() {
|
||||
Cache cache = cacheManager.getCache("default");
|
||||
|
||||
Object key = new Object();
|
||||
assertCacheMiss(key, cache);
|
||||
|
||||
Object value = simpleService.getSimple(key);
|
||||
assertCacheHit(key, value, cache);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customCacheResolver() {
|
||||
Cache cache = cacheManager.getCache("primary");
|
||||
|
||||
Object key = new Object();
|
||||
assertCacheMiss(key, cache);
|
||||
|
||||
Object value = simpleService.getWithCustomCacheResolver(key);
|
||||
assertCacheHit(key, value, cache);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customCacheManager() {
|
||||
Cache cache = anotherCacheManager.getCache("default");
|
||||
|
||||
Object key = new Object();
|
||||
assertCacheMiss(key, cache);
|
||||
|
||||
Object value = simpleService.getWithCustomCacheManager(key);
|
||||
assertCacheHit(key, value, cache);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runtimeResolution() {
|
||||
Cache defaultCache = cacheManager.getCache("default");
|
||||
Cache primaryCache = cacheManager.getCache("primary");
|
||||
|
||||
Object key = new Object();
|
||||
assertCacheMiss(key, defaultCache, primaryCache);
|
||||
Object value = simpleService.getWithRuntimeCacheResolution(key, "default");
|
||||
assertCacheHit(key, value, defaultCache);
|
||||
assertCacheMiss(key, primaryCache);
|
||||
|
||||
Object key2 = new Object();
|
||||
assertCacheMiss(key2, defaultCache, primaryCache);
|
||||
Object value2 = simpleService.getWithRuntimeCacheResolution(key2, "primary");
|
||||
assertCacheHit(key2, value2, primaryCache);
|
||||
assertCacheMiss(key2, defaultCache);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void namedResolution() {
|
||||
Cache cache = cacheManager.getCache("secondary");
|
||||
|
||||
Object key = new Object();
|
||||
assertCacheMiss(key, cache);
|
||||
|
||||
Object value = simpleService.getWithNamedCacheResolution(key);
|
||||
assertCacheHit(key, value, cache);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noCacheResolved() {
|
||||
Method m = ReflectionUtils.findMethod(SimpleService.class, "noCacheResolved", Object.class);
|
||||
try {
|
||||
simpleService.noCacheResolved(new Object());
|
||||
fail("Should have failed, no cache resolved");
|
||||
} catch (IllegalStateException e) {
|
||||
String msg = e.getMessage();
|
||||
assertTrue("Reference to the method must be contained in the message", msg.contains(m.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unknownCacheResolver() {
|
||||
try {
|
||||
simpleService.unknownCacheResolver(new Object());
|
||||
fail("Should have failed, no cache resolver with that name");
|
||||
} catch (NoSuchBeanDefinitionException e) {
|
||||
assertEquals("Wrong bean name in exception", "unknownCacheResolver", e.getBeanName());
|
||||
}
|
||||
}
|
||||
|
||||
protected void assertCacheMiss(Object key, Cache... caches) {
|
||||
for (Cache cache : caches) {
|
||||
assertNull("No entry in " + cache + " should have been found with key " + key, cache.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
protected void assertCacheHit(Object key, Object value, Cache... caches) {
|
||||
for (Cache cache : caches) {
|
||||
Cache.ValueWrapper wrapper = cache.get(key);
|
||||
assertNotNull("An entry in " + cache + " should have been found with key " + key, wrapper);
|
||||
assertEquals("Wrong value in " + cache + " for entry with key " + key, value, wrapper.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableCaching
|
||||
static class Config extends CachingConfigurerSupport {
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public CacheManager cacheManager() {
|
||||
return CacheTestUtils.createSimpleCacheManager("default", "primary", "secondary");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
public KeyGenerator keyGenerator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CacheManager anotherCacheManager() {
|
||||
return CacheTestUtils.createSimpleCacheManager("default", "primary", "secondary");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CacheResolver primaryCacheResolver() {
|
||||
return new NamedCacheResolver(cacheManager(), "primary");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CacheResolver secondaryCacheResolver() {
|
||||
return new NamedCacheResolver(cacheManager(), "primary");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CacheResolver runtimeCacheResolver() {
|
||||
return new RuntimeCacheResolver(cacheManager());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CacheResolver namedCacheResolver() {
|
||||
NamedCacheResolver resolver = new NamedCacheResolver();
|
||||
resolver.setCacheManager(cacheManager());
|
||||
resolver.setCacheNames(Collections.singleton("secondary"));
|
||||
return resolver;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CacheResolver nullCacheResolver() {
|
||||
return new NullCacheResolver(cacheManager());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SimpleService simpleService() {
|
||||
return new SimpleService();
|
||||
}
|
||||
}
|
||||
|
||||
@CacheConfig(cacheNames = "default")
|
||||
static class SimpleService {
|
||||
|
||||
private final AtomicLong counter = new AtomicLong();
|
||||
|
||||
@Cacheable
|
||||
public Object getSimple(Object key) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@Cacheable(cacheResolver = "primaryCacheResolver")
|
||||
public Object getWithCustomCacheResolver(Object key) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@Cacheable(cacheManager = "anotherCacheManager")
|
||||
public Object getWithCustomCacheManager(Object key) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@Cacheable(cacheResolver = "runtimeCacheResolver", key = "#p0")
|
||||
public Object getWithRuntimeCacheResolution(Object key, String cacheName) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@Cacheable(cacheResolver = "namedCacheResolver")
|
||||
public Object getWithNamedCacheResolution(Object key) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@Cacheable(cacheResolver = "nullCacheResolver") // No cache resolved for the operation
|
||||
public Object noCacheResolved(Object key) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@Cacheable(cacheResolver = "unknownCacheResolver") // No such bean defined
|
||||
public Object unknownCacheResolver(Object key) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Example of {@link CacheResolver} that resolve the caches at
|
||||
* runtime (i.e. based on method invocation parameters).
|
||||
* <p>Expects the second argument to hold the name of the cache to use
|
||||
*/
|
||||
private static class RuntimeCacheResolver extends BaseCacheResolver {
|
||||
|
||||
private RuntimeCacheResolver(CacheManager cacheManager) {
|
||||
super(cacheManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> context) {
|
||||
String cacheName = (String) context.getArgs()[1];
|
||||
return Collections.singleton(cacheName);
|
||||
}
|
||||
}
|
||||
|
||||
private static class NullCacheResolver extends BaseCacheResolver {
|
||||
|
||||
private NullCacheResolver(CacheManager cacheManager) {
|
||||
super(cacheManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> context) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
75
spring-context/src/test/java/org/springframework/cache/interceptor/MethodCacheKeyTests.java
vendored
Normal file
75
spring-context/src/test/java/org/springframework/cache/interceptor/MethodCacheKeyTests.java
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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.junit.Assert.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class MethodCacheKeyTests {
|
||||
|
||||
@Rule
|
||||
public final TestName name = new TestName();
|
||||
|
||||
@Test
|
||||
public void sameInstanceEquals() {
|
||||
Method m = ReflectionUtils.findMethod(getClass(), name.getMethodName());
|
||||
MethodCacheKey instance = new MethodCacheKey(m, getClass());
|
||||
assertKeyEquals(instance, instance);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals() {
|
||||
Method m = ReflectionUtils.findMethod(getClass(), name.getMethodName());
|
||||
MethodCacheKey first = new MethodCacheKey(m, getClass());
|
||||
MethodCacheKey second = new MethodCacheKey(m, getClass());
|
||||
|
||||
assertKeyEquals(first, second);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalsNoTarget() {
|
||||
Method m = ReflectionUtils.findMethod(getClass(), name.getMethodName());
|
||||
MethodCacheKey first = new MethodCacheKey(m, null);
|
||||
MethodCacheKey second = new MethodCacheKey(m, null);
|
||||
|
||||
assertKeyEquals(first, second);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noTargetClassNotEquals() {
|
||||
Method m = ReflectionUtils.findMethod(getClass(), name.getMethodName());
|
||||
MethodCacheKey first = new MethodCacheKey(m, getClass());
|
||||
MethodCacheKey second = new MethodCacheKey(m, null);
|
||||
|
||||
assertFalse(first.equals(second));
|
||||
}
|
||||
|
||||
protected void assertKeyEquals(MethodCacheKey first, MethodCacheKey second) {
|
||||
assertEquals(first, second);
|
||||
assertEquals(first.hashCode(), second.hashCode());
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue