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