diff --git a/spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java b/spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java index bef1b773c4..737cb6f324 100644 --- a/spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java +++ b/spring-context/src/main/java/org/springframework/cache/annotation/SpringCacheAnnotationParser.java @@ -27,6 +27,7 @@ import org.springframework.cache.interceptor.CacheEvictOperation; import org.springframework.cache.interceptor.CacheOperation; import org.springframework.cache.interceptor.CachePutOperation; import org.springframework.cache.interceptor.CacheableOperation; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -61,29 +62,29 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria protected Collection parseCacheAnnotations(DefaultCacheConfig cachingConfig, AnnotatedElement ae) { Collection ops = null; - Collection cacheables = getAnnotations(ae, Cacheable.class); - if (cacheables != null) { + Collection cacheables = AnnotatedElementUtils.findAllMergedAnnotations(ae, Cacheable.class); + if (!cacheables.isEmpty()) { ops = lazyInit(ops); for (Cacheable cacheable : cacheables) { ops.add(parseCacheableAnnotation(ae, cachingConfig, cacheable)); } } - Collection evicts = getAnnotations(ae, CacheEvict.class); - if (evicts != null) { + Collection evicts = AnnotatedElementUtils.findAllMergedAnnotations(ae, CacheEvict.class); + if (!evicts.isEmpty()) { ops = lazyInit(ops); for (CacheEvict evict : evicts) { ops.add(parseEvictAnnotation(ae, cachingConfig, evict)); } } - Collection puts = getAnnotations(ae, CachePut.class); - if (puts != null) { + Collection puts = AnnotatedElementUtils.findAllMergedAnnotations(ae, CachePut.class); + if (!puts.isEmpty()) { ops = lazyInit(ops); for (CachePut put : puts) { ops.add(parsePutAnnotation(ae, cachingConfig, put)); } } - Collection cachings = getAnnotations(ae, Caching.class); - if (cachings != null) { + Collection cachings = AnnotatedElementUtils.findAllMergedAnnotations(ae, Caching.class); + if (!cachings.isEmpty()) { ops = lazyInit(ops); for (Caching caching : cachings) { ops.addAll(parseCachingAnnotation(ae, cachingConfig, caching)); @@ -198,26 +199,6 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria return new DefaultCacheConfig(); } - private Collection getAnnotations(AnnotatedElement ae, Class annotationType) { - Collection anns = new ArrayList(1); - - // look at raw annotation - A ann = ae.getAnnotation(annotationType); - if (ann != null) { - anns.add(AnnotationUtils.synthesizeAnnotation(ann, ae)); - } - - // scan meta-annotations - for (Annotation metaAnn : ae.getAnnotations()) { - ann = metaAnn.annotationType().getAnnotation(annotationType); - if (ann != null) { - anns.add(AnnotationUtils.synthesizeAnnotation(ann, ae)); - } - } - - return (!anns.isEmpty() ? anns : null); - } - /** * Validates the specified {@link CacheOperation}. *

Throws an {@link IllegalStateException} if the state of the operation is diff --git a/spring-context/src/test/java/org/springframework/cache/annotation/AnnotationCacheOperationSourceTests.java b/spring-context/src/test/java/org/springframework/cache/annotation/AnnotationCacheOperationSourceTests.java index 35091c83a3..4b1d1016bf 100644 --- a/spring-context/src/test/java/org/springframework/cache/annotation/AnnotationCacheOperationSourceTests.java +++ b/spring-context/src/test/java/org/springframework/cache/annotation/AnnotationCacheOperationSourceTests.java @@ -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 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 ops = getOps(AnnotatedClass.class, "multipleComposed", 3); + Collection ops = getOps(AnnotatedClass.class, "singleComposed", 2); Iterator 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 ops = getOps(AnnotatedClass.class, "multipleComposed", 4); + Iterator 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 ""; }