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:
47
spring-context/src/test/java/org/springframework/cache/CacheTestUtils.java
vendored
Normal file
47
spring-context/src/test/java/org/springframework/cache/CacheTestUtils.java
vendored
Normal file
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user