Undeprecate TypeVariableMap methods on GenericTypeResolver
Issue: SPR-15429
This commit is contained in:
parent
42420a2df7
commit
7fbc20e225
|
|
@ -21,8 +21,12 @@ import java.lang.reflect.ParameterizedType;
|
|||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.lang.reflect.WildcardType;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
|
||||
/**
|
||||
* Helper class for resolving generic types against type variables.
|
||||
|
|
@ -38,6 +42,11 @@ import org.springframework.util.Assert;
|
|||
*/
|
||||
public abstract class GenericTypeResolver {
|
||||
|
||||
/** Cache from Class to TypeVariable Map */
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static final Map<Class<?>, Map<TypeVariable, Type>> typeVariableCache = new ConcurrentReferenceHashMap<>();
|
||||
|
||||
|
||||
/**
|
||||
* Determine the target type for the given generic parameter type.
|
||||
* @param methodParameter the method parameter specification
|
||||
|
|
@ -196,4 +205,80 @@ public abstract class GenericTypeResolver {
|
|||
return ResolvableType.NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the specified generic type against the given TypeVariable map.
|
||||
* <p>Used by Spring Data.
|
||||
* @param genericType the generic type to resolve
|
||||
* @param map the TypeVariable Map to resolved against
|
||||
* @return the type if it resolves to a Class, or {@code Object.class} otherwise
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static Class<?> resolveType(Type genericType, Map<TypeVariable, Type> map) {
|
||||
return ResolvableType.forType(genericType, new TypeVariableMapVariableResolver(map)).resolve(Object.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a mapping of {@link TypeVariable#getName TypeVariable names} to
|
||||
* {@link Class concrete classes} for the specified {@link Class}.
|
||||
* Searches all super types, enclosing types and interfaces.
|
||||
* @see #resolveType(Type, Map)
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static Map<TypeVariable, Type> getTypeVariableMap(Class<?> clazz) {
|
||||
Map<TypeVariable, Type> typeVariableMap = typeVariableCache.get(clazz);
|
||||
if (typeVariableMap == null) {
|
||||
typeVariableMap = new HashMap<>();
|
||||
buildTypeVariableMap(ResolvableType.forClass(clazz), typeVariableMap);
|
||||
typeVariableCache.put(clazz, Collections.unmodifiableMap(typeVariableMap));
|
||||
}
|
||||
return typeVariableMap;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static void buildTypeVariableMap(ResolvableType type, Map<TypeVariable, Type> typeVariableMap) {
|
||||
if (type != ResolvableType.NONE) {
|
||||
if (type.getType() instanceof ParameterizedType) {
|
||||
TypeVariable<?>[] variables = type.resolve().getTypeParameters();
|
||||
for (int i = 0; i < variables.length; i++) {
|
||||
ResolvableType generic = type.getGeneric(i);
|
||||
while (generic.getType() instanceof TypeVariable<?>) {
|
||||
generic = generic.resolveType();
|
||||
}
|
||||
if (generic != ResolvableType.NONE) {
|
||||
typeVariableMap.put(variables[i], generic.getType());
|
||||
}
|
||||
}
|
||||
}
|
||||
buildTypeVariableMap(type.getSuperType(), typeVariableMap);
|
||||
for (ResolvableType interfaceType : type.getInterfaces()) {
|
||||
buildTypeVariableMap(interfaceType, typeVariableMap);
|
||||
}
|
||||
if (type.resolve().isMemberClass()) {
|
||||
buildTypeVariableMap(ResolvableType.forClass(type.resolve().getEnclosingClass()), typeVariableMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings({"serial", "rawtypes"})
|
||||
private static class TypeVariableMapVariableResolver implements ResolvableType.VariableResolver {
|
||||
|
||||
private final Map<TypeVariable, Type> typeVariableMap;
|
||||
|
||||
public TypeVariableMapVariableResolver(Map<TypeVariable, Type> typeVariableMap) {
|
||||
this.typeVariableMap = typeVariableMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResolvableType resolveVariable(TypeVariable<?> variable) {
|
||||
Type type = this.typeVariableMap.get(variable);
|
||||
return (type != null ? ResolvableType.forType(type) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getSource() {
|
||||
return this.typeVariableMap;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,11 @@
|
|||
package org.springframework.core;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
|
@ -32,7 +36,7 @@ import static org.springframework.util.ReflectionUtils.*;
|
|||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public class GenericTypeResolverTests {
|
||||
|
||||
@Test
|
||||
|
|
@ -72,11 +76,69 @@ public class GenericTypeResolverTests {
|
|||
resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "object"), MyInterfaceType.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveType() {
|
||||
Method intMessageMethod = findMethod(MyTypeWithMethods.class, "readIntegerInputMessage", MyInterfaceType.class);
|
||||
MethodParameter intMessageMethodParam = new MethodParameter(intMessageMethod, 0);
|
||||
assertEquals(MyInterfaceType.class,
|
||||
resolveType(intMessageMethodParam.getGenericParameterType(), new HashMap<>()));
|
||||
|
||||
Method intArrMessageMethod = findMethod(MyTypeWithMethods.class, "readIntegerArrayInputMessage",
|
||||
MyInterfaceType[].class);
|
||||
MethodParameter intArrMessageMethodParam = new MethodParameter(intArrMessageMethod, 0);
|
||||
assertEquals(MyInterfaceType[].class,
|
||||
resolveType(intArrMessageMethodParam.getGenericParameterType(), new HashMap<>()));
|
||||
|
||||
Method genericArrMessageMethod = findMethod(MySimpleTypeWithMethods.class, "readGenericArrayInputMessage",
|
||||
Object[].class);
|
||||
MethodParameter genericArrMessageMethodParam = new MethodParameter(genericArrMessageMethod, 0);
|
||||
Map<TypeVariable, Type> varMap = getTypeVariableMap(MySimpleTypeWithMethods.class);
|
||||
assertEquals(Integer[].class, resolveType(genericArrMessageMethodParam.getGenericParameterType(), varMap));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBoundParameterizedType() {
|
||||
assertEquals(B.class, resolveTypeArgument(TestImpl.class, TestIfc.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTypeVariableMap() throws Exception {
|
||||
Map<TypeVariable, Type> map;
|
||||
|
||||
map = GenericTypeResolver.getTypeVariableMap(MySimpleInterfaceType.class);
|
||||
assertThat(map.toString(), equalTo("{T=class java.lang.String}"));
|
||||
|
||||
map = GenericTypeResolver.getTypeVariableMap(MyCollectionInterfaceType.class);
|
||||
assertThat(map.toString(), equalTo("{T=java.util.Collection<java.lang.String>}"));
|
||||
|
||||
map = GenericTypeResolver.getTypeVariableMap(MyCollectionSuperclassType.class);
|
||||
assertThat(map.toString(), equalTo("{T=java.util.Collection<java.lang.String>}"));
|
||||
|
||||
map = GenericTypeResolver.getTypeVariableMap(MySimpleTypeWithMethods.class);
|
||||
assertThat(map.toString(), equalTo("{T=class java.lang.Integer}"));
|
||||
|
||||
map = GenericTypeResolver.getTypeVariableMap(TopLevelClass.class);
|
||||
assertThat(map.toString(), equalTo("{}"));
|
||||
|
||||
map = GenericTypeResolver.getTypeVariableMap(TypedTopLevelClass.class);
|
||||
assertThat(map.toString(), equalTo("{T=class java.lang.Integer}"));
|
||||
|
||||
map = GenericTypeResolver.getTypeVariableMap(TypedTopLevelClass.TypedNested.class);
|
||||
assertThat(map.size(), equalTo(2));
|
||||
Type t = null;
|
||||
Type x = null;
|
||||
for (Map.Entry<TypeVariable, Type> entry : map.entrySet()) {
|
||||
if (entry.getKey().toString().equals("T")) {
|
||||
t = entry.getValue();
|
||||
}
|
||||
else {
|
||||
x = entry.getValue();
|
||||
}
|
||||
}
|
||||
assertThat(t, equalTo((Type) Integer.class));
|
||||
assertThat(x, equalTo((Type) Long.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getGenericsCannotBeResolved() throws Exception {
|
||||
// SPR-11030
|
||||
|
|
|
|||
Loading…
Reference in New Issue