Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down Expand Up @@ -513,13 +514,25 @@ public <A extends Annotation> boolean hasMethodAnnotation(Class<A> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,16 @@

package org.springframework.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
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;

Expand Down Expand Up @@ -69,6 +67,11 @@ public abstract class ReflectionUtils {
*/
private static final Map<Class<?>, Field[]> declaredFieldsCache = new ConcurrentReferenceHashMap<>(256);

/**
* Cache for {@link ReflectionUtils#getInheritedParamAnnotations(Method, int)} ()}, allowing for fast iteration.
*/
private static final Map<Method, Annotation[][]> inheritedParamAnnotationsCache = new ConcurrentReferenceHashMap<>(256);


/**
* Attempt to find a {@link Field field} on the supplied {@link Class} with the
Expand Down Expand Up @@ -782,13 +785,84 @@ 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<Annotation> annotations = getInheritedParamAnnotations(method.getDeclaringClass(), method, paramIndex);

// Remove duplicated annotation in result
Set<Annotation> 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<Annotation> 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<Annotation> 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
*/
public static void clearCache() {
declaredMethodsCache.clear();
declaredFieldsCache.clear();
inheritedParamAnnotationsCache.clear();
}


Expand Down