Browse Source

Find annotations on implemented generic interface methods as well

Issue: SPR-16060
pull/1891/head
Juergen Hoeller 6 years ago
parent
commit
23d4862017
  1. 21
      spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java
  2. 29
      spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java

21
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.apache.commons.logging.LogFactory;
import org.springframework.core.BridgeMethodResolver; import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.ResolvableType;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
@ -588,8 +589,7 @@ public abstract class AnnotationUtils {
Set<Method> annotatedMethods = getAnnotatedMethodsInBaseType(ifc); Set<Method> annotatedMethods = getAnnotatedMethodsInBaseType(ifc);
if (!annotatedMethods.isEmpty()) { if (!annotatedMethods.isEmpty()) {
for (Method annotatedMethod : annotatedMethods) { for (Method annotatedMethod : annotatedMethods) {
if (annotatedMethod.getName().equals(method.getName()) && if (isOverride(method, annotatedMethod)) {
Arrays.equals(annotatedMethod.getParameterTypes(), method.getParameterTypes())) {
A annotation = getAnnotation(annotatedMethod, annotationType); A annotation = getAnnotation(annotatedMethod, annotationType);
if (annotation != null) { if (annotation != null) {
return annotation; return annotation;
@ -647,6 +647,23 @@ public abstract class AnnotationUtils {
return true; 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 * Find a single {@link Annotation} of {@code annotationType} on the
* supplied {@link Class}, traversing its interfaces, annotations, and * supplied {@link Class}, traversing its interfaces, annotations, and

29
spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java

@ -178,6 +178,13 @@ public class AnnotationUtilsTests {
assertNotNull(order); 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 @Test
public void findMethodAnnotationFromInterfaceOnSuper() throws Exception { public void findMethodAnnotationFromInterfaceOnSuper() throws Exception {
Method method = SubOfImplementsInterfaceWithAnnotatedMethod.class.getMethod("foo"); Method method = SubOfImplementsInterfaceWithAnnotatedMethod.class.getMethod("foo");
@ -286,7 +293,7 @@ public class AnnotationUtilsTests {
} }
@Test @Test
public void findAnnotationDeclaringClassForAllScenarios() throws Exception { public void findAnnotationDeclaringClassForAllScenarios() {
// no class-level annotation // no class-level annotation
assertNull(findAnnotationDeclaringClass(Transactional.class, NonAnnotatedInterface.class)); assertNull(findAnnotationDeclaringClass(Transactional.class, NonAnnotatedInterface.class));
assertNull(findAnnotationDeclaringClass(Transactional.class, NonAnnotatedClass.class)); assertNull(findAnnotationDeclaringClass(Transactional.class, NonAnnotatedClass.class));
@ -395,7 +402,7 @@ public class AnnotationUtilsTests {
} }
@Test @Test
public void isAnnotationInheritedForAllScenarios() throws Exception { public void isAnnotationInheritedForAllScenarios() {
// no class-level annotation // no class-level annotation
assertFalse(isAnnotationInherited(Transactional.class, NonAnnotatedInterface.class)); assertFalse(isAnnotationInherited(Transactional.class, NonAnnotatedInterface.class));
assertFalse(isAnnotationInherited(Transactional.class, NonAnnotatedClass.class)); assertFalse(isAnnotationInherited(Transactional.class, NonAnnotatedClass.class));
@ -504,7 +511,7 @@ public class AnnotationUtilsTests {
} }
@Test @Test
public void getDefaultValueFromNonPublicAnnotation() throws Exception { public void getDefaultValueFromNonPublicAnnotation() {
Annotation[] declaredAnnotations = NonPublicAnnotatedClass.class.getDeclaredAnnotations(); Annotation[] declaredAnnotations = NonPublicAnnotatedClass.class.getDeclaredAnnotations();
assertEquals(1, declaredAnnotations.length); assertEquals(1, declaredAnnotations.length);
Annotation annotation = declaredAnnotations[0]; Annotation annotation = declaredAnnotations[0];
@ -515,7 +522,7 @@ public class AnnotationUtilsTests {
} }
@Test @Test
public void getDefaultValueFromAnnotationType() throws Exception { public void getDefaultValueFromAnnotationType() {
assertEquals(Ordered.LOWEST_PRECEDENCE, getDefaultValue(Order.class, VALUE)); assertEquals(Ordered.LOWEST_PRECEDENCE, getDefaultValue(Order.class, VALUE));
assertEquals(Ordered.LOWEST_PRECEDENCE, getDefaultValue(Order.class)); assertEquals(Ordered.LOWEST_PRECEDENCE, getDefaultValue(Order.class));
} }
@ -547,7 +554,7 @@ public class AnnotationUtilsTests {
} }
@Test @Test
public void getRepeatableAnnotationsDeclaredOnClassWithAttributeAliases() throws Exception { public void getRepeatableAnnotationsDeclaredOnClassWithAttributeAliases() {
final List<String> expectedLocations = asList("A", "B"); final List<String> expectedLocations = asList("A", "B");
Set<ContextConfig> annotations = getRepeatableAnnotations(ConfigHierarchyTestCase.class, ContextConfig.class, null); Set<ContextConfig> annotations = getRepeatableAnnotations(ConfigHierarchyTestCase.class, ContextConfig.class, null);
@ -1750,6 +1757,18 @@ public class AnnotationUtilsTests {
public static class SubTransactionalAndOrderedClass extends TransactionalAndOrderedClass { public static class SubTransactionalAndOrderedClass extends TransactionalAndOrderedClass {
} }
public interface InterfaceWithGenericAnnotatedMethod<T> {
@Order
void foo(T t);
}
public static class ImplementsInterfaceWithGenericAnnotatedMethod implements InterfaceWithGenericAnnotatedMethod<String> {
public void foo(String t) {
}
}
public interface InterfaceWithAnnotatedMethod { public interface InterfaceWithAnnotatedMethod {
@Order @Order

Loading…
Cancel
Save