From b121c50a9bcf929778c79e0e72fabd8d99156635 Mon Sep 17 00:00:00 2001 From: Christopher Rued Date: Tue, 10 Oct 2017 22:55:54 -0400 Subject: [PATCH] Enhance AnnotationUtils to find annotations on generic interfaces When scanning for annotations on a class that implements a generic interface where the generic type is specified in the implementing class, annotation scanning would fail to identify annotations from the interface since the parameter types do not match. For example, given an interface: public interface Foo { @Order void foo(T t); } and a class public class StringFoo implements Foo { public void foo(String s) { ... } } when scanning StringFoo.foo for annotations, no annotations were identified. This commit changes annotation scanning so that when scanning for annotations, the parameters are compared for assignability (using Class.isAssignableFrom()) rather than requiring exact matches. Issue: SEC-3081 Correct formatting --- .gitignore | 2 + .../core/annotation/AnnotationUtils.java | 41 +++++++++++++++---- .../core/annotation/AnnotationUtilsTests.java | 18 ++++++++ 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index a9136e7ebca5..6c24cda42411 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,5 @@ out test-output atlassian-ide-plugin.xml .gradletasknamecache +/.nb-gradle/ + 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 b7ed7e8ec203..502630baf87e 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 @@ -581,21 +581,46 @@ private static A searchOnInterfaces(Method method, Class< A annotation = null; for (Class iface : ifcs) { if (isInterfaceWithAnnotatedMethods(iface)) { - try { - Method equivalentMethod = iface.getMethod(method.getName(), method.getParameterTypes()); + Method equivalentMethod = findMethodWithMatchingParameters(iface, method.getName(), method.getParameterTypes()); + if (equivalentMethod != null) { annotation = getAnnotation(equivalentMethod, annotationType); - } - catch (NoSuchMethodException ex) { - // Skip this interface - it doesn't have the method... - } - if (annotation != null) { - break; + if (annotation != null) { + break; + } } } } return annotation; } + private static Method findMethodWithMatchingParameters(Class cls, String methodName, Class[] parameterTypes) { + try { + // first try exact match + return cls.getMethod(methodName, parameterTypes); + } catch (NoSuchMethodException e) { + // then look for method with assignable parameters + for (Method method : cls.getMethods()) { + if (method.getName().equals(methodName) && + method.getParameterCount() == parameterTypes.length && + parametersAreAssignable(parameterTypes, method)) { + return method; + } + } + } + // return null if there is no match + return null; + } + + @SuppressWarnings("unchecked") + private static boolean parametersAreAssignable(Class[] parameterTypes, Method method) { + for (int j = 0; j < parameterTypes.length; j++) { + if (!method.getParameterTypes()[j].isAssignableFrom(parameterTypes[j])) { + return false; + } + } + return true; + } + static boolean isInterfaceWithAnnotatedMethods(Class iface) { Boolean found = annotatedInterfaceCache.get(iface); if (found != null) { 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 92e00fccb192..d4c16c9823d6 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 @@ -194,6 +194,13 @@ public void findMethodAnnotationFromInterface() throws Exception { assertNotNull(order); } + @Test + public void findMethodAnnotationFromGenericInterfaceSuper() 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"); @@ -1755,6 +1762,17 @@ public static class TransactionalAndOrderedClass extends TransactionalClass { public static class SubTransactionalAndOrderedClass extends TransactionalAndOrderedClass { } + public static interface InterfaceWithGenericAnnotatedMethod { + @Order + void foo(T t); + } + + public static class ImplementsInterfaceWithGenericAnnotatedMethod implements InterfaceWithGenericAnnotatedMethod { + public void foo(String t) { + // no body in test method + } + } + public interface InterfaceWithAnnotatedMethod { @Order