Revise ResolvableType.as for introspection performance

This revision limits serializability of derived interfaces, superclasses and type parameters, optimizing for introspection performance instead.

Issue: SPR-17070
This commit is contained in:
Juergen Hoeller 2018-07-22 17:49:53 +02:00
parent 5051850fa9
commit cfbacfd89b
5 changed files with 24 additions and 82 deletions

View File

@ -75,7 +75,7 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
private Class<?> containingClass;
@Nullable
private volatile ResolvableType resolvableType;
private transient volatile ResolvableType resolvableType;
/**

View File

@ -430,7 +430,8 @@ public class ResolvableType implements Serializable {
if (this == NONE) {
return NONE;
}
if (ObjectUtils.nullSafeEquals(resolve(), type)) {
Class<?> resolved = resolve();
if (resolved == null || resolved == type) {
return this;
}
for (ResolvableType interfaceType : getInterfaces()) {
@ -445,6 +446,7 @@ public class ResolvableType implements Serializable {
/**
* Return a {@link ResolvableType} representing the direct supertype of this type.
* If no supertype is available this method returns {@link #NONE}.
* <p>Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}.
* @see #getInterfaces()
*/
public ResolvableType getSuperType() {
@ -454,7 +456,7 @@ public class ResolvableType implements Serializable {
}
ResolvableType superType = this.superType;
if (superType == null) {
superType = forType(SerializableTypeWrapper.forGenericSuperclass(resolved), asVariableResolver());
superType = forType(resolved.getGenericSuperclass(), this);
this.superType = superType;
}
return superType;
@ -464,16 +466,21 @@ public class ResolvableType implements Serializable {
* Return a {@link ResolvableType} array representing the direct interfaces
* implemented by this type. If this type does not implement any interfaces an
* empty array is returned.
* <p>Note: The resulting {@link ResolvableType} instances may not be {@link Serializable}.
* @see #getSuperType()
*/
public ResolvableType[] getInterfaces() {
Class<?> resolved = resolve();
if (resolved == null || ObjectUtils.isEmpty(resolved.getGenericInterfaces())) {
if (resolved == null) {
return EMPTY_TYPES_ARRAY;
}
ResolvableType[] interfaces = this.interfaces;
if (interfaces == null) {
interfaces = forTypes(SerializableTypeWrapper.forGenericInterfaces(resolved), asVariableResolver());
Type[] genericIfcs = resolved.getGenericInterfaces();
interfaces = new ResolvableType[genericIfcs.length];
for (int i = 0; i < genericIfcs.length; i++) {
interfaces[i] = forType(genericIfcs[i], this);
}
this.interfaces = interfaces;
}
return interfaces;
@ -673,8 +680,11 @@ public class ResolvableType implements Serializable {
ResolvableType[] generics = this.generics;
if (generics == null) {
if (this.type instanceof Class) {
Class<?> typeClass = (Class<?>) this.type;
generics = forTypes(SerializableTypeWrapper.forTypeParameters(typeClass), this.variableResolver);
Type[] typeParams = ((Class<?>) this.type).getTypeParameters();
generics = new ResolvableType[typeParams.length];
for (int i = 0; i < generics.length; i++) {
generics[i] = ResolvableType.forType(typeParams[i], this);
}
}
else if (this.type instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) this.type).getActualTypeArguments();
@ -818,7 +828,7 @@ public class ResolvableType implements Serializable {
@Nullable
private Type resolveBounds(Type[] bounds) {
if (ObjectUtils.isEmpty(bounds) || Object.class == bounds[0]) {
if (bounds.length == 0 || bounds[0] == Object.class) {
return null;
}
return bounds[0];
@ -1309,17 +1319,9 @@ public class ResolvableType implements Serializable {
return new ResolvableType(arrayClass, null, null, componentType);
}
private static ResolvableType[] forTypes(Type[] types, @Nullable 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 Type}.
* Note: The resulting {@link ResolvableType} may not be {@link Serializable}.
* <p>Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}.
* @param type the source type (potentially {@code null})
* @return a {@link ResolvableType} for the specified {@link Type}
* @see #forType(Type, ResolvableType)
@ -1330,7 +1332,8 @@ public class ResolvableType implements Serializable {
/**
* 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}.
* owner type.
* <p>Note: The resulting {@link ResolvableType} instance 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
@ -1347,7 +1350,7 @@ public class ResolvableType implements Serializable {
/**
* Return a {@link ResolvableType} for the specified {@link ParameterizedTypeReference}.
* Note: The resulting {@link ResolvableType} may not be {@link Serializable}.
* <p>Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}.
* @param typeReference the reference to obtain the source type from
* @return a {@link ResolvableType} for the specified {@link ParameterizedTypeReference}
* @since 4.3.12

View File

@ -41,9 +41,7 @@ import org.springframework.util.ReflectionUtils;
*
* <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.
* Alternatively, 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
@ -84,41 +82,6 @@ final class SerializableTypeWrapper {
return forTypeProvider(new MethodParameterTypeProvider(methodParameter));
}
/**
* Return a {@link Serializable} variant of {@link Class#getGenericSuperclass()}.
*/
@SuppressWarnings("serial")
@Nullable
public static Type forGenericSuperclass(final Class<?> type) {
return forTypeProvider(type::getGenericSuperclass);
}
/**
* Return a {@link Serializable} variant of {@link Class#getGenericInterfaces()}.
*/
@SuppressWarnings("serial")
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(() -> type.getGenericInterfaces()[index]);
}
return result;
}
/**
* Return a {@link Serializable} variant of {@link Class#getTypeParameters()}.
*/
@SuppressWarnings("serial")
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(() -> type.getTypeParameters()[index]);
}
return result;
}
/**
* Unwrap the given type, effectively returning the original non-serializable type.
* @param type the type to unwrap

View File

@ -1246,8 +1246,6 @@ public class ResolvableTypeTests {
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));
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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.
@ -27,7 +27,6 @@ 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;
@ -65,27 +64,6 @@ public class SerializableTypeWrapperTests {
assertSerializable(type);
}
@Test
public void forGenericSuperClass() throws Exception {
Type type = SerializableTypeWrapper.forGenericSuperclass(ArrayList.class);
assertThat(type.toString(), equalTo("java.util.AbstractList<E>"));
assertSerializable(type);
}
@Test
public void forGenericInterfaces() throws Exception {
Type type = SerializableTypeWrapper.forGenericInterfaces(List.class)[0];
assertThat(type.toString(), equalTo("java.util.Collection<E>"));
assertSerializable(type);
}
@Test
public void forTypeParameters() throws Exception {
Type type = SerializableTypeWrapper.forTypeParameters(List.class)[0];
assertThat(type.toString(), equalTo("E"));
assertSerializable(type);
}
@Test
public void classType() throws Exception {
Type type = SerializableTypeWrapper.forField(Fields.class.getField("classType"));