Refine ResolvableType class
- Support for serialization - Allow programmatic creation of an array from a given component type - Allow programmatic creation with given generics - Extract generics from Class types using Class.getTypeParameters() - Move TypeVariableResolver to an inner class (and make method private) - Refine 'resolve()' algorithm Issue: SPR-10973
This commit is contained in:
parent
7ffd05a8e3
commit
3337fd32cb
|
@ -256,10 +256,22 @@ public abstract class GenericTypeResolver {
|
|||
* @deprecated as of Spring 4.0 in favor of {@link ResolvableType}
|
||||
*/
|
||||
@Deprecated
|
||||
public static Class<?> resolveType(Type genericType, Map<TypeVariable, Type> typeVariableMap) {
|
||||
TypeVariableResolver variableResolver = new TypeVariableMapResolver(typeVariableMap);
|
||||
Class<?> resolved = ResolvableType.forType(genericType, variableResolver).resolve();
|
||||
return (resolved == null ? Object.class : resolved);
|
||||
public static Class<?> resolveType(Type genericType, final Map<TypeVariable, Type> typeVariableMap) {
|
||||
|
||||
ResolvableType.VariableResolver variableResolver = new ResolvableType.VariableResolver() {
|
||||
@Override
|
||||
public ResolvableType resolveVariable(TypeVariable<?> variable) {
|
||||
Type type = typeVariableMap.get(variable);
|
||||
return (type == null ? null : ResolvableType.forType(type));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getSource() {
|
||||
return typeVariableMap;
|
||||
}
|
||||
};
|
||||
|
||||
return ResolvableType.forType(genericType, variableResolver).resolve(Object.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -303,40 +315,4 @@ public abstract class GenericTypeResolver {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adapts a {@code typeVariableMap} to a {@link TypeVariableResolver}.
|
||||
*/
|
||||
private static class TypeVariableMapResolver implements TypeVariableResolver {
|
||||
|
||||
private final Map<TypeVariable, Type> typeVariableMap;
|
||||
|
||||
public TypeVariableMapResolver(Map<TypeVariable, Type> typeVariableMap) {
|
||||
Assert.notNull("TypeVariableMap must not be null");
|
||||
this.typeVariableMap = typeVariableMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type resolveVariable(TypeVariable typeVariable) {
|
||||
return this.typeVariableMap.get(typeVariable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof TypeVariableMapResolver) {
|
||||
TypeVariableMapResolver other = (TypeVariableMapResolver) obj;
|
||||
return this.typeVariableMap.equals(other.typeVariableMap);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.typeVariableMap.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.core;
|
||||
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
|
@ -41,9 +43,9 @@ import org.springframework.util.StringUtils;
|
|||
*
|
||||
* <p>{@code ResolvableTypes} may be obtained from {@link #forField(Field) fields},
|
||||
* {@link #forMethodParameter(Method, int) method parameters},
|
||||
* {@link #forMethodReturnType(Method) method returns}, {@link #forClass(Class) classes}, or
|
||||
* directly from a {@link #forType(Type) java.lang.reflect.Type}. Most methods on this class
|
||||
* will themselves return {@link ResolvableType}s, allowing easy navigation. For example:
|
||||
* {@link #forMethodReturnType(Method) method returns} or
|
||||
* {@link #forClass(Class) classes}. Most methods on this class will themselves return
|
||||
* {@link ResolvableType}s, allowing easy navigation. For example:
|
||||
* <pre class="code">
|
||||
* private HashMap<Integer, List<String>> myMap;
|
||||
*
|
||||
|
@ -61,7 +63,6 @@ import org.springframework.util.StringUtils;
|
|||
* @author Phillip Webb
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.0
|
||||
* @see TypeVariableResolver
|
||||
* @see #forField(Field)
|
||||
* @see #forMethodParameter(Method, int)
|
||||
* @see #forMethodReturnType(Method)
|
||||
|
@ -69,7 +70,7 @@ import org.springframework.util.StringUtils;
|
|||
* @see #forClass(Class)
|
||||
* @see #forType(Type)
|
||||
*/
|
||||
public final class ResolvableType implements TypeVariableResolver {
|
||||
public final class ResolvableType implements Serializable {
|
||||
|
||||
private static ConcurrentReferenceHashMap<ResolvableType, ResolvableType> cache =
|
||||
new ConcurrentReferenceHashMap<ResolvableType, ResolvableType>();
|
||||
|
@ -79,7 +80,7 @@ public final class ResolvableType implements TypeVariableResolver {
|
|||
* {@code ResolvableType} returned when no value is available. {@code NONE} is used
|
||||
* in preference to {@code null} so that multiple method calls can be safely chained.
|
||||
*/
|
||||
public static final ResolvableType NONE = new ResolvableType(null, null);
|
||||
public static final ResolvableType NONE = new ResolvableType(null, null, null);
|
||||
|
||||
|
||||
private static final ResolvableType[] EMPTY_TYPES_ARRAY = new ResolvableType[0];
|
||||
|
@ -91,9 +92,9 @@ public final class ResolvableType implements TypeVariableResolver {
|
|||
private final Type type;
|
||||
|
||||
/**
|
||||
* The {@link TypeVariableResolver} to use or {@code null} if no resolver is available.
|
||||
* The {@link VariableResolver} to use or {@code null} if no resolver is available.
|
||||
*/
|
||||
private final TypeVariableResolver variableResolver;
|
||||
private final VariableResolver variableResolver;
|
||||
|
||||
/**
|
||||
* Stored copy of the resolved value or {@code null} if the resolve method has not
|
||||
|
@ -101,15 +102,23 @@ public final class ResolvableType implements TypeVariableResolver {
|
|||
*/
|
||||
private Class<?> resolved;
|
||||
|
||||
/**
|
||||
* The component type for an array or {@code null} if the type should be deduced.
|
||||
*/
|
||||
private final ResolvableType componentType;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Private constructor used to create a new {@link ResolvableType}.
|
||||
* @param type the underlying java type (may only be {@code null} for {@link #NONE})
|
||||
* @param variableResolver the resolver used for {@link TypeVariable}s (may be {@code null})
|
||||
* @param componentType an option declared component type for arrays (may be {@code null})
|
||||
*/
|
||||
private ResolvableType(Type type, TypeVariableResolver variableResolver) {
|
||||
private ResolvableType(Type type, VariableResolver variableResolver, ResolvableType componentType) {
|
||||
this.type = type;
|
||||
this.variableResolver = variableResolver;
|
||||
this.componentType = componentType;
|
||||
}
|
||||
|
||||
|
||||
|
@ -203,10 +212,12 @@ public final class ResolvableType implements TypeVariableResolver {
|
|||
if (this == NONE) {
|
||||
return NONE;
|
||||
}
|
||||
if (this.componentType != null) {
|
||||
return this.componentType;
|
||||
}
|
||||
if (this.type instanceof Class) {
|
||||
Class<?> componentType = ((Class<?>) this.type).getComponentType();
|
||||
return (componentType == null ? NONE : forType(componentType,
|
||||
this.variableResolver));
|
||||
return forType(componentType, this.variableResolver);
|
||||
}
|
||||
if (this.type instanceof GenericArrayType) {
|
||||
return forType(((GenericArrayType) this.type).getGenericComponentType(),
|
||||
|
@ -272,11 +283,11 @@ public final class ResolvableType implements TypeVariableResolver {
|
|||
* @see #getInterfaces()
|
||||
*/
|
||||
public ResolvableType getSuperType() {
|
||||
Class<?> resolved = resolve();
|
||||
final Class<?> resolved = resolve();
|
||||
if (resolved == null || resolved.getGenericSuperclass() == null) {
|
||||
return NONE;
|
||||
}
|
||||
return forType(resolved.getGenericSuperclass(), this);
|
||||
return forType(SerializableTypeWrapper.forGenericSuperclass(resolved), asVariableResolver());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -286,16 +297,11 @@ public final class ResolvableType implements TypeVariableResolver {
|
|||
* @see #getSuperType()
|
||||
*/
|
||||
public ResolvableType[] getInterfaces() {
|
||||
Class<?> resolved = resolve();
|
||||
final Class<?> resolved = resolve();
|
||||
if (resolved == null || ObjectUtils.isEmpty(resolved.getGenericInterfaces())) {
|
||||
return EMPTY_TYPES_ARRAY;
|
||||
}
|
||||
Type[] interfaceTypes = resolved.getGenericInterfaces();
|
||||
ResolvableType[] interfaces = new ResolvableType[interfaceTypes.length];
|
||||
for (int i = 0; i < interfaceTypes.length; i++) {
|
||||
interfaces[i] = forType(interfaceTypes[i], this);
|
||||
}
|
||||
return interfaces;
|
||||
return forTypes(SerializableTypeWrapper.forGenericInterfaces(resolved), asVariableResolver());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -407,11 +413,15 @@ public final class ResolvableType implements TypeVariableResolver {
|
|||
if (this == NONE) {
|
||||
return EMPTY_TYPES_ARRAY;
|
||||
}
|
||||
if (this.type instanceof Class<?>) {
|
||||
Class<?> typeClass = (Class<?>) this.type;
|
||||
return forTypes(SerializableTypeWrapper.forTypeParameters(typeClass), this.variableResolver);
|
||||
}
|
||||
if (this.type instanceof ParameterizedType) {
|
||||
Type[] genericTypes = ((ParameterizedType) getType()).getActualTypeArguments();
|
||||
ResolvableType[] generics = new ResolvableType[genericTypes.length];
|
||||
for (int i = 0; i < genericTypes.length; i++) {
|
||||
generics[i] = forType(genericTypes[i], this);
|
||||
Type[] actualTypeArguments = ((ParameterizedType) this.type).getActualTypeArguments();
|
||||
ResolvableType[] generics = new ResolvableType[actualTypeArguments.length];
|
||||
for (int i = 0; i < actualTypeArguments.length; i++) {
|
||||
generics[i] = forType(actualTypeArguments[i], this.variableResolver);
|
||||
}
|
||||
return generics;
|
||||
}
|
||||
|
@ -475,10 +485,8 @@ public final class ResolvableType implements TypeVariableResolver {
|
|||
*/
|
||||
public Class<?> resolve(Class<?> fallback) {
|
||||
if (this.resolved == null) {
|
||||
synchronized (this) {
|
||||
this.resolved = resolveClass();
|
||||
this.resolved = (this.resolved == null ? void.class : this.resolved);
|
||||
}
|
||||
Class<?> resolvedClass = resolveClass();
|
||||
this.resolved = (resolvedClass == null ? void.class : resolvedClass);
|
||||
}
|
||||
return (this.resolved == void.class ? fallback : this.resolved);
|
||||
}
|
||||
|
@ -495,27 +503,40 @@ public final class ResolvableType implements TypeVariableResolver {
|
|||
|
||||
/**
|
||||
* Resolve this type by a single level, returning the resolved value or {@link #NONE}.
|
||||
* NOTE: the returned {@link ResolvableType} should only be used as an intermediary as
|
||||
* it cannot be serialized.
|
||||
*/
|
||||
ResolvableType resolveType() {
|
||||
Type resolved = null;
|
||||
|
||||
if (this.type instanceof ParameterizedType) {
|
||||
resolved = ((ParameterizedType) this.type).getRawType();
|
||||
return forType(((ParameterizedType) this.type).getRawType(),
|
||||
this.variableResolver);
|
||||
}
|
||||
else if (this.type instanceof WildcardType) {
|
||||
resolved = resolveBounds(((WildcardType) this.type).getUpperBounds());
|
||||
|
||||
if (this.type instanceof WildcardType) {
|
||||
Type resolved = resolveBounds(((WildcardType) this.type).getUpperBounds());
|
||||
if (resolved == null) {
|
||||
resolved = resolveBounds(((WildcardType) this.type).getLowerBounds());
|
||||
}
|
||||
return forType(resolved, this.variableResolver);
|
||||
}
|
||||
else if (this.type instanceof TypeVariable) {
|
||||
|
||||
if (this.type instanceof TypeVariable) {
|
||||
TypeVariable<?> variable = (TypeVariable<?>) this.type;
|
||||
|
||||
// Try default variable resolution
|
||||
if (this.variableResolver != null) {
|
||||
resolved = this.variableResolver.resolveVariable((TypeVariable<?>) this.type);
|
||||
}
|
||||
if (resolved == null) {
|
||||
resolved = resolveBounds(((TypeVariable<?>) this.type).getBounds());
|
||||
ResolvableType resolved = this.variableResolver.resolveVariable(variable);
|
||||
if(resolved != null) {
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
return (resolved == null ? NONE : forType(resolved, this.variableResolver));
|
||||
|
||||
// Fallback to bounds
|
||||
return forType(resolveBounds(variable.getBounds()), this.variableResolver);
|
||||
}
|
||||
|
||||
return NONE;
|
||||
}
|
||||
|
||||
private Type resolveBounds(Type[] bounds) {
|
||||
|
@ -525,31 +546,35 @@ public final class ResolvableType implements TypeVariableResolver {
|
|||
return bounds[0];
|
||||
}
|
||||
|
||||
public Type resolveVariable(TypeVariable<?> variable) {
|
||||
Assert.notNull("Variable must not be null");
|
||||
private ResolvableType resolveVariable(TypeVariable<?> variable) {
|
||||
|
||||
if (this.type instanceof TypeVariable) {
|
||||
return resolveType().resolveVariable(variable);
|
||||
}
|
||||
|
||||
if (this.type instanceof ParameterizedType) {
|
||||
|
||||
ParameterizedType parameterizedType = (ParameterizedType) this.type;
|
||||
Type owner = parameterizedType.getOwnerType();
|
||||
if (parameterizedType.getRawType().equals(variable.getGenericDeclaration())) {
|
||||
TypeVariable<?>[] variables = resolve().getTypeParameters();
|
||||
for (int i = 0; i < variables.length; i++) {
|
||||
if (ObjectUtils.nullSafeEquals(variables[i].getName(), variable.getName())) {
|
||||
return parameterizedType.getActualTypeArguments()[i];
|
||||
Type actualType = parameterizedType.getActualTypeArguments()[i];
|
||||
return forType(actualType, this.variableResolver);
|
||||
}
|
||||
}
|
||||
}
|
||||
Type resolved = null;
|
||||
|
||||
if (parameterizedType.getOwnerType() != null) {
|
||||
return forType(parameterizedType.getOwnerType(),
|
||||
this.variableResolver).resolveVariable(variable);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.variableResolver != null) {
|
||||
resolved = this.variableResolver.resolveVariable(variable);
|
||||
}
|
||||
if (resolved == null && owner != null) {
|
||||
resolved = forType(owner, this.variableResolver).resolveVariable(variable);
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
if (this.type instanceof TypeVariable) {
|
||||
return resolveType().resolveVariable(variable);
|
||||
return this.variableResolver.resolveVariable(variable);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -580,17 +605,63 @@ public final class ResolvableType implements TypeVariableResolver {
|
|||
}
|
||||
if (obj instanceof ResolvableType) {
|
||||
ResolvableType other = (ResolvableType) obj;
|
||||
return ObjectUtils.nullSafeEquals(this.type, other.type) &&
|
||||
ObjectUtils.nullSafeEquals(this.variableResolver, other.variableResolver);
|
||||
boolean equals = ObjectUtils.nullSafeEquals(this.type, other.type);
|
||||
equals &= variableResolverSourceEquals(this.variableResolver, other.variableResolver);
|
||||
equals &= ObjectUtils.nullSafeEquals(this.componentType, other.componentType);
|
||||
return equals;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return ObjectUtils.nullSafeHashCode(this.type);
|
||||
int hashCode = ObjectUtils.nullSafeHashCode(this.type);
|
||||
hashCode = hashCode * 31 + ObjectUtils.nullSafeHashCode(this.variableResolver);
|
||||
hashCode = hashCode * 31 + ObjectUtils.nullSafeHashCode(this.componentType);
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom serialization support for {@value #NONE}.
|
||||
*/
|
||||
private Object readResolve() throws ObjectStreamException {
|
||||
return (this.type == null ? NONE : this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapts this {@link ResolvableType} to a {@link VariableResolver}.
|
||||
*/
|
||||
VariableResolver asVariableResolver() {
|
||||
if (this == NONE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new VariableResolver() {
|
||||
@Override
|
||||
public ResolvableType resolveVariable(TypeVariable<?> variable) {
|
||||
return ResolvableType.this.resolveVariable(variable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getSource() {
|
||||
return ResolvableType.this;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean variableResolverSourceEquals(VariableResolver o1, VariableResolver o2) {
|
||||
Object s1 = (o1 == null ? null : o1.getSource());
|
||||
Object s2 = (o2 == null ? null : o2.getSource());
|
||||
return ObjectUtils.nullSafeEquals(s1,s2);
|
||||
}
|
||||
|
||||
private static ResolvableType[] forTypes(Type[] types, VariableResolver owner) {
|
||||
ResolvableType[] result = new ResolvableType[types.length];
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
result[i] = forType(types[i], owner);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Class}. For example
|
||||
|
@ -598,6 +669,7 @@ public final class ResolvableType implements TypeVariableResolver {
|
|||
* @param sourceClass the source class (must not be {@code null}
|
||||
* @return a {@link ResolvableType} for the specified class
|
||||
* @see #forClass(Class, Class)
|
||||
* @see #forClassWithGenerics(Class, Class...)
|
||||
*/
|
||||
public static ResolvableType forClass(Class<?> sourceClass) {
|
||||
Assert.notNull(sourceClass, "Source class must not be null");
|
||||
|
@ -609,14 +681,15 @@ public final class ResolvableType implements TypeVariableResolver {
|
|||
* implementation. For example
|
||||
* {@code ResolvableType.forClass(List.class, MyArrayList.class)}.
|
||||
* @param sourceClass the source class (must not be {@code null}
|
||||
* @param implementationClass the implementation class (must not be {@code null})
|
||||
* @param implementationClass the implementation class
|
||||
* @return a {@link ResolvableType} for the specified class backed by the given
|
||||
* implementation class
|
||||
* @see #forClass(Class)
|
||||
* @see #forClassWithGenerics(Class, Class...)
|
||||
*/
|
||||
public static ResolvableType forClass(Class<?> sourceClass, Class<?> implementationClass) {
|
||||
Assert.notNull(sourceClass, "Source class must not be null");
|
||||
ResolvableType asType = (implementationClass != null ? forType(implementationClass).as(sourceClass) : NONE);
|
||||
ResolvableType asType = forType(implementationClass).as(sourceClass);
|
||||
return (asType == NONE ? forType(sourceClass) : asType);
|
||||
}
|
||||
|
||||
|
@ -628,7 +701,7 @@ public final class ResolvableType implements TypeVariableResolver {
|
|||
*/
|
||||
public static ResolvableType forField(Field field) {
|
||||
Assert.notNull(field, "Field must not be null");
|
||||
return forType(field.getGenericType());
|
||||
return forType(SerializableTypeWrapper.forField(field));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -637,15 +710,14 @@ public final class ResolvableType implements TypeVariableResolver {
|
|||
* <p>Use this variant when the class that declares the field includes generic
|
||||
* parameter variables that are satisfied by the implementation class.
|
||||
* @param field the source field
|
||||
* @param implementationClass the implementation class (must not be {@code null})
|
||||
* @param implementationClass the implementation class
|
||||
* @return a {@link ResolvableType} for the specified field
|
||||
* @see #forField(Field)
|
||||
*/
|
||||
public static ResolvableType forField(Field field, Class<?> implementationClass) {
|
||||
Assert.notNull(field, "Field must not be null");
|
||||
TypeVariableResolver variableResolver = (implementationClass != null ?
|
||||
forType(implementationClass).as(field.getDeclaringClass()) : null);
|
||||
return forType(field.getGenericType(), variableResolver);
|
||||
ResolvableType owner = forType(implementationClass).as(field.getDeclaringClass());
|
||||
return forType(SerializableTypeWrapper.forField(field), owner.asVariableResolver());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -658,7 +730,7 @@ public final class ResolvableType implements TypeVariableResolver {
|
|||
*/
|
||||
public static ResolvableType forField(Field field, int nestingLevel) {
|
||||
Assert.notNull(field, "Field must not be null");
|
||||
return forType(field.getGenericType()).getNested(nestingLevel);
|
||||
return forType(SerializableTypeWrapper.forField(field)).getNested(nestingLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -669,15 +741,14 @@ public final class ResolvableType implements TypeVariableResolver {
|
|||
* @param field the source field
|
||||
* @param nestingLevel the nesting level (1 for the outer level; 2 for a nested
|
||||
* generic type; etc)
|
||||
* @param implementationClass the implementation class (must not be {@code null})
|
||||
* @param implementationClass the implementation class
|
||||
* @return a {@link ResolvableType} for the specified field
|
||||
* @see #forField(Field)
|
||||
*/
|
||||
public static ResolvableType forField(Field field, int nestingLevel, Class<?> implementationClass) {
|
||||
Assert.notNull(field, "Field must not be null");
|
||||
TypeVariableResolver variableResolver = (implementationClass != null ?
|
||||
forType(implementationClass).as(field.getDeclaringClass()) : null);
|
||||
return forType(field.getGenericType(), variableResolver).getNested(nestingLevel);
|
||||
ResolvableType owner = forType(implementationClass).as(field.getDeclaringClass());
|
||||
return forType(SerializableTypeWrapper.forField(field), owner.asVariableResolver()).getNested(nestingLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -699,19 +770,45 @@ public final class ResolvableType implements TypeVariableResolver {
|
|||
* implementation class.
|
||||
* @param constructor the source constructor (must not be {@code null})
|
||||
* @param parameterIndex the parameter index
|
||||
* @param implementationClass the implementation class (must not be {@code null})
|
||||
* @param implementationClass the implementation class
|
||||
* @return a {@link ResolvableType} for the specified constructor parameter
|
||||
* @see #forConstructorParameter(Constructor, int)
|
||||
*/
|
||||
public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex,
|
||||
Class<?> implementationClass) {
|
||||
|
||||
Assert.notNull(constructor, "Constructor must not be null");
|
||||
MethodParameter methodParameter = new MethodParameter(constructor, parameterIndex);
|
||||
methodParameter.setContainingClass(implementationClass);
|
||||
return forMethodParameter(methodParameter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Method} return type.
|
||||
* @param method the source for the method return type
|
||||
* @return a {@link ResolvableType} for the specified method return
|
||||
* @see #forMethodReturnType(Method, Class)
|
||||
*/
|
||||
public static ResolvableType forMethodReturnType(Method method) {
|
||||
Assert.notNull(method, "Method must not be null");
|
||||
return forMethodParameter(MethodParameter.forMethodOrConstructor(method, -1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Method} return type.
|
||||
* Use this variant when the class that declares the method includes generic
|
||||
* parameter variables that are satisfied by the implementation class.
|
||||
* @param method the source for the method return type
|
||||
* @param implementationClass the implementation class
|
||||
* @return a {@link ResolvableType} for the specified method return
|
||||
* @see #forMethodReturnType(Method)
|
||||
*/
|
||||
public static ResolvableType forMethodReturnType(Method method, Class<?> implementationClass) {
|
||||
Assert.notNull(method, "Method must not be null");
|
||||
MethodParameter methodParameter = MethodParameter.forMethodOrConstructor(method, -1);
|
||||
methodParameter.setContainingClass(implementationClass);
|
||||
return forMethodParameter(methodParameter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Method} parameter.
|
||||
* @param method the source method (must not be {@code null})
|
||||
|
@ -731,12 +828,13 @@ public final class ResolvableType implements TypeVariableResolver {
|
|||
* includes generic parameter variables that are satisfied by the implementation class.
|
||||
* @param method the source method (must not be {@code null})
|
||||
* @param parameterIndex the parameter index
|
||||
* @param implementationClass the implementation class (must not be {@code null})
|
||||
* @param implementationClass the implementation class
|
||||
* @return a {@link ResolvableType} for the specified method parameter
|
||||
* @see #forMethodParameter(Method, int, Class)
|
||||
* @see #forMethodParameter(MethodParameter)
|
||||
*/
|
||||
public static ResolvableType forMethodParameter(Method method, int parameterIndex, Class<?> implementationClass) {
|
||||
public static ResolvableType forMethodParameter(Method method, int parameterIndex,
|
||||
Class<?> implementationClass) {
|
||||
Assert.notNull(method, "Method must not be null");
|
||||
MethodParameter methodParameter = new MethodParameter(method, parameterIndex);
|
||||
methodParameter.setContainingClass(implementationClass);
|
||||
|
@ -751,59 +849,119 @@ public final class ResolvableType implements TypeVariableResolver {
|
|||
*/
|
||||
public static ResolvableType forMethodParameter(MethodParameter methodParameter) {
|
||||
Assert.notNull(methodParameter, "MethodParameter must not be null");
|
||||
TypeVariableResolver variableResolver = (methodParameter.getContainingClass() != null ?
|
||||
forType(methodParameter.getContainingClass()).as(methodParameter.getDeclaringClass()) : null);
|
||||
return forType(methodParameter.getGenericParameterType(), variableResolver).getNested(
|
||||
methodParameter.getNestingLevel(), methodParameter.typeIndexesPerLevel);
|
||||
ResolvableType owner = forType(methodParameter.getContainingClass()).as(
|
||||
methodParameter.getDeclaringClass());
|
||||
return forType(SerializableTypeWrapper.forMethodParameter(methodParameter),
|
||||
owner.asVariableResolver()).getNested(methodParameter.getNestingLevel(),
|
||||
methodParameter.typeIndexesPerLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Method} return type.
|
||||
* @param method the source for the method return type
|
||||
* @return a {@link ResolvableType} for the specified method return
|
||||
* @see #forMethodReturnType(Method, Class)
|
||||
* Return a {@link ResolvableType} as a array of the specified {@code componentType}.
|
||||
* @param componentType the component type
|
||||
* @return a {@link ResolvableType} as an array of the specified component type
|
||||
*/
|
||||
public static ResolvableType forMethodReturnType(Method method) {
|
||||
Assert.notNull(method, "Method must not be null");
|
||||
return forType(method.getGenericReturnType());
|
||||
public static ResolvableType forArrayComponent(final ResolvableType componentType) {
|
||||
Assert.notNull(componentType, "ComponentType must not be null");
|
||||
Class<?> arrayClass = Array.newInstance(componentType.resolve(), 0).getClass();
|
||||
return new ResolvableType(arrayClass, null, componentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Method} return type.
|
||||
* Use this variant when the class that declares the method includes generic
|
||||
* parameter variables that are satisfied by the implementation class.
|
||||
* @param method the source for the method return type
|
||||
* @param implementationClass the implementation class (must not be {@code null})
|
||||
* @return a {@link ResolvableType} for the specified method return
|
||||
* @see #forMethodReturnType(Method)
|
||||
* Return a {@link ResolvableType} for the specified {@link Class} with pre-declared
|
||||
* generics.
|
||||
* @param sourceClass the source class
|
||||
* @param generics the generics of the class
|
||||
* @return a {@link ResolvableType} for the specific class and generics
|
||||
* @see #forClassWithGenerics(Class, ResolvableType...)
|
||||
*/
|
||||
public static ResolvableType forMethodReturnType(Method method, Class<?> implementationClass) {
|
||||
Assert.notNull(method, "Method must not be null");
|
||||
TypeVariableResolver variableResolver = (implementationClass != null ?
|
||||
forType(implementationClass).as(method.getDeclaringClass()) : null);
|
||||
return forType(method.getGenericReturnType(), variableResolver);
|
||||
public static ResolvableType forClassWithGenerics(Class<?> sourceClass,
|
||||
Class<?>... generics) {
|
||||
Assert.notNull(sourceClass, "Source class must not be null");
|
||||
Assert.notNull(generics, "Generics must not be null");
|
||||
ResolvableType[] resolvableGenerics = new ResolvableType[generics.length];
|
||||
for (int i = 0; i < generics.length; i++) {
|
||||
resolvableGenerics[i] = forClass(generics[i]);
|
||||
}
|
||||
return forClassWithGenerics(sourceClass, resolvableGenerics);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link java.lang.reflect.Type}.
|
||||
* @param type the source type (must not be {@code null})
|
||||
* @return a {@link ResolvableType} for the specified {@link java.lang.reflect.Type}
|
||||
* Return a {@link ResolvableType} for the specified {@link Class} with pre-declared
|
||||
* generics.
|
||||
* @param sourceClass the source class
|
||||
* @param generics the generics of the class
|
||||
* @return a {@link ResolvableType} for the specific class and generics
|
||||
* @see #forClassWithGenerics(Class, Class...)
|
||||
*/
|
||||
public static ResolvableType forClassWithGenerics(Class<?> sourceClass,
|
||||
final ResolvableType... generics) {
|
||||
Assert.notNull(sourceClass, "Source class must not be null");
|
||||
Assert.notNull(generics, "Generics must not be null");
|
||||
final TypeVariable<?>[] typeVariables = sourceClass.getTypeParameters();
|
||||
Assert.isTrue(typeVariables.length == generics.length,
|
||||
"Missmatched number of generics specified");
|
||||
VariableResolver variableResolver = new VariableResolver() {
|
||||
@Override
|
||||
public ResolvableType resolveVariable(TypeVariable<?> variable) {
|
||||
for (int i = 0; i < typeVariables.length; i++) {
|
||||
if(typeVariables[i].equals(variable)) {
|
||||
return generics[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getSource() {
|
||||
return generics;
|
||||
}
|
||||
};
|
||||
|
||||
return forType(sourceClass, variableResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Type}. NOTE: The resulting
|
||||
* {@link ResolvableType} may not be {@link Serializable}.
|
||||
* @param type the source type or {@code null}
|
||||
* @return a {@link ResolvableType} for the specified {@link Type}
|
||||
* @see #forType(Type, ResolvableType)
|
||||
*/
|
||||
public static ResolvableType forType(Type type) {
|
||||
return forType(type, null);
|
||||
return forType(type, (VariableResolver) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link java.lang.reflect.Type}
|
||||
* backed by a given {@link TypeVariableResolver}.
|
||||
* @param type the source type (must not be {@code null})
|
||||
* @param variableResolver the variable resolver
|
||||
* @return a {@link ResolvableType} for the specified {@link java.lang.reflect.Type}
|
||||
* and {@link TypeVariableResolver}
|
||||
* Return a {@link ResolvableType} for the specified {@link Type} backed by the
|
||||
* given owner type. NOTE: The resulting {@link ResolvableType} may not be
|
||||
* {@link Serializable}.
|
||||
* @param type the source type or {@code null}
|
||||
* @param owner the owner type used to resolve variables
|
||||
* @return a {@link ResolvableType} for the specified {@link Type} and owner
|
||||
* @see #forType(Type)
|
||||
*/
|
||||
public static ResolvableType forType(Type type, TypeVariableResolver variableResolver) {
|
||||
ResolvableType key = new ResolvableType(type, variableResolver);
|
||||
public static ResolvableType forType(Type type, ResolvableType owner) {
|
||||
VariableResolver variableResolver = null;
|
||||
if (owner != null) {
|
||||
variableResolver = owner.asVariableResolver();
|
||||
}
|
||||
return forType(type, variableResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link ResolvableType} for the specified {@link Type} backed by a given
|
||||
* {@link VariableResolver}.
|
||||
* @param type the source type or {@code null}
|
||||
* @param variableResolver the variable resolver or {@code null}
|
||||
* @return a {@link ResolvableType} for the specified {@link Type} and {@link VariableResolver}
|
||||
*/
|
||||
static ResolvableType forType(Type type, VariableResolver variableResolver) {
|
||||
if(type == null) {
|
||||
return NONE;
|
||||
}
|
||||
// Check the cache, we may have a ResolvableType that may have already been resolved
|
||||
ResolvableType key = new ResolvableType(type, variableResolver, null);
|
||||
ResolvableType resolvableType = cache.get(key);
|
||||
if (resolvableType == null) {
|
||||
resolvableType = key;
|
||||
|
@ -813,6 +971,26 @@ public final class ResolvableType implements TypeVariableResolver {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Strategy interface used to resolve {@link TypeVariable}s.
|
||||
*/
|
||||
static interface VariableResolver extends Serializable {
|
||||
|
||||
/**
|
||||
* Return the source of the resolver (used for hashCode and equals).
|
||||
*/
|
||||
Object getSource();
|
||||
|
||||
/**
|
||||
* Resolve the specified varaible.
|
||||
* @param variable the variable to resolve
|
||||
* @return the resolved variable or {@code null}
|
||||
*/
|
||||
ResolvableType resolveVariable(TypeVariable<?> variable);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal helper to handle bounds from {@link WildcardType}s.
|
||||
*/
|
||||
|
@ -886,7 +1064,8 @@ public final class ResolvableType implements TypeVariableResolver {
|
|||
Type[] bounds = boundsType == Kind.UPPER ? wildcardType.getUpperBounds() : wildcardType.getLowerBounds();
|
||||
ResolvableType[] resolvableBounds = new ResolvableType[bounds.length];
|
||||
for (int i = 0; i < bounds.length; i++) {
|
||||
resolvableBounds[i] = forType(bounds[i], type.variableResolver);
|
||||
resolvableBounds[i] = ResolvableType.forType(bounds[i],
|
||||
type.variableResolver);
|
||||
}
|
||||
return new WildcardBounds(boundsType, resolvableBounds);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,330 @@
|
|||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.GenericArrayType;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.lang.reflect.WildcardType;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Internal utility class that can be used to obtain wrapped {@link Serializable} variants
|
||||
* of {@link java.lang.reflect.Type}s.
|
||||
*
|
||||
* <p>{@link #forField(Field) Fields} or {@link #forMethodParameter(MethodParameter)
|
||||
* MethodParameters} can be used as the root source for a serializable type. Alternatively
|
||||
* the {@link #forGenericSuperclass(Class) superclass},
|
||||
* {@link #forGenericInterfaces(Class) interfaces} or {@link #forTypeParameters(Class)
|
||||
* type parameters} or a regular {@link Class} can also be used as source.
|
||||
*
|
||||
* <p>The returned type will either be a {@link Class} or a serializable proxy of
|
||||
* {@link GenericArrayType}, {@link ParameterizedType}, {@link TypeVariable} or
|
||||
* {@link WildcardType}. With the exception of {@link Class} (which is final) calls to
|
||||
* methods that return further {@link Type}s (for example
|
||||
* {@link GenericArrayType#getGenericComponentType()}) will be automatically wrapped.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 4.0
|
||||
*/
|
||||
abstract class SerializableTypeWrapper {
|
||||
|
||||
private static final Class<?>[] SUPPORTED_SERIALAZABLE_TYPES = { GenericArrayType.class,
|
||||
ParameterizedType.class, TypeVariable.class, WildcardType.class };
|
||||
|
||||
|
||||
/**
|
||||
* Return a {@link Serializable} variant of {@link Field#getGenericType()}.
|
||||
*/
|
||||
public static Type forField(Field field) {
|
||||
Assert.notNull(field, "Field must not be null");
|
||||
return forTypeProvider(new FieldTypeProvider(field));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link Serializable} variant of
|
||||
* {@link MethodParameter#getGenericParameterType()}.
|
||||
*/
|
||||
public static Type forMethodParameter(MethodParameter methodParameter) {
|
||||
return forTypeProvider(new MethodParameterTypeProvider(methodParameter));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link Serializable} variant of {@link Class#getGenericSuperclass()}.
|
||||
*/
|
||||
public static Type forGenericSuperclass(final Class<?> type) {
|
||||
return forTypeProvider(new TypeProvider() {
|
||||
@Override
|
||||
public Type getType() {
|
||||
return type.getGenericSuperclass();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link Serializable} variant of {@link Class#getGenericInterfaces()}.
|
||||
*/
|
||||
public static Type[] forGenericInterfaces(final Class<?> type) {
|
||||
Type[] result = new Type[type.getGenericInterfaces().length];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
final int index = i;
|
||||
result[i] = forTypeProvider(new TypeProvider() {
|
||||
@Override
|
||||
public Type getType() {
|
||||
return type.getGenericInterfaces()[index];
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link Serializable} variant of {@link Class#getTypeParameters()}.
|
||||
*/
|
||||
public static Type[] forTypeParameters(final Class<?> type) {
|
||||
Type[] result = new Type[type.getTypeParameters().length];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
final int index = i;
|
||||
result[i] = forTypeProvider(new TypeProvider() {
|
||||
@Override
|
||||
public Type getType() {
|
||||
return type.getTypeParameters()[index];
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a {@link Serializable} {@link Type} backed by a {@link TypeProvider} .
|
||||
*/
|
||||
private static Type forTypeProvider(final TypeProvider provider) {
|
||||
Assert.notNull(provider, "Provider must not be null");
|
||||
if (provider.getType() instanceof Serializable || provider.getType() == null) {
|
||||
return provider.getType();
|
||||
}
|
||||
for (Class<?> type : SUPPORTED_SERIALAZABLE_TYPES) {
|
||||
if (type.isAssignableFrom(provider.getType().getClass())) {
|
||||
ClassLoader classLoader = provider.getClass().getClassLoader();
|
||||
Class<?>[] interfaces = new Class<?>[] { type, Serializable.class };
|
||||
InvocationHandler handler = new TypeProxyInvocationHandler(provider);
|
||||
return (Type) Proxy.newProxyInstance(classLoader, interfaces, handler);
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported Type class "
|
||||
+ provider.getType().getClass().getName());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A {@link Serializable} interface providing access to a {@link Type}.
|
||||
*/
|
||||
private static interface TypeProvider extends Serializable {
|
||||
|
||||
/**
|
||||
* Return the (possibly non {@link Serializable}) {@link Type}.
|
||||
*/
|
||||
Type getType();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@link Serializable} {@link InvocationHandler} used by the Proxied {@link Type}.
|
||||
* Provides serialization support and enhances any methods that return {@code Type}
|
||||
* or {@code Type[]}.
|
||||
*/
|
||||
private static class TypeProxyInvocationHandler implements InvocationHandler,
|
||||
Serializable {
|
||||
|
||||
private final TypeProvider provider;
|
||||
|
||||
|
||||
public TypeProxyInvocationHandler(TypeProvider provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
if (Type.class.equals(method.getReturnType()) && args == null) {
|
||||
return forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, -1));
|
||||
}
|
||||
if (Type[].class.equals(method.getReturnType()) && args == null) {
|
||||
Type[] result = new Type[((Type[]) method.invoke(this.provider.getType(), args)).length];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return method.invoke(this.provider.getType(), args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* {@link TypeProvider} for {@link Type}s obtained from a {@link Field}.
|
||||
*/
|
||||
private static class FieldTypeProvider implements TypeProvider {
|
||||
|
||||
private final String fieldName;
|
||||
|
||||
private final Class<?> declaringClass;
|
||||
|
||||
private transient Field field;
|
||||
|
||||
|
||||
public FieldTypeProvider(Field field) {
|
||||
this.fieldName = field.getName();
|
||||
this.declaringClass = field.getDeclaringClass();
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return this.field.getGenericType();
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream inputStream) throws IOException,
|
||||
ClassNotFoundException {
|
||||
inputStream.defaultReadObject();
|
||||
try {
|
||||
this.field = this.declaringClass.getDeclaredField(this.fieldName);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException(
|
||||
"Could not find original class structure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@link TypeProvider} for {@link Type}s obtained from a {@link MethodParameter}.
|
||||
*/
|
||||
private static class MethodParameterTypeProvider implements TypeProvider {
|
||||
|
||||
private final String methodName;
|
||||
|
||||
private final Class<?>[] parameterTypes;
|
||||
|
||||
private final Class<?> declaringClass;
|
||||
|
||||
private final int parameterIndex;
|
||||
|
||||
private transient MethodParameter methodParameter;
|
||||
|
||||
|
||||
public MethodParameterTypeProvider(MethodParameter methodParameter) {
|
||||
if (methodParameter.getMethod() != null) {
|
||||
this.methodName = methodParameter.getMethod().getName();
|
||||
this.parameterTypes = methodParameter.getMethod().getParameterTypes();
|
||||
}
|
||||
else {
|
||||
this.methodName = null;
|
||||
this.parameterTypes = methodParameter.getConstructor().getParameterTypes();
|
||||
}
|
||||
this.declaringClass = methodParameter.getDeclaringClass();
|
||||
this.parameterIndex = methodParameter.getParameterIndex();
|
||||
this.methodParameter = methodParameter;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return this.methodParameter.getGenericParameterType();
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream inputStream) throws IOException,
|
||||
ClassNotFoundException {
|
||||
inputStream.defaultReadObject();
|
||||
try {
|
||||
if (this.methodName != null) {
|
||||
this.methodParameter = new MethodParameter(
|
||||
this.declaringClass.getDeclaredMethod(this.methodName,
|
||||
this.parameterTypes), this.parameterIndex);
|
||||
}
|
||||
else {
|
||||
this.methodParameter = new MethodParameter(
|
||||
this.declaringClass.getDeclaredConstructor(this.parameterTypes),
|
||||
this.parameterIndex);
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new IllegalStateException(
|
||||
"Could not find original class structure", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@link TypeProvider} for {@link Type}s obtained by invoking a no-arg method.
|
||||
*/
|
||||
private static class MethodInvokeTypeProvider implements TypeProvider {
|
||||
|
||||
private final TypeProvider provider;
|
||||
|
||||
private final String methodName;
|
||||
|
||||
private final int index;
|
||||
|
||||
private transient Object result;
|
||||
|
||||
|
||||
public MethodInvokeTypeProvider(TypeProvider provider, Method method, int index) {
|
||||
this.provider = provider;
|
||||
this.methodName = method.getName();
|
||||
this.index = index;
|
||||
this.result = ReflectionUtils.invokeMethod(method, provider.getType());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
if (this.result instanceof Type || this.result == null) {
|
||||
return (Type) this.result;
|
||||
}
|
||||
return ((Type[])this.result)[this.index];
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream inputStream) throws IOException,
|
||||
ClassNotFoundException {
|
||||
inputStream.defaultReadObject();
|
||||
Method method = ReflectionUtils.findMethod(
|
||||
this.provider.getType().getClass(), this.methodName);
|
||||
this.result = ReflectionUtils.invokeMethod(method, this.provider.getType());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
|
||||
/**
|
||||
* Strategy interface that can be used to resolve {@link java.lang.reflect.TypeVariable}s.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 4.0
|
||||
* @see ResolvableType
|
||||
* @see GenericTypeResolver
|
||||
*/
|
||||
interface TypeVariableResolver {
|
||||
|
||||
/**
|
||||
* Resolve the specified type variable.
|
||||
* @param typeVariable the type variable to resolve (must not be {@code null})
|
||||
* @return the resolved {@link java.lang.reflect.Type} for the variable or
|
||||
* {@code null} if the variable cannot be resolved.
|
||||
*/
|
||||
Type resolveVariable(TypeVariable<?> typeVariable);
|
||||
|
||||
}
|
|
@ -16,6 +16,10 @@
|
|||
|
||||
package org.springframework.core;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
|
@ -44,12 +48,14 @@ import org.junit.runner.RunWith;
|
|||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.core.ResolvableType.VariableResolver;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import static org.mockito.BDDMockito.*;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.BDDMockito.*;
|
||||
|
||||
/**
|
||||
* Tests for {@link ResolvableType}.
|
||||
|
@ -86,7 +92,6 @@ public class ResolvableTypeTests {
|
|||
assertThat(none.resolve(String.class), equalTo((Class) String.class));
|
||||
assertThat(none.resolveGeneric(0), nullValue());
|
||||
assertThat(none.resolveGenerics().length, equalTo(0));
|
||||
assertThat(none.resolveVariable(mock(TypeVariable.class)), nullValue());
|
||||
assertThat(none.toString(), equalTo("?"));
|
||||
assertThat(none.isAssignableFrom(ResolvableType.forClass(Object.class)), equalTo(false));
|
||||
}
|
||||
|
@ -415,6 +420,13 @@ public class ResolvableTypeTests {
|
|||
assertThat(type.getGeneric().getGeneric().getType(), equalTo((Type) String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericOfGenericWithAs() throws Exception {
|
||||
ResolvableType type = ResolvableType.forField(Fields.class.getField("stringListList")).asCollection();
|
||||
assertThat(type.toString(), equalTo("java.util.Collection<java.util.List<java.lang.String>>"));
|
||||
assertThat(type.getGeneric().asCollection().toString(), equalTo("java.util.Collection<java.lang.String>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getGenericOfGenericByIndexes() throws Exception {
|
||||
ResolvableType type = ResolvableType.forField(Fields.class.getField("stringListList"));
|
||||
|
@ -437,13 +449,21 @@ public class ResolvableTypeTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void getGenerics() throws Exception {
|
||||
public void getGenericsFromParameterizedType() throws Exception {
|
||||
ResolvableType type = ResolvableType.forClass(List.class, ExtendsList.class);
|
||||
ResolvableType[] generics = type.getGenerics();
|
||||
assertThat(generics.length, equalTo(1));
|
||||
assertThat(generics[0].resolve(), equalTo((Class) CharSequence.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getGenericsFromClass() throws Exception {
|
||||
ResolvableType type = ResolvableType.forClass(List.class);
|
||||
ResolvableType[] generics = type.getGenerics();
|
||||
assertThat(generics.length, equalTo(1));
|
||||
assertThat(generics[0].getType().toString(), equalTo("E"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noGetGenerics() throws Exception {
|
||||
ResolvableType type = ResolvableType.forClass(ExtendsList.class);
|
||||
|
@ -549,9 +569,8 @@ public class ResolvableTypeTests {
|
|||
public void doesResolveFromOuterOwner() throws Exception {
|
||||
ResolvableType type = ResolvableType.forField(
|
||||
Fields.class.getField("listOfListOfUnknown")).as(Collection.class);
|
||||
ResolvableType generic = type.getGeneric(0);
|
||||
assertThat(generic.resolve(), equalTo((Class) List.class));
|
||||
assertThat(generic.as(Collection.class).getGeneric(0).as(Collection.class).resolve(), nullValue());
|
||||
assertThat(type.getGeneric(0).resolve(), equalTo((Class) List.class));
|
||||
assertThat(type.getGeneric(0).as(Collection.class).getGeneric(0).as(Collection.class).resolve(), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -721,15 +740,16 @@ public class ResolvableTypeTests {
|
|||
public void resolveTypeVariableFromTypeWithVariableResolver() throws Exception {
|
||||
Type sourceType = Methods.class.getMethod("typedReturn").getGenericReturnType();
|
||||
ResolvableType type = ResolvableType.forType(
|
||||
sourceType, ResolvableType.forClass(TypedMethods.class).as(Methods.class));
|
||||
sourceType, ResolvableType.forClass(TypedMethods.class).as(Methods.class).asVariableResolver());
|
||||
assertThat(type.resolve(), equalTo((Class) String.class));
|
||||
assertThat(type.getType().toString(), equalTo("T"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveTypeWithCustomVariableResolver() throws Exception {
|
||||
TypeVariableResolver variableResolver = mock(TypeVariableResolver.class);
|
||||
given(variableResolver.resolveVariable((TypeVariable<?>) anyObject())).willReturn(Long.class);
|
||||
VariableResolver variableResolver = mock(VariableResolver.class);
|
||||
ResolvableType longType = ResolvableType.forClass(Long.class);
|
||||
given(variableResolver.resolveVariable((TypeVariable<?>) anyObject())).willReturn(longType);
|
||||
|
||||
ResolvableType variable = ResolvableType.forType(
|
||||
Fields.class.getField("typeVariableType").getGenericType(), variableResolver);
|
||||
|
@ -747,10 +767,10 @@ public class ResolvableTypeTests {
|
|||
public void toStrings() throws Exception {
|
||||
assertThat(ResolvableType.NONE.toString(), equalTo("?"));
|
||||
|
||||
assertFieldToStringValue("classType", "java.util.List");
|
||||
assertFieldToStringValue("classType", "java.util.List<?>");
|
||||
assertFieldToStringValue("typeVariableType", "?");
|
||||
assertFieldToStringValue("parameterizedType", "java.util.List<?>");
|
||||
assertFieldToStringValue("arrayClassType", "java.util.List[]");
|
||||
assertFieldToStringValue("arrayClassType", "java.util.List<?>[]");
|
||||
assertFieldToStringValue("genericArrayType", "java.util.List<java.lang.String>[]");
|
||||
assertFieldToStringValue("genericMultiArrayType", "java.util.List<java.lang.String>[][][]");
|
||||
assertFieldToStringValue("wildcardType", "java.util.List<java.lang.Number>");
|
||||
|
@ -761,7 +781,7 @@ public class ResolvableTypeTests {
|
|||
assertFieldToStringValue("stringArrayList", "java.util.List<java.lang.String[]>");
|
||||
assertFieldToStringValue("stringIntegerMultiValueMap", "org.springframework.util.MultiValueMap<java.lang.String, java.lang.Integer>");
|
||||
assertFieldToStringValue("stringIntegerMultiValueMapSwitched", VariableNameSwitch.class.getName() + "<java.lang.Integer, java.lang.String>");
|
||||
assertFieldToStringValue("listOfListOfUnknown", "java.util.List<java.util.List>");
|
||||
assertFieldToStringValue("listOfListOfUnknown", "java.util.List<java.util.List<?>>");
|
||||
|
||||
assertTypedFieldToStringValue("typeVariableType", "java.lang.String");
|
||||
assertTypedFieldToStringValue("parameterizedType", "java.util.List<java.lang.String>");
|
||||
|
@ -789,6 +809,17 @@ public class ResolvableTypeTests {
|
|||
assertThat(type.resolve(), equalTo((Type) Integer.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveFromClassWithGenerics() throws Exception {
|
||||
ResolvableType type = ResolvableType.forClassWithGenerics(List.class, ResolvableType.forClassWithGenerics(List.class, String.class));
|
||||
assertThat(type.asCollection().toString(), equalTo("java.util.Collection<java.util.List<java.lang.String>>"));
|
||||
assertThat(type.asCollection().getGeneric().toString(), equalTo("java.util.List<java.lang.String>"));
|
||||
assertThat(type.asCollection().getGeneric().asCollection().toString(), equalTo("java.util.Collection<java.lang.String>"));
|
||||
assertThat(type.toString(), equalTo("java.util.List<java.util.List<java.lang.String>>"));
|
||||
assertThat(type.asCollection().getGeneric().getGeneric().resolve(), equalTo((Type) String.class));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void isAssignableFromMustNotBeNull() throws Exception {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
|
@ -997,7 +1028,7 @@ public class ResolvableTypeTests {
|
|||
public void hashCodeAndEquals() throws Exception {
|
||||
ResolvableType forClass = ResolvableType.forClass(List.class);
|
||||
ResolvableType forFieldDirect = ResolvableType.forField(Fields.class.getDeclaredField("stringList"));
|
||||
ResolvableType forFieldViaType = ResolvableType.forType(Fields.class.getDeclaredField("stringList").getGenericType());
|
||||
ResolvableType forFieldViaType = ResolvableType.forType(Fields.class.getDeclaredField("stringList").getGenericType(), (VariableResolver) null);
|
||||
ResolvableType forFieldWithImplementation = ResolvableType.forField(Fields.class.getDeclaredField("stringList"), TypedFields.class);
|
||||
|
||||
assertThat(forClass, equalTo(forClass));
|
||||
|
@ -1024,6 +1055,60 @@ public class ResolvableTypeTests {
|
|||
assertThat(t.resolveGeneric(1, 0), equalTo((Class) String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forClassWithGenerics() throws Exception {
|
||||
ResolvableType elementType = ResolvableType.forClassWithGenerics(Map.class, Integer.class, String.class);
|
||||
ResolvableType listType = ResolvableType.forClassWithGenerics(List.class, elementType);
|
||||
assertThat(listType.toString(), equalTo("java.util.List<java.util.Map<java.lang.Integer, java.lang.String>>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void classWithGenericsAs() throws Exception {
|
||||
ResolvableType type = ResolvableType.forClassWithGenerics(MultiValueMap.class, Integer.class, String.class);
|
||||
assertThat(type.asMap().toString(), equalTo("java.util.Map<java.lang.Integer, java.util.List<java.lang.String>>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forClassWithMismatchedGenerics() throws Exception {
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("Missmatched number of generics specified");
|
||||
ResolvableType.forClassWithGenerics(Map.class, Integer.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forArrayComponent() throws Exception {
|
||||
ResolvableType elementType = ResolvableType.forField(Fields.class.getField("stringList"));
|
||||
ResolvableType type = ResolvableType.forArrayComponent(elementType);
|
||||
assertThat(type.toString(), equalTo("java.util.List<java.lang.String>[]"));
|
||||
assertThat(type.resolve(), equalTo((Class) List[].class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serialize() throws Exception {
|
||||
testSerialization(ResolvableType.forClass(List.class));
|
||||
testSerialization(ResolvableType.forField(Fields.class.getField("charSequenceList")));
|
||||
testSerialization(ResolvableType.forMethodParameter(Methods.class.getMethod("charSequenceParameter", List.class), 0));
|
||||
testSerialization(ResolvableType.forMethodReturnType(Methods.class.getMethod("charSequenceReturn")));
|
||||
testSerialization(ResolvableType.forConstructorParameter(Constructors.class.getConstructor(List.class), 0));
|
||||
testSerialization(ResolvableType.forField(Fields.class.getField("charSequenceList")).getGeneric());
|
||||
testSerialization(ResolvableType.forField(Fields.class.getField("charSequenceList")).asCollection());
|
||||
testSerialization(ResolvableType.forClass(ExtendsMap.class).getSuperType());
|
||||
ResolvableType deserializedNone = testSerialization(ResolvableType.NONE);
|
||||
assertThat(deserializedNone, sameInstance(ResolvableType.NONE));
|
||||
}
|
||||
|
||||
private ResolvableType testSerialization(ResolvableType type) throws Exception {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oos = new ObjectOutputStream(bos);
|
||||
oos.writeObject(type);
|
||||
oos.close();
|
||||
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
|
||||
ResolvableType read = (ResolvableType) ois.readObject();
|
||||
assertThat(read, equalTo(type));
|
||||
assertThat(read.getType(), equalTo(type.getType()));
|
||||
assertThat(read.resolve(), equalTo((Class) type.resolve()));
|
||||
return read;
|
||||
}
|
||||
|
||||
private static AssertAssignbleMatcher assertAssignable(final ResolvableType type,
|
||||
final ResolvableType... fromTypes) {
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.GenericArrayType;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.lang.reflect.WildcardType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for {@link SerializableTypeWrapper}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class SerializableTypeWrapperTests {
|
||||
|
||||
@Test
|
||||
public void forField() throws Exception {
|
||||
Type type = SerializableTypeWrapper.forField(Fields.class.getField("parameterizedType"));
|
||||
assertThat(type.toString(), equalTo("java.util.List<java.lang.String>"));
|
||||
assertSerialzable(type);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forMethodParameter() throws Exception {
|
||||
Method method = Methods.class.getDeclaredMethod("method", Class.class, Object.class);
|
||||
Type type = SerializableTypeWrapper.forMethodParameter(MethodParameter.forMethodOrConstructor(method, 0));
|
||||
assertThat(type.toString(), equalTo("java.lang.Class<T>"));
|
||||
assertSerialzable(type);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forConstructor() throws Exception {
|
||||
Constructor<?> constructor = Constructors.class.getDeclaredConstructor(List.class);
|
||||
Type type = SerializableTypeWrapper.forMethodParameter(MethodParameter.forMethodOrConstructor(constructor, 0));
|
||||
assertThat(type.toString(), equalTo("java.util.List<java.lang.String>"));
|
||||
assertSerialzable(type);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forGenericSuperClass() throws Exception {
|
||||
Type type = SerializableTypeWrapper.forGenericSuperclass(ArrayList.class);
|
||||
assertThat(type.toString(), equalTo("java.util.AbstractList<E>"));
|
||||
assertSerialzable(type);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forGenericInterfaces() throws Exception {
|
||||
Type type = SerializableTypeWrapper.forGenericInterfaces(List.class)[0];
|
||||
assertThat(type.toString(), equalTo("java.util.Collection<E>"));
|
||||
assertSerialzable(type);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forTypeParamters() throws Exception {
|
||||
Type type = SerializableTypeWrapper.forTypeParameters(List.class)[0];
|
||||
assertThat(type.toString(), equalTo("E"));
|
||||
assertSerialzable(type);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void classType() throws Exception {
|
||||
Type type = SerializableTypeWrapper.forField(Fields.class.getField("classType"));
|
||||
assertThat(type.toString(), equalTo("class java.lang.String"));
|
||||
assertSerialzable(type);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void genericArrayType() throws Exception {
|
||||
GenericArrayType type = (GenericArrayType) SerializableTypeWrapper.forField(Fields.class.getField("genericArrayType"));
|
||||
assertThat(type.toString(), equalTo("java.util.List<java.lang.String>[]"));
|
||||
assertSerialzable(type);
|
||||
assertSerialzable(type.getGenericComponentType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parameterizedType() throws Exception {
|
||||
ParameterizedType type = (ParameterizedType) SerializableTypeWrapper.forField(Fields.class.getField("parameterizedType"));
|
||||
assertThat(type.toString(), equalTo("java.util.List<java.lang.String>"));
|
||||
assertSerialzable(type);
|
||||
assertSerialzable(type.getOwnerType());
|
||||
assertSerialzable(type.getRawType());
|
||||
assertSerialzable(type.getActualTypeArguments());
|
||||
assertSerialzable(type.getActualTypeArguments()[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeVariableType() throws Exception {
|
||||
TypeVariable<?> type = (TypeVariable<?>) SerializableTypeWrapper.forField(Fields.class.getField("typeVariableType"));
|
||||
assertThat(type.toString(), equalTo("T"));
|
||||
assertSerialzable(type);
|
||||
assertSerialzable(type.getBounds());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wildcardType() throws Exception {
|
||||
ParameterizedType typeSource = (ParameterizedType) SerializableTypeWrapper.forField(Fields.class.getField("wildcardType"));
|
||||
WildcardType type = (WildcardType) typeSource.getActualTypeArguments()[0];
|
||||
assertThat(type.toString(), equalTo("? extends java.lang.CharSequence"));
|
||||
assertSerialzable(type);
|
||||
assertSerialzable(type.getLowerBounds());
|
||||
assertSerialzable(type.getUpperBounds());
|
||||
}
|
||||
|
||||
|
||||
private void assertSerialzable(Object source) throws Exception {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oos = new ObjectOutputStream(bos);
|
||||
oos.writeObject(source);
|
||||
oos.close();
|
||||
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
|
||||
assertThat(ois.readObject(), equalTo(source));
|
||||
}
|
||||
|
||||
|
||||
static class Fields<T> {
|
||||
|
||||
public String classType;
|
||||
|
||||
public List<String>[] genericArrayType;
|
||||
|
||||
public List<String> parameterizedType;
|
||||
|
||||
public T typeVariableType;
|
||||
|
||||
public List<? extends CharSequence> wildcardType;
|
||||
|
||||
}
|
||||
|
||||
static interface Methods {
|
||||
|
||||
<T> List<T> method(Class<T> p1, T p2);
|
||||
|
||||
}
|
||||
|
||||
static class Constructors {
|
||||
|
||||
public Constructors(List<String> p) {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue