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 1a6dda592df7..2fee7a51df6a 100644
--- a/spring-core/src/main/java/org/springframework/core/MethodParameter.java
+++ b/spring-core/src/main/java/org/springframework/core/MethodParameter.java
@@ -37,6 +37,7 @@
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
+import org.springframework.util.ReflectionUtils;
/**
* Helper class that encapsulates the specification of a method parameter, i.e. a {@link Method}
@@ -513,13 +514,25 @@ public boolean hasMethodAnnotation(Class annotationTyp
public Annotation[] getParameterAnnotations() {
Annotation[] paramAnns = this.parameterAnnotations;
if (paramAnns == null) {
- Annotation[][] annotationArray = this.executable.getParameterAnnotations();
- if (this.parameterIndex >= 0 && this.parameterIndex < annotationArray.length) {
- paramAnns = adaptAnnotationArray(annotationArray[this.parameterIndex]);
+ // Executable is a method
+ if(this.getMethod() != null){
+ if (this.parameterIndex >= 0 && this.parameterIndex < this.executable.getParameterCount()) {
+ paramAnns = ReflectionUtils.getInheritedParamAnnotations(this.getMethod(), this.parameterIndex);
+ }
+ else{
+ paramAnns = ReflectionUtils.getInheritedParamAnnotations(this.getMethod(), 0);
+ }
}
- else {
- paramAnns = new Annotation[0];
+ else{
+ Annotation[][] annotationArray = this.executable.getParameterAnnotations();
+ if (this.parameterIndex >= 0 && this.parameterIndex < annotationArray.length) {
+ paramAnns = adaptAnnotationArray(annotationArray[this.parameterIndex]);
+ }
+ else {
+ paramAnns = new Annotation[0];
+ }
}
+
this.parameterAnnotations = paramAnns;
}
return paramAnns;
diff --git a/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java b/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java
index d5a24687c3e8..c885c9d5b6a8 100644
--- a/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java
+++ b/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java
@@ -16,6 +16,7 @@
package org.springframework.util;
+import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
@@ -23,11 +24,8 @@
import java.lang.reflect.Modifier;
import java.lang.reflect.UndeclaredThrowableException;
import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
+import java.util.stream.Collectors;
import org.springframework.lang.Nullable;
@@ -69,6 +67,11 @@ public abstract class ReflectionUtils {
*/
private static final Map, Field[]> declaredFieldsCache = new ConcurrentReferenceHashMap<>(256);
+ /**
+ * Cache for {@link ReflectionUtils#getInheritedParamAnnotations(Method, int)} ()}, allowing for fast iteration.
+ */
+ private static final Map inheritedParamAnnotationsCache = new ConcurrentReferenceHashMap<>(256);
+
/**
* Attempt to find a {@link Field field} on the supplied {@link Class} with the
@@ -782,6 +785,76 @@ public static void shallowCopyFieldState(final Object src, final Object dest) {
}, COPYABLE_FIELDS);
}
+ /**
+ * This will get parameter's annotations from super class or interface
+ * The order of result is current class, interfaces, super class
+ * @param method which method's param want to get
+ * @param paramIndex param index of method
+ * @return
+ */
+ public static Annotation[] getInheritedParamAnnotations(final Method method, int paramIndex){
+ Annotation[][] cachedResult = inheritedParamAnnotationsCache.get(method);
+ if(cachedResult == null){
+ inheritedParamAnnotationsCache.put(method, new Annotation[method.getParameterCount()][]);
+
+ // Recursion call it again make sure cache is available
+ return getInheritedParamAnnotations(method, paramIndex);
+ }
+ else{
+ if(cachedResult[paramIndex] == null){
+ List annotations = getInheritedParamAnnotations(method.getDeclaringClass(), method, paramIndex);
+
+ // Remove duplicated annotation in result
+ Set annotationSet = new LinkedHashSet<>(annotations);
+ Annotation[] result = annotationSet.toArray(new Annotation[0]);
+
+ // Add result to cache
+ cachedResult[paramIndex] = result;
+ }
+
+ return cachedResult[paramIndex];
+ }
+ }
+
+ @SuppressWarnings("unchecked call")
+ private static List getInheritedParamAnnotations(@Nullable final Class clazz, final Method method, final int paramIndex) {
+ if(clazz == null){
+ return new ArrayList<>();
+ }
+
+ Annotation[][] paramAnnotations = method.getParameterAnnotations();
+
+ final int index;
+ if (paramIndex >= paramAnnotations.length || paramIndex< 0) {
+ index = 0; // Should we throw a exception?
+ }
+ else{
+ index = paramIndex;
+ }
+
+ List annotations = new ArrayList<>();
+
+ // Add all annotations from current clazz
+ try{
+ // Get method with same signature
+ Method currentMethod = clazz.getDeclaredMethod(method.getName(), method.getParameterTypes());
+ Annotation[] currentParamAnnotations = currentMethod.getParameterAnnotations()[paramIndex];
+ annotations.addAll(Arrays.asList(currentParamAnnotations));
+ }catch (NoSuchMethodException ignore){
+ // Ignored 'NoSuchMethodException' for class which has no method
+ }
+
+ // Then add all annotations from interfaces(recursion call)
+ annotations.addAll(Arrays.stream(clazz.getInterfaces())
+ .map(item-> getInheritedParamAnnotations(item, method, index))
+ .flatMap(Collection::stream).collect(Collectors.toList()));
+
+ // Add all annotations from super class finally(recursion call)
+ annotations.addAll(getInheritedParamAnnotations(clazz.getSuperclass(), method, index));
+
+ return annotations;
+ }
+
/**
* Clear the internal method/field cache.
* @since 4.2.4
@@ -789,6 +862,7 @@ public static void shallowCopyFieldState(final Object src, final Object dest) {
public static void clearCache() {
declaredMethodsCache.clear();
declaredFieldsCache.clear();
+ inheritedParamAnnotationsCache.clear();
}