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.Type;
|
||||||
import java.lang.reflect.TypeVariable;
|
import java.lang.reflect.TypeVariable;
|
||||||
import java.lang.reflect.WildcardType;
|
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.Assert;
|
||||||
|
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class for resolving generic types against type variables.
|
* Helper class for resolving generic types against type variables.
|
||||||
|
|
@ -38,6 +42,11 @@ import org.springframework.util.Assert;
|
||||||
*/
|
*/
|
||||||
public abstract class GenericTypeResolver {
|
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.
|
* Determine the target type for the given generic parameter type.
|
||||||
* @param methodParameter the method parameter specification
|
* @param methodParameter the method parameter specification
|
||||||
|
|
@ -196,4 +205,80 @@ public abstract class GenericTypeResolver {
|
||||||
return ResolvableType.NONE;
|
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;
|
package org.springframework.core;
|
||||||
|
|
||||||
import java.io.Serializable;
|
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.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
@ -32,7 +36,7 @@ import static org.springframework.util.ReflectionUtils.*;
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
public class GenericTypeResolverTests {
|
public class GenericTypeResolverTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -72,11 +76,69 @@ public class GenericTypeResolverTests {
|
||||||
resolveReturnTypeArgument(findMethod(MyTypeWithMethods.class, "object"), MyInterfaceType.class));
|
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
|
@Test
|
||||||
public void testBoundParameterizedType() {
|
public void testBoundParameterizedType() {
|
||||||
assertEquals(B.class, resolveTypeArgument(TestImpl.class, TestIfc.class));
|
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
|
@Test
|
||||||
public void getGenericsCannotBeResolved() throws Exception {
|
public void getGenericsCannotBeResolved() throws Exception {
|
||||||
// SPR-11030
|
// SPR-11030
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue