Allow Cache annotations to not specify any cache name
Since Spring 4.1, a CacheResolver may be configured to customize the way the cache(s) to use for a given cache operation are retrieved. Since a CacheResolver implementation may not use the cache names information at all, this attribute has been made optional. However, a fix was still applied, preventing a Cache operation without a cache name to be defined properly. We now allow this valid use case. Issue: SPR-13081
This commit is contained in:
@@ -236,11 +236,6 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
|
|||||||
"default cache resolver if none is set. If a cache resolver is set, the cache manager" +
|
"default cache resolver if none is set. If a cache resolver is set, the cache manager" +
|
||||||
"won't be used.");
|
"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 " +
|
|
||||||
"declare a @CacheConfig at the class-level with the default cache name(s) to use.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -17,18 +17,24 @@
|
|||||||
package org.springframework.cache;
|
package org.springframework.cache;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.ExpectedException;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
import org.springframework.cache.annotation.Cacheable;
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
import org.springframework.cache.annotation.Caching;
|
import org.springframework.cache.annotation.Caching;
|
||||||
|
import org.springframework.cache.annotation.CachingConfigurerSupport;
|
||||||
import org.springframework.cache.annotation.EnableCaching;
|
import org.springframework.cache.annotation.EnableCaching;
|
||||||
import org.springframework.cache.concurrent.ConcurrentMapCache;
|
import org.springframework.cache.concurrent.ConcurrentMapCache;
|
||||||
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
|
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
|
||||||
|
import org.springframework.cache.interceptor.AbstractCacheResolver;
|
||||||
|
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
|
||||||
|
import org.springframework.cache.interceptor.CacheResolver;
|
||||||
import org.springframework.cache.support.SimpleCacheManager;
|
import org.springframework.cache.support.SimpleCacheManager;
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@@ -46,6 +52,9 @@ import static org.mockito.Mockito.*;
|
|||||||
*/
|
*/
|
||||||
public class CacheReproTests {
|
public class CacheReproTests {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final ExpectedException thrown = ExpectedException.none();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void spr11124() throws Exception {
|
public void spr11124() throws Exception {
|
||||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Spr11124Config.class);
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Spr11124Config.class);
|
||||||
@@ -84,7 +93,7 @@ public class CacheReproTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void spr11592GetNeverCache(){
|
public void spr11592GetNeverCache() {
|
||||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Spr11592Config.class);
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Spr11592Config.class);
|
||||||
Spr11592Service bean = context.getBean(Spr11592Service.class);
|
Spr11592Service bean = context.getBean(Spr11592Service.class);
|
||||||
Cache cache = context.getBean("cache", Cache.class);
|
Cache cache = context.getBean("cache", Cache.class);
|
||||||
@@ -100,6 +109,27 @@ public class CacheReproTests {
|
|||||||
context.close();
|
context.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void spr13081ConfigNoCacheNameIsRequired() {
|
||||||
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Spr13081Config.class);
|
||||||
|
MyCacheResolver cacheResolver = context.getBean(MyCacheResolver.class);
|
||||||
|
Spr13081Service bean = context.getBean(Spr13081Service.class);
|
||||||
|
assertNull(cacheResolver.getCache("foo").get("foo"));
|
||||||
|
Object result = bean.getSimple("foo"); // cache name = id
|
||||||
|
assertEquals(result, cacheResolver.getCache("foo").get("foo").get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void spr13081ConfigFailIfCacheResolverReturnsNullCacheName() {
|
||||||
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Spr13081Config.class);
|
||||||
|
Spr13081Service bean = context.getBean(Spr13081Service.class);
|
||||||
|
|
||||||
|
|
||||||
|
thrown.expect(IllegalStateException.class);
|
||||||
|
thrown.expectMessage(MyCacheResolver.class.getName());
|
||||||
|
bean.getSimple(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableCaching
|
@EnableCaching
|
||||||
@@ -119,9 +149,9 @@ public class CacheReproTests {
|
|||||||
|
|
||||||
public interface Spr11124Service {
|
public interface Spr11124Service {
|
||||||
|
|
||||||
public List<String> single(int id);
|
List<String> single(int id);
|
||||||
|
|
||||||
public List<String> multiple(int id);
|
List<String> multiple(int id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -142,7 +172,7 @@ public class CacheReproTests {
|
|||||||
@Override
|
@Override
|
||||||
@Caching(cacheable = {
|
@Caching(cacheable = {
|
||||||
@Cacheable(cacheNames = "bigCache", unless = "#result.size() < 4"),
|
@Cacheable(cacheNames = "bigCache", unless = "#result.size() < 4"),
|
||||||
@Cacheable(cacheNames = "smallCache", unless = "#result.size() > 3") })
|
@Cacheable(cacheNames = "smallCache", unless = "#result.size() > 3")})
|
||||||
public List<String> multiple(int id) {
|
public List<String> multiple(int id) {
|
||||||
if (this.multipleCount > 0) {
|
if (this.multipleCount > 0) {
|
||||||
fail("Called too many times");
|
fail("Called too many times");
|
||||||
@@ -215,4 +245,49 @@ public class CacheReproTests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableCaching
|
||||||
|
public static class Spr13081Config extends CachingConfigurerSupport {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Override
|
||||||
|
public CacheResolver cacheResolver() {
|
||||||
|
return new MyCacheResolver();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Spr13081Service service() {
|
||||||
|
return new Spr13081Service();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MyCacheResolver extends AbstractCacheResolver {
|
||||||
|
|
||||||
|
public MyCacheResolver() {
|
||||||
|
super(new ConcurrentMapCacheManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Collection<String> getCacheNames(CacheOperationInvocationContext<?> context) {
|
||||||
|
String cacheName = (String) context.getArgs()[0];
|
||||||
|
if (cacheName != null) {
|
||||||
|
return Collections.singleton(cacheName);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Cache getCache(String name) {
|
||||||
|
return getCacheManager().getCache(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Spr13081Service {
|
||||||
|
|
||||||
|
@Cacheable
|
||||||
|
public Object getSimple(String cacheName) {
|
||||||
|
return new Object();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,9 +191,12 @@ public class AnnotationCacheOperationSourceTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void validateAtLeastOneCacheNameMustBeSet() {
|
public void validateNoCacheIsValid() {
|
||||||
thrown.expect(IllegalStateException.class);
|
// Valid as a CacheResolver might return the cache names to use with other info
|
||||||
getOps(AnnotatedClass.class, "noCacheNameSpecified");
|
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "noCacheNameSpecified");
|
||||||
|
CacheOperation cacheOperation = ops.iterator().next();
|
||||||
|
assertNotNull("cache names set must not be null", cacheOperation.getCacheNames());
|
||||||
|
assertEquals("no cache names specified", 0, cacheOperation.getCacheNames().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user