From f29092db23422d34fba067cf3cf9c72412b96b04 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 17 Oct 2013 13:06:54 -0700 Subject: [PATCH] Polish ResolvableType & SerializableTypeWrapper Fix 'missing serialVersionUID' warnings, support for void.class types and refine resolve() algorithm to support narrowed types. Issue: SPR-10973 --- .../springframework/core/ResolvableType.java | 43 +++++++++++++------ .../core/SerializableTypeWrapper.java | 24 +++++++++++ .../core/ResolvableTypeTests.java | 15 ++++++- 3 files changed, 67 insertions(+), 15 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/ResolvableType.java b/spring-core/src/main/java/org/springframework/core/ResolvableType.java index d2486b393f..acdc30e810 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -72,6 +72,9 @@ import org.springframework.util.StringUtils; */ public final class ResolvableType implements Serializable { + private static final long serialVersionUID = 1L; + + private static ConcurrentReferenceHashMap cache = new ConcurrentReferenceHashMap(); @@ -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++) { diff --git a/spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java b/spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java index d20c8c77af..20c41b3886 100644 --- a/spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java +++ b/spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java @@ -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; diff --git a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java index 3d0aa09af8..60cbf3548e 100644 --- a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java +++ b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java @@ -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);