diff options
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.java | 368 |
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"; + } + } + +} |