From 23d4862017f8e830321c34abd4c704468144ecb0 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Wed, 18 Jul 2018 19:44:30 +0200 Subject: [PATCH] Find annotations on implemented generic interface methods as well Issue: SPR-16060 --- .../core/annotation/AnnotationUtils.java | 21 ++++++++++++-- .../core/annotation/AnnotationUtilsTests.java | 29 +++++++++++++++---- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java index 28e07be2fe6..12693854498 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java @@ -39,6 +39,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.core.BridgeMethodResolver; +import org.springframework.core.ResolvableType; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -588,8 +589,7 @@ public abstract class AnnotationUtils { Set annotatedMethods = getAnnotatedMethodsInBaseType(ifc); if (!annotatedMethods.isEmpty()) { for (Method annotatedMethod : annotatedMethods) { - if (annotatedMethod.getName().equals(method.getName()) && - Arrays.equals(annotatedMethod.getParameterTypes(), method.getParameterTypes())) { + if (isOverride(method, annotatedMethod)) { A annotation = getAnnotation(annotatedMethod, annotationType); if (annotation != null) { return annotation; @@ -647,6 +647,23 @@ public abstract class AnnotationUtils { return true; } + private static boolean isOverride(Method method, Method candidate) { + if (!candidate.getName().equals(method.getName()) || + candidate.getParameterCount() != method.getParameterCount()) { + return false; + } + Class[] paramTypes = method.getParameterTypes(); + if (Arrays.equals(candidate.getParameterTypes(), paramTypes)) { + return true; + } + for (int i = 0; i < paramTypes.length; i++) { + if (paramTypes[i] != ResolvableType.forMethodParameter(candidate, i, method.getDeclaringClass()).resolve()) { + return false; + } + } + return true; + } + /** * Find a single {@link Annotation} of {@code annotationType} on the * supplied {@link Class}, traversing its interfaces, annotations, and diff --git a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java index 76ea0990434..a62131a8dd5 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java @@ -178,6 +178,13 @@ public class AnnotationUtilsTests { assertNotNull(order); } + @Test // SPR-16060 + public void findMethodAnnotationFromGenericInterface() throws Exception { + Method method = ImplementsInterfaceWithGenericAnnotatedMethod.class.getMethod("foo", String.class); + Order order = findAnnotation(method, Order.class); + assertNotNull(order); + } + @Test public void findMethodAnnotationFromInterfaceOnSuper() throws Exception { Method method = SubOfImplementsInterfaceWithAnnotatedMethod.class.getMethod("foo"); @@ -286,7 +293,7 @@ public class AnnotationUtilsTests { } @Test - public void findAnnotationDeclaringClassForAllScenarios() throws Exception { + public void findAnnotationDeclaringClassForAllScenarios() { // no class-level annotation assertNull(findAnnotationDeclaringClass(Transactional.class, NonAnnotatedInterface.class)); assertNull(findAnnotationDeclaringClass(Transactional.class, NonAnnotatedClass.class)); @@ -395,7 +402,7 @@ public class AnnotationUtilsTests { } @Test - public void isAnnotationInheritedForAllScenarios() throws Exception { + public void isAnnotationInheritedForAllScenarios() { // no class-level annotation assertFalse(isAnnotationInherited(Transactional.class, NonAnnotatedInterface.class)); assertFalse(isAnnotationInherited(Transactional.class, NonAnnotatedClass.class)); @@ -504,7 +511,7 @@ public class AnnotationUtilsTests { } @Test - public void getDefaultValueFromNonPublicAnnotation() throws Exception { + public void getDefaultValueFromNonPublicAnnotation() { Annotation[] declaredAnnotations = NonPublicAnnotatedClass.class.getDeclaredAnnotations(); assertEquals(1, declaredAnnotations.length); Annotation annotation = declaredAnnotations[0]; @@ -515,7 +522,7 @@ public class AnnotationUtilsTests { } @Test - public void getDefaultValueFromAnnotationType() throws Exception { + public void getDefaultValueFromAnnotationType() { assertEquals(Ordered.LOWEST_PRECEDENCE, getDefaultValue(Order.class, VALUE)); assertEquals(Ordered.LOWEST_PRECEDENCE, getDefaultValue(Order.class)); } @@ -547,7 +554,7 @@ public class AnnotationUtilsTests { } @Test - public void getRepeatableAnnotationsDeclaredOnClassWithAttributeAliases() throws Exception { + public void getRepeatableAnnotationsDeclaredOnClassWithAttributeAliases() { final List expectedLocations = asList("A", "B"); Set annotations = getRepeatableAnnotations(ConfigHierarchyTestCase.class, ContextConfig.class, null); @@ -1750,6 +1757,18 @@ public class AnnotationUtilsTests { public static class SubTransactionalAndOrderedClass extends TransactionalAndOrderedClass { } + public interface InterfaceWithGenericAnnotatedMethod { + + @Order + void foo(T t); + } + + public static class ImplementsInterfaceWithGenericAnnotatedMethod implements InterfaceWithGenericAnnotatedMethod { + + public void foo(String t) { + } + } + public interface InterfaceWithAnnotatedMethod { @Order