summaryrefslogtreecommitdiff
path: root/spring-core/src/test/java/org/springframework/core/annotation/MultipleComposedAnnotationsOnSingleAnnotatedElementTests.java
diff options
context:
space:
mode:
Diffstat (limited to 'spring-core/src/test/java/org/springframework/core/annotation/MultipleComposedAnnotationsOnSingleAnnotatedElementTests.java')
-rw-r--r--spring-core/src/test/java/org/springframework/core/annotation/MultipleComposedAnnotationsOnSingleAnnotatedElementTests.java368
1 files changed, 368 insertions, 0 deletions
diff --git a/spring-core/src/test/java/org/springframework/core/annotation/MultipleComposedAnnotationsOnSingleAnnotatedElementTests.java b/spring-core/src/test/java/org/springframework/core/annotation/MultipleComposedAnnotationsOnSingleAnnotatedElementTests.java
new file mode 100644
index 00000000..fc2259ac
--- /dev/null
+++ b/spring-core/src/test/java/org/springframework/core/annotation/MultipleComposedAnnotationsOnSingleAnnotatedElementTests.java
@@ -0,0 +1,368 @@
+/*
+ * 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.
+ * 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.core.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Method;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+import static org.springframework.core.annotation.AnnotatedElementUtils.*;
+
+/**
+ * Unit tests that verify support for finding multiple composed annotations on
+ * a single annotated element.
+ *
+ * <p>See <a href="https://jira.spring.io/browse/SPR-13486">SPR-13486</a>.
+ *
+ * @author Sam Brannen
+ * @since 4.3
+ * @see AnnotatedElementUtils
+ * @see AnnotatedElementUtilsTests
+ * @see ComposedRepeatableAnnotationsTests
+ */
+public class MultipleComposedAnnotationsOnSingleAnnotatedElementTests {
+
+ @Test
+ public void getMultipleComposedAnnotationsOnClass() {
+ assertGetAllMergedAnnotationsBehavior(MultipleComposedCachesClass.class);
+ }
+
+ @Test
+ public void getMultipleInheritedComposedAnnotationsOnSuperclass() {
+ assertGetAllMergedAnnotationsBehavior(SubMultipleComposedCachesClass.class);
+ }
+
+ @Test
+ public void getMultipleNoninheritedComposedAnnotationsOnClass() {
+ Class<?> element = MultipleNoninheritedComposedCachesClass.class;
+ Set<Cacheable> cacheables = getAllMergedAnnotations(element, Cacheable.class);
+ assertNotNull(cacheables);
+ assertEquals(2, cacheables.size());
+
+ Iterator<Cacheable> iterator = cacheables.iterator();
+ Cacheable cacheable1 = iterator.next();
+ Cacheable cacheable2 = iterator.next();
+ assertEquals("noninheritedCache1", cacheable1.value());
+ assertEquals("noninheritedCache2", cacheable2.value());
+ }
+
+ @Test
+ public void getMultipleNoninheritedComposedAnnotationsOnSuperclass() {
+ Class<?> element = SubMultipleNoninheritedComposedCachesClass.class;
+ Set<Cacheable> cacheables = getAllMergedAnnotations(element, Cacheable.class);
+ assertNotNull(cacheables);
+ assertEquals(0, cacheables.size());
+ }
+
+ @Test
+ public void getComposedPlusLocalAnnotationsOnClass() {
+ assertGetAllMergedAnnotationsBehavior(ComposedPlusLocalCachesClass.class);
+ }
+
+ @Test
+ public void getMultipleComposedAnnotationsOnInterface() {
+ Class<MultipleComposedCachesOnInterfaceClass> element = MultipleComposedCachesOnInterfaceClass.class;
+ Set<Cacheable> cacheables = getAllMergedAnnotations(element, Cacheable.class);
+ assertNotNull(cacheables);
+ assertEquals(0, cacheables.size());
+ }
+
+ @Test
+ public void getMultipleComposedAnnotationsOnMethod() throws Exception {
+ AnnotatedElement element = getClass().getDeclaredMethod("multipleComposedCachesMethod");
+ assertGetAllMergedAnnotationsBehavior(element);
+ }
+
+ @Test
+ public void getComposedPlusLocalAnnotationsOnMethod() throws Exception {
+ AnnotatedElement element = getClass().getDeclaredMethod("composedPlusLocalCachesMethod");
+ assertGetAllMergedAnnotationsBehavior(element);
+ }
+
+ @Test
+ @Ignore("Disabled since some Java 8 updates handle the bridge method differently")
+ public void getMultipleComposedAnnotationsOnBridgeMethod() throws Exception {
+ Set<Cacheable> cacheables = getAllMergedAnnotations(getBridgeMethod(), Cacheable.class);
+ assertNotNull(cacheables);
+ assertEquals(0, cacheables.size());
+ }
+
+ @Test
+ public void findMultipleComposedAnnotationsOnClass() {
+ assertFindAllMergedAnnotationsBehavior(MultipleComposedCachesClass.class);
+ }
+
+ @Test
+ public void findMultipleInheritedComposedAnnotationsOnSuperclass() {
+ assertFindAllMergedAnnotationsBehavior(SubMultipleComposedCachesClass.class);
+ }
+
+ @Test
+ public void findMultipleNoninheritedComposedAnnotationsOnClass() {
+ Class<?> element = MultipleNoninheritedComposedCachesClass.class;
+ Set<Cacheable> cacheables = findAllMergedAnnotations(element, Cacheable.class);
+ assertNotNull(cacheables);
+ assertEquals(2, cacheables.size());
+
+ Iterator<Cacheable> iterator = cacheables.iterator();
+ Cacheable cacheable1 = iterator.next();
+ Cacheable cacheable2 = iterator.next();
+ assertEquals("noninheritedCache1", cacheable1.value());
+ assertEquals("noninheritedCache2", cacheable2.value());
+ }
+
+ @Test
+ public void findMultipleNoninheritedComposedAnnotationsOnSuperclass() {
+ Class<?> element = SubMultipleNoninheritedComposedCachesClass.class;
+ Set<Cacheable> cacheables = findAllMergedAnnotations(element, Cacheable.class);
+ assertNotNull(cacheables);
+ assertEquals(2, cacheables.size());
+
+ Iterator<Cacheable> iterator = cacheables.iterator();
+ Cacheable cacheable1 = iterator.next();
+ Cacheable cacheable2 = iterator.next();
+ assertEquals("noninheritedCache1", cacheable1.value());
+ assertEquals("noninheritedCache2", cacheable2.value());
+ }
+
+ @Test
+ public void findComposedPlusLocalAnnotationsOnClass() {
+ assertFindAllMergedAnnotationsBehavior(ComposedPlusLocalCachesClass.class);
+ }
+
+ @Test
+ public void findMultipleComposedAnnotationsOnInterface() {
+ assertFindAllMergedAnnotationsBehavior(MultipleComposedCachesOnInterfaceClass.class);
+ }
+
+ @Test
+ public void findComposedCacheOnInterfaceAndLocalCacheOnClass() {
+ assertFindAllMergedAnnotationsBehavior(ComposedCacheOnInterfaceAndLocalCacheClass.class);
+ }
+
+ @Test
+ public void findMultipleComposedAnnotationsOnMethod() throws Exception {
+ AnnotatedElement element = getClass().getDeclaredMethod("multipleComposedCachesMethod");
+ assertFindAllMergedAnnotationsBehavior(element);
+ }
+
+ @Test
+ public void findComposedPlusLocalAnnotationsOnMethod() throws Exception {
+ AnnotatedElement element = getClass().getDeclaredMethod("composedPlusLocalCachesMethod");
+ assertFindAllMergedAnnotationsBehavior(element);
+ }
+
+ @Test
+ public void findMultipleComposedAnnotationsOnBridgeMethod() throws Exception {
+ assertFindAllMergedAnnotationsBehavior(getBridgeMethod());
+ }
+
+ /**
+ * Bridge/bridged method setup code copied from
+ * {@link org.springframework.core.BridgeMethodResolverTests#testWithGenericParameter()}.
+ */
+ public Method getBridgeMethod() throws NoSuchMethodException {
+ Method[] methods = StringGenericParameter.class.getMethods();
+ Method bridgeMethod = null;
+ Method bridgedMethod = null;
+
+ for (Method method : methods) {
+ if ("getFor".equals(method.getName()) && !method.getParameterTypes()[0].equals(Integer.class)) {
+ if (method.getReturnType().equals(Object.class)) {
+ bridgeMethod = method;
+ }
+ else {
+ bridgedMethod = method;
+ }
+ }
+ }
+ assertTrue(bridgeMethod != null && bridgeMethod.isBridge());
+ assertTrue(bridgedMethod != null && !bridgedMethod.isBridge());
+
+ return bridgeMethod;
+ }
+
+ private void assertGetAllMergedAnnotationsBehavior(AnnotatedElement element) {
+ assertNotNull(element);
+
+ Set<Cacheable> cacheables = getAllMergedAnnotations(element, Cacheable.class);
+ assertNotNull(cacheables);
+ assertEquals(2, cacheables.size());
+
+ Iterator<Cacheable> iterator = cacheables.iterator();
+ Cacheable fooCacheable = iterator.next();
+ Cacheable barCacheable = iterator.next();
+ assertEquals("fooKey", fooCacheable.key());
+ assertEquals("fooCache", fooCacheable.value());
+ assertEquals("barKey", barCacheable.key());
+ assertEquals("barCache", barCacheable.value());
+ }
+
+ private void assertFindAllMergedAnnotationsBehavior(AnnotatedElement element) {
+ assertNotNull(element);
+
+ Set<Cacheable> cacheables = findAllMergedAnnotations(element, Cacheable.class);
+ assertNotNull(cacheables);
+ assertEquals(2, cacheables.size());
+
+ Iterator<Cacheable> iterator = cacheables.iterator();
+ Cacheable fooCacheable = iterator.next();
+ Cacheable barCacheable = iterator.next();
+ assertEquals("fooKey", fooCacheable.key());
+ assertEquals("fooCache", fooCacheable.value());
+ assertEquals("barKey", barCacheable.key());
+ assertEquals("barCache", barCacheable.value());
+ }
+
+
+ // -------------------------------------------------------------------------
+
+ /**
+ * Mock of {@code org.springframework.cache.annotation.Cacheable}.
+ */
+ @Target({ ElementType.METHOD, ElementType.TYPE })
+ @Retention(RetentionPolicy.RUNTIME)
+ @Inherited
+ @interface Cacheable {
+
+ @AliasFor("cacheName")
+ String value() default "";
+
+ @AliasFor("value")
+ String cacheName() default "";
+
+ String key() default "";
+ }
+
+ @Cacheable("fooCache")
+ @Target({ ElementType.METHOD, ElementType.TYPE })
+ @Retention(RetentionPolicy.RUNTIME)
+ @Inherited
+ @interface FooCache {
+
+ @AliasFor(annotation = Cacheable.class)
+ String key() default "";
+ }
+
+ @Cacheable("barCache")
+ @Target({ ElementType.METHOD, ElementType.TYPE })
+ @Retention(RetentionPolicy.RUNTIME)
+ @Inherited
+ @interface BarCache {
+
+ @AliasFor(annotation = Cacheable.class)
+ String key();
+ }
+
+ @Cacheable("noninheritedCache1")
+ @Target({ ElementType.METHOD, ElementType.TYPE })
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface NoninheritedCache1 {
+
+ @AliasFor(annotation = Cacheable.class)
+ String key() default "";
+ }
+
+ @Cacheable("noninheritedCache2")
+ @Target({ ElementType.METHOD, ElementType.TYPE })
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface NoninheritedCache2 {
+
+ @AliasFor(annotation = Cacheable.class)
+ String key() default "";
+ }
+
+ @FooCache(key = "fooKey")
+ @BarCache(key = "barKey")
+ private static class MultipleComposedCachesClass {
+ }
+
+ private static class SubMultipleComposedCachesClass extends MultipleComposedCachesClass {
+ }
+
+ @NoninheritedCache1
+ @NoninheritedCache2
+ private static class MultipleNoninheritedComposedCachesClass {
+ }
+
+ private static class SubMultipleNoninheritedComposedCachesClass extends MultipleNoninheritedComposedCachesClass {
+ }
+
+ @Cacheable(cacheName = "fooCache", key = "fooKey")
+ @BarCache(key = "barKey")
+ private static class ComposedPlusLocalCachesClass {
+ }
+
+ @FooCache(key = "fooKey")
+ @BarCache(key = "barKey")
+ private interface MultipleComposedCachesInterface {
+ }
+
+ private static class MultipleComposedCachesOnInterfaceClass implements MultipleComposedCachesInterface {
+ }
+
+ @Cacheable(cacheName = "fooCache", key = "fooKey")
+ private interface ComposedCacheInterface {
+ }
+
+ @BarCache(key = "barKey")
+ private static class ComposedCacheOnInterfaceAndLocalCacheClass implements ComposedCacheInterface {
+ }
+
+
+ @FooCache(key = "fooKey")
+ @BarCache(key = "barKey")
+ private void multipleComposedCachesMethod() {
+ }
+
+ @Cacheable(cacheName = "fooCache", key = "fooKey")
+ @BarCache(key = "barKey")
+ private void composedPlusLocalCachesMethod() {
+ }
+
+
+ public interface GenericParameter<T> {
+
+ T getFor(Class<T> cls);
+ }
+
+ @SuppressWarnings("unused")
+ private static class StringGenericParameter implements GenericParameter<String> {
+
+ @FooCache(key = "fooKey")
+ @BarCache(key = "barKey")
+ @Override
+ public String getFor(Class<String> cls) {
+ return "foo";
+ }
+
+ public String getFor(Integer integer) {
+ return "foo";
+ }
+ }
+
+}