New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SPR-16216 Make annotation of param can be inherited #1600

Open
wants to merge 1 commit into
base: master
from
File filter...
Filter file types
Jump to file or symbol
Failed to load files and symbols.
+97 −10
Diff settings

Always

Just for now

@@ -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 Type getNestedGenericParameterType() {
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;
@@ -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;

@@ -69,6 +67,11 @@
*/
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
@@ -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();
}


ProTip! Use n and p to navigate between commits in a pull request.