|
|
|
@ -18,6 +18,7 @@ package org.springframework.core; |
|
|
|
|
|
|
|
|
|
|
|
import java.lang.reflect.Array; |
|
|
|
import java.lang.reflect.Array; |
|
|
|
import java.lang.reflect.GenericArrayType; |
|
|
|
import java.lang.reflect.GenericArrayType; |
|
|
|
|
|
|
|
import java.lang.reflect.MalformedParameterizedTypeException; |
|
|
|
import java.lang.reflect.Method; |
|
|
|
import java.lang.reflect.Method; |
|
|
|
import java.lang.reflect.ParameterizedType; |
|
|
|
import java.lang.reflect.ParameterizedType; |
|
|
|
import java.lang.reflect.Type; |
|
|
|
import java.lang.reflect.Type; |
|
|
|
@ -74,12 +75,12 @@ public abstract class GenericTypeResolver { |
|
|
|
* @param clazz the class to resolve type variables against |
|
|
|
* @param clazz the class to resolve type variables against |
|
|
|
* @return the corresponding generic parameter or return type |
|
|
|
* @return the corresponding generic parameter or return type |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public static Class<?> resolveParameterType(MethodParameter methodParam, Class clazz) { |
|
|
|
public static Class<?> resolveParameterType(MethodParameter methodParam, Class<?> clazz) { |
|
|
|
Type genericType = getTargetType(methodParam); |
|
|
|
Type genericType = getTargetType(methodParam); |
|
|
|
Assert.notNull(clazz, "Class must not be null"); |
|
|
|
Assert.notNull(clazz, "Class must not be null"); |
|
|
|
Map<TypeVariable, Type> typeVariableMap = getTypeVariableMap(clazz); |
|
|
|
Map<TypeVariable, Type> typeVariableMap = getTypeVariableMap(clazz); |
|
|
|
Type rawType = getRawType(genericType, typeVariableMap); |
|
|
|
Type rawType = getRawType(genericType, typeVariableMap); |
|
|
|
Class result = (rawType instanceof Class ? (Class) rawType : methodParam.getParameterType()); |
|
|
|
Class<?> result = (rawType instanceof Class ? (Class) rawType : methodParam.getParameterType()); |
|
|
|
methodParam.setParameterType(result); |
|
|
|
methodParam.setParameterType(result); |
|
|
|
methodParam.typeVariableMap = typeVariableMap; |
|
|
|
methodParam.typeVariableMap = typeVariableMap; |
|
|
|
return result; |
|
|
|
return result; |
|
|
|
@ -227,7 +228,7 @@ public abstract class GenericTypeResolver { |
|
|
|
* @param genericIfc the generic interface or superclass to resolve the type argument from |
|
|
|
* @param genericIfc the generic interface or superclass to resolve the type argument from |
|
|
|
* @return the resolved type of the argument, or {@code null} if not resolvable |
|
|
|
* @return the resolved type of the argument, or {@code null} if not resolvable |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public static Class<?> resolveTypeArgument(Class clazz, Class genericIfc) { |
|
|
|
public static Class<?> resolveTypeArgument(Class<?> clazz, Class<?> genericIfc) { |
|
|
|
Class[] typeArgs = resolveTypeArguments(clazz, genericIfc); |
|
|
|
Class[] typeArgs = resolveTypeArguments(clazz, genericIfc); |
|
|
|
if (typeArgs == null) { |
|
|
|
if (typeArgs == null) { |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
@ -248,11 +249,11 @@ public abstract class GenericTypeResolver { |
|
|
|
* @return the resolved type of each argument, with the array size matching the |
|
|
|
* @return the resolved type of each argument, with the array size matching the |
|
|
|
* number of actual type arguments, or {@code null} if not resolvable |
|
|
|
* number of actual type arguments, or {@code null} if not resolvable |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public static Class[] resolveTypeArguments(Class clazz, Class genericIfc) { |
|
|
|
public static Class[] resolveTypeArguments(Class<?> clazz, Class<?> genericIfc) { |
|
|
|
return doResolveTypeArguments(clazz, clazz, genericIfc); |
|
|
|
return doResolveTypeArguments(clazz, clazz, genericIfc); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private static Class[] doResolveTypeArguments(Class ownerClass, Class classToIntrospect, Class genericIfc) { |
|
|
|
private static Class[] doResolveTypeArguments(Class<?> ownerClass, Class<?> classToIntrospect, Class<?> genericIfc) { |
|
|
|
while (classToIntrospect != null) { |
|
|
|
while (classToIntrospect != null) { |
|
|
|
if (genericIfc.isInterface()) { |
|
|
|
if (genericIfc.isInterface()) { |
|
|
|
Type[] ifcs = classToIntrospect.getGenericInterfaces(); |
|
|
|
Type[] ifcs = classToIntrospect.getGenericInterfaces(); |
|
|
|
@ -264,18 +265,23 @@ public abstract class GenericTypeResolver { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
|
Class[] result = doResolveTypeArguments( |
|
|
|
try { |
|
|
|
ownerClass, classToIntrospect.getGenericSuperclass(), genericIfc); |
|
|
|
Class[] result = doResolveTypeArguments(ownerClass, classToIntrospect.getGenericSuperclass(), genericIfc); |
|
|
|
if (result != null) { |
|
|
|
if (result != null) { |
|
|
|
return result; |
|
|
|
return result; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
catch (MalformedParameterizedTypeException ex) { |
|
|
|
|
|
|
|
// from getGenericSuperclass() - return null to skip further superclass traversal
|
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
classToIntrospect = classToIntrospect.getSuperclass(); |
|
|
|
classToIntrospect = classToIntrospect.getSuperclass(); |
|
|
|
} |
|
|
|
} |
|
|
|
return null; |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private static Class[] doResolveTypeArguments(Class ownerClass, Type ifc, Class genericIfc) { |
|
|
|
private static Class[] doResolveTypeArguments(Class<?> ownerClass, Type ifc, Class<?> genericIfc) { |
|
|
|
if (ifc instanceof ParameterizedType) { |
|
|
|
if (ifc instanceof ParameterizedType) { |
|
|
|
ParameterizedType paramIfc = (ParameterizedType) ifc; |
|
|
|
ParameterizedType paramIfc = (ParameterizedType) ifc; |
|
|
|
Type rawType = paramIfc.getRawType(); |
|
|
|
Type rawType = paramIfc.getRawType(); |
|
|
|
@ -301,7 +307,7 @@ public abstract class GenericTypeResolver { |
|
|
|
/** |
|
|
|
/** |
|
|
|
* Extract a class instance from given Type. |
|
|
|
* Extract a class instance from given Type. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private static Class extractClass(Class ownerClass, Type arg) { |
|
|
|
private static Class<?> extractClass(Class<?> ownerClass, Type arg) { |
|
|
|
if (arg instanceof ParameterizedType) { |
|
|
|
if (arg instanceof ParameterizedType) { |
|
|
|
return extractClass(ownerClass, ((ParameterizedType) arg).getRawType()); |
|
|
|
return extractClass(ownerClass, ((ParameterizedType) arg).getRawType()); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -368,7 +374,7 @@ public abstract class GenericTypeResolver { |
|
|
|
* {@link Class concrete classes} for the specified {@link Class}. Searches |
|
|
|
* {@link Class concrete classes} for the specified {@link Class}. Searches |
|
|
|
* all super types, enclosing types and interfaces. |
|
|
|
* all super types, enclosing types and interfaces. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public static Map<TypeVariable, Type> getTypeVariableMap(Class clazz) { |
|
|
|
public static Map<TypeVariable, Type> getTypeVariableMap(Class<?> clazz) { |
|
|
|
Map<TypeVariable, Type> typeVariableMap = typeVariableCache.get(clazz); |
|
|
|
Map<TypeVariable, Type> typeVariableMap = typeVariableCache.get(clazz); |
|
|
|
|
|
|
|
|
|
|
|
if (typeVariableMap == null) { |
|
|
|
if (typeVariableMap == null) { |
|
|
|
@ -377,29 +383,38 @@ public abstract class GenericTypeResolver { |
|
|
|
// interfaces
|
|
|
|
// interfaces
|
|
|
|
extractTypeVariablesFromGenericInterfaces(clazz.getGenericInterfaces(), typeVariableMap); |
|
|
|
extractTypeVariablesFromGenericInterfaces(clazz.getGenericInterfaces(), typeVariableMap); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
// super class
|
|
|
|
// super class
|
|
|
|
Type genericType = clazz.getGenericSuperclass(); |
|
|
|
Class<?> type = clazz; |
|
|
|
Class type = clazz.getSuperclass(); |
|
|
|
while (type.getSuperclass() != null && !Object.class.equals(type.getSuperclass())) { |
|
|
|
while (type != null && !Object.class.equals(type)) { |
|
|
|
Type genericType = type.getGenericSuperclass(); |
|
|
|
if (genericType instanceof ParameterizedType) { |
|
|
|
if (genericType instanceof ParameterizedType) { |
|
|
|
ParameterizedType pt = (ParameterizedType) genericType; |
|
|
|
ParameterizedType pt = (ParameterizedType) genericType; |
|
|
|
populateTypeMapFromParameterizedType(pt, typeVariableMap); |
|
|
|
populateTypeMapFromParameterizedType(pt, typeVariableMap); |
|
|
|
} |
|
|
|
} |
|
|
|
extractTypeVariablesFromGenericInterfaces(type.getGenericInterfaces(), typeVariableMap); |
|
|
|
extractTypeVariablesFromGenericInterfaces(type.getSuperclass().getGenericInterfaces(), typeVariableMap); |
|
|
|
genericType = type.getGenericSuperclass(); |
|
|
|
|
|
|
|
type = type.getSuperclass(); |
|
|
|
type = type.getSuperclass(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (MalformedParameterizedTypeException ex) { |
|
|
|
|
|
|
|
// from getGenericSuperclass() - ignore and continue with member class check
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
// enclosing class
|
|
|
|
// enclosing class
|
|
|
|
type = clazz; |
|
|
|
Class<?> type = clazz; |
|
|
|
while (type.isMemberClass()) { |
|
|
|
while (type.isMemberClass()) { |
|
|
|
genericType = type.getGenericSuperclass(); |
|
|
|
Type genericType = type.getGenericSuperclass(); |
|
|
|
if (genericType instanceof ParameterizedType) { |
|
|
|
if (genericType instanceof ParameterizedType) { |
|
|
|
ParameterizedType pt = (ParameterizedType) genericType; |
|
|
|
ParameterizedType pt = (ParameterizedType) genericType; |
|
|
|
populateTypeMapFromParameterizedType(pt, typeVariableMap); |
|
|
|
populateTypeMapFromParameterizedType(pt, typeVariableMap); |
|
|
|
} |
|
|
|
} |
|
|
|
type = type.getEnclosingClass(); |
|
|
|
type = type.getEnclosingClass(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
catch (MalformedParameterizedTypeException ex) { |
|
|
|
|
|
|
|
// from getGenericSuperclass() - ignore and preserve previously accumulated type variables
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
typeVariableCache.put(clazz, typeVariableMap); |
|
|
|
typeVariableCache.put(clazz, typeVariableMap); |
|
|
|
} |
|
|
|
} |
|
|
|
|