diff --git a/spring-core/src/main/java/org/springframework/core/MethodParameter.java b/spring-core/src/main/java/org/springframework/core/MethodParameter.java index 7267704ed45..2e6aa39ff83 100644 --- a/spring-core/src/main/java/org/springframework/core/MethodParameter.java +++ b/spring-core/src/main/java/org/springframework/core/MethodParameter.java @@ -337,7 +337,6 @@ public class MethodParameter { return (isOptional() ? nested() : this); } - /** * Set a containing class to resolve the parameter type against. */ @@ -390,9 +389,19 @@ public class MethodParameter { paramType = (method != null ? method.getGenericReturnType() : void.class); } else { - paramType = (this.method != null ? - this.method.getGenericParameterTypes()[this.parameterIndex] : - this.constructor.getGenericParameterTypes()[this.parameterIndex]); + Type[] genericParameterTypes = (this.method != null ? + this.method.getGenericParameterTypes() : this.constructor.getGenericParameterTypes()); + int index = this.parameterIndex; + if (this.constructor != null && this.constructor.getDeclaringClass().isMemberClass() && + !Modifier.isStatic(this.constructor.getDeclaringClass().getModifiers()) && + genericParameterTypes.length == this.constructor.getParameterTypes().length - 1) { + // Bug in javac: type array excludes enclosing instance parameter + // for inner classes with at least one generic constructor parameter, + // so access it with the actual parameter index lowered by 1 + index = this.parameterIndex - 1; + } + paramType = (index >= 0 && index < genericParameterTypes.length ? + genericParameterTypes[index] : getParameterType()); } this.genericParameterType = paramType; } @@ -497,12 +506,8 @@ public class MethodParameter { // for inner classes, so access it with the actual parameter index lowered by 1 index = this.parameterIndex - 1; } - if (index >= 0 && index < annotationArray.length) { - paramAnns = adaptAnnotationArray(annotationArray[index]); - } - else { - paramAnns = EMPTY_ANNOTATION_ARRAY; - } + paramAnns = (index >= 0 && index < annotationArray.length ? + adaptAnnotationArray(annotationArray[index]) : EMPTY_ANNOTATION_ARRAY); this.parameterAnnotations = paramAnns; } return paramAnns; diff --git a/spring-core/src/test/java/org/springframework/core/MethodParameterTests.java b/spring-core/src/test/java/org/springframework/core/MethodParameterTests.java index d7b01d52ed3..3e1849b7841 100644 --- a/spring-core/src/test/java/org/springframework/core/MethodParameterTests.java +++ b/spring-core/src/test/java/org/springframework/core/MethodParameterTests.java @@ -22,6 +22,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import java.util.concurrent.Callable; import org.junit.Before; import org.junit.Test; @@ -96,7 +97,7 @@ public class MethodParameterTests { @Test // SPR-16652 public void annotatedConstructorParameterInInnerClass() throws Exception { - Constructor constructor = InnerClass.class.getConstructor(getClass(), String.class, Integer.class); + Constructor constructor = InnerClass.class.getConstructor(getClass(), String.class, Callable.class); MethodParameter methodParameter = MethodParameter.forMethodOrConstructor(constructor, 0); assertEquals(getClass(), methodParameter.getParameterType()); @@ -107,10 +108,28 @@ public class MethodParameterTests { assertNotNull("Failed to find @Param annotation", methodParameter.getParameterAnnotation(Param.class)); methodParameter = MethodParameter.forMethodOrConstructor(constructor, 2); - assertEquals(Integer.class, methodParameter.getParameterType()); + assertEquals(Callable.class, methodParameter.getParameterType()); assertNull(methodParameter.getParameterAnnotation(Param.class)); } + @Test // SPR-16734 + public void genericConstructorParameterInInnerClass() throws Exception { + Constructor constructor = InnerClass.class.getConstructor(getClass(), String.class, Callable.class); + + MethodParameter methodParameter = MethodParameter.forMethodOrConstructor(constructor, 0); + assertEquals(getClass(), methodParameter.getParameterType()); + assertEquals(getClass(), methodParameter.getGenericParameterType()); + + methodParameter = MethodParameter.forMethodOrConstructor(constructor, 1); + assertEquals(String.class, methodParameter.getParameterType()); + assertEquals(String.class, methodParameter.getGenericParameterType()); + + methodParameter = MethodParameter.forMethodOrConstructor(constructor, 2); + assertEquals(Callable.class, methodParameter.getParameterType()); + assertEquals(ResolvableType.forClassWithGenerics(Callable.class, Integer.class).getType(), + methodParameter.getGenericParameterType()); + } + public int method(String p1, long p2) { return 42; @@ -126,7 +145,7 @@ public class MethodParameterTests { @SuppressWarnings("unused") private class InnerClass { - public InnerClass(@Param String s, Integer i) { + public InnerClass(@Param String s, Callable i) { } }