Support @Cache* as merged composed annotations

Prior to this commit, @Cacheable, @CacheEvict, @CachePut, and @Caching
could be used to create custom stereotype annotations with hardcoded
values for their attributes; however, it was not possible to create
composed annotations with attribute overrides.

This commit addresses this issue by refactoring
SpringCacheAnnotationParser to use the newly introduced
findAllMergedAnnotations() method in AnnotatedElementUtils. As a
result, @Cacheable, @CacheEvict, @CachePut, and @Caching can now be
used to create custom composed annotations with attribute overrides
configured via @AliasFor.

Issue: SPR-13475
This commit is contained in:
Sam Brannen
2016-03-21 12:53:14 +01:00
parent 2a715e9c61
commit 59c88eb3c0
2 changed files with 49 additions and 56 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 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.
@@ -26,7 +26,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -105,34 +104,46 @@ public class AnnotationCacheOperationSourceTests {
assertTrue(next.getCacheNames().contains("bar"));
}
// TODO [SPR-13475] Enable test once @Cache* is supported as a composed annotation.
@Ignore("Disabled until SPR-13475 is resolved")
@Test
public void singleComposedAnnotation() throws Exception {
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "singleComposed", 1);
CacheOperation cacheOperation = ops.iterator().next();
assertThat(cacheOperation, instanceOf(CacheableOperation.class));
assertThat(cacheOperation.getCacheNames(), equalTo(Collections.singleton("composed")));
}
// TODO [SPR-13475] Enable test once @Cache* is supported as a composed annotation.
@Ignore("Disabled until SPR-13475 is resolved")
@Test
public void multipleComposedAnnotations() throws Exception {
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "multipleComposed", 3);
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "singleComposed", 2);
Iterator<CacheOperation> it = ops.iterator();
CacheOperation cacheOperation = it.next();
assertThat(cacheOperation, instanceOf(CacheableOperation.class));
assertThat(cacheOperation.getCacheNames(), equalTo(Collections.singleton("directly declared")));
assertThat(cacheOperation.getKey(), equalTo(""));
cacheOperation = it.next();
assertThat(cacheOperation, instanceOf(CacheableOperation.class));
assertThat(cacheOperation.getCacheNames(), equalTo(Collections.singleton("composedCache")));
assertThat(cacheOperation.getKey(), equalTo("composedKey"));
}
@Test
public void multipleComposedAnnotations() throws Exception {
Collection<CacheOperation> ops = getOps(AnnotatedClass.class, "multipleComposed", 4);
Iterator<CacheOperation> it = ops.iterator();
CacheOperation cacheOperation = it.next();
assertThat(cacheOperation, instanceOf(CacheableOperation.class));
assertThat(cacheOperation.getCacheNames(), equalTo(Collections.singleton("directly declared")));
assertThat(cacheOperation.getKey(), equalTo(""));
cacheOperation = it.next();
assertThat(cacheOperation, instanceOf(CacheableOperation.class));
assertThat(cacheOperation.getCacheNames(), equalTo(Collections.singleton("composedCache")));
assertThat(cacheOperation.getKey(), equalTo("composedKey"));
cacheOperation = it.next();
assertThat(cacheOperation, instanceOf(CacheableOperation.class));
assertThat(cacheOperation.getCacheNames(), equalTo(Collections.singleton("foo")));
assertThat(cacheOperation.getKey(), equalTo(""));
cacheOperation = it.next();
assertThat(cacheOperation, instanceOf(CacheEvictOperation.class));
assertThat(cacheOperation.getCacheNames(), equalTo(Collections.singleton("composedCache")));
assertThat(cacheOperation.getCacheNames(), equalTo(Collections.singleton("composedCacheEvict")));
assertThat(cacheOperation.getKey(), equalTo("composedEvictionKey"));
}
@Test
@@ -309,13 +320,15 @@ public class AnnotationCacheOperationSourceTests {
public void multipleStereotype() {
}
@ComposedCacheable("composed")
@Cacheable("directly declared")
@ComposedCacheable(cacheNames = "composedCache", key = "composedKey")
public void singleComposed() {
}
@Cacheable("directly declared")
@ComposedCacheable(cacheNames = "composedCache", key = "composedKey")
@CacheableFoo
@ComposedCacheEvict(cacheNames = "composedCache", key = "composedKey")
@ComposedCacheEvict(cacheNames = "composedCacheEvict", key = "composedEvictionKey")
public void multipleComposed() {
}
@@ -443,38 +456,37 @@ public class AnnotationCacheOperationSourceTests {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@CacheConfig(keyGenerator = "classKeyGenerator",
cacheManager = "classCacheManager", cacheResolver = "classCacheResolver")
@CacheConfig(keyGenerator = "classKeyGenerator", cacheManager = "classCacheManager", cacheResolver = "classCacheResolver")
public @interface CacheConfigFoo {
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@Cacheable(cacheNames = "shadowed cache name", key = "shadowed key")
public @interface ComposedCacheable {
@interface ComposedCacheable {
@AliasFor(annotation = Cacheable.class, attribute = "cacheNames")
@AliasFor(annotation = Cacheable.class)
String[] value() default {};
@AliasFor(annotation = Cacheable.class, attribute = "cacheNames")
@AliasFor(annotation = Cacheable.class)
String[] cacheNames() default {};
@AliasFor(annotation = Cacheable.class, attribute = "key")
@AliasFor(annotation = Cacheable.class)
String key() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
@CacheEvict(cacheNames = "shadowed cache name", key = "shadowed key")
public @interface ComposedCacheEvict {
@interface ComposedCacheEvict {
@AliasFor(annotation = Cacheable.class, attribute = "cacheNames")
@AliasFor(annotation = CacheEvict.class)
String[] value() default {};
@AliasFor(annotation = Cacheable.class, attribute = "cacheNames")
@AliasFor(annotation = CacheEvict.class)
String[] cacheNames() default {};
@AliasFor(annotation = Cacheable.class, attribute = "key")
@AliasFor(annotation = CacheEvict.class)
String key() default "";
}