Polish ResolvableType & SerializableTypeWrapper

Fix 'missing serialVersionUID' warnings, support for void.class types
and refine resolve() algorithm to support narrowed types.

Issue: SPR-10973
This commit is contained in:
Phillip Webb 2013-10-17 13:06:54 -07:00
parent f4a66a4326
commit f29092db23
3 changed files with 67 additions and 15 deletions

View File

@ -72,6 +72,9 @@ import org.springframework.util.StringUtils;
*/
public final class ResolvableType implements Serializable {
private static final long serialVersionUID = 1L;
private static ConcurrentReferenceHashMap<ResolvableType, ResolvableType> cache =
new ConcurrentReferenceHashMap<ResolvableType, ResolvableType>();
@ -97,8 +100,13 @@ public final class ResolvableType implements Serializable {
private final VariableResolver variableResolver;
/**
* Stored copy of the resolved value or {@code null} if the resolve method has not
* yet been called. {@code void.class} is used when the resolve method failed.
* If resolution has happened and {@link #resolved} contains a valid result.
*/
private boolean isResolved = false;
/**
* Late binding stored copy of the resolved value (valid when {@link #isResolved} is
* true).
*/
private Class<?> resolved;
@ -484,11 +492,11 @@ public final class ResolvableType implements Serializable {
* @see #resolveGenerics()
*/
public Class<?> resolve(Class<?> fallback) {
if (this.resolved == null) {
Class<?> resolvedClass = resolveClass();
this.resolved = (resolvedClass == null ? void.class : resolvedClass);
if (!this.isResolved) {
this.resolved = resolveClass();
this.isResolved = true;
}
return (this.resolved == void.class ? fallback : this.resolved);
return (this.resolved == null ? fallback : this.resolved);
}
private Class<?> resolveClass() {
@ -553,15 +561,12 @@ public final class ResolvableType implements Serializable {
}
if (this.type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) this.type;
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())) {
Type actualType = parameterizedType.getActualTypeArguments()[i];
return forType(actualType, this.variableResolver);
}
TypeVariable<?>[] variables = resolve().getTypeParameters();
for (int i = 0; i < variables.length; i++) {
if (ObjectUtils.nullSafeEquals(variables[i].getName(), variable.getName())) {
Type actualType = parameterizedType.getActualTypeArguments()[i];
return forType(actualType, this.variableResolver);
}
}
@ -637,6 +642,10 @@ public final class ResolvableType implements Serializable {
}
return new VariableResolver() {
private static final long serialVersionUID = 1L;
@Override
public ResolvableType resolveVariable(TypeVariable<?> variable) {
return ResolvableType.this.resolveVariable(variable);
@ -901,7 +910,13 @@ public final class ResolvableType implements Serializable {
final TypeVariable<?>[] typeVariables = sourceClass.getTypeParameters();
Assert.isTrue(typeVariables.length == generics.length,
"Missmatched number of generics specified");
VariableResolver variableResolver = new VariableResolver() {
private static final long serialVersionUID = 1L;
@Override
public ResolvableType resolveVariable(TypeVariable<?> variable) {
for (int i = 0; i < typeVariables.length; i++) {

View File

@ -78,6 +78,10 @@ abstract class SerializableTypeWrapper {
*/
public static Type forGenericSuperclass(final Class<?> type) {
return forTypeProvider(new TypeProvider() {
private static final long serialVersionUID = 1L;
@Override
public Type getType() {
return type.getGenericSuperclass();
@ -93,6 +97,10 @@ abstract class SerializableTypeWrapper {
for (int i = 0; i < result.length; i++) {
final int index = i;
result[i] = forTypeProvider(new TypeProvider() {
private static final long serialVersionUID = 1L;
@Override
public Type getType() {
return type.getGenericInterfaces()[index];
@ -110,6 +118,10 @@ abstract class SerializableTypeWrapper {
for (int i = 0; i < result.length; i++) {
final int index = i;
result[i] = forTypeProvider(new TypeProvider() {
private static final long serialVersionUID = 1L;
@Override
public Type getType() {
return type.getTypeParameters()[index];
@ -162,6 +174,9 @@ abstract class SerializableTypeWrapper {
private static class TypeProxyInvocationHandler implements InvocationHandler,
Serializable {
private static final long serialVersionUID = 1L;
private final TypeProvider provider;
@ -194,6 +209,9 @@ abstract class SerializableTypeWrapper {
*/
private static class FieldTypeProvider implements TypeProvider {
private static final long serialVersionUID = 1L;
private final String fieldName;
private final Class<?> declaringClass;
@ -233,6 +251,9 @@ abstract class SerializableTypeWrapper {
*/
private static class MethodParameterTypeProvider implements TypeProvider {
private static final long serialVersionUID = 1L;
private final String methodName;
private final Class<?>[] parameterTypes;
@ -293,6 +314,9 @@ abstract class SerializableTypeWrapper {
*/
private static class MethodInvokeTypeProvider implements TypeProvider {
private static final long serialVersionUID = 1L;
private final TypeProvider provider;
private final String methodName;

View File

@ -52,7 +52,6 @@ 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.*;
@ -1097,6 +1096,20 @@ public class ResolvableTypeTests {
assertThat(deserializedNone, sameInstance(ResolvableType.NONE));
}
@Test
public void canResolveVoid() throws Exception {
ResolvableType type = ResolvableType.forClass(void.class);
assertThat(type.resolve(), equalTo((Class) void.class));
}
@Test
public void narrow() throws Exception {
ResolvableType type = ResolvableType.forField(Fields.class.getField("stringList"));
ResolvableType narrow = ResolvableType.forType(ArrayList.class, type);
assertThat(narrow.getGeneric().resolve(), equalTo((Class) String.class));
}
private ResolvableType testSerialization(ResolvableType type) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);