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:
@@ -75,7 +75,7 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
|
|||||||
private Class<?> containingClass;
|
private Class<?> containingClass;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private volatile ResolvableType resolvableType;
|
private transient volatile ResolvableType resolvableType;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -430,7 +430,8 @@ public class ResolvableType implements Serializable {
|
|||||||
if (this == NONE) {
|
if (this == NONE) {
|
||||||
return NONE;
|
return NONE;
|
||||||
}
|
}
|
||||||
if (ObjectUtils.nullSafeEquals(resolve(), type)) {
|
Class<?> resolved = resolve();
|
||||||
|
if (resolved == null || resolved == type) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
for (ResolvableType interfaceType : getInterfaces()) {
|
for (ResolvableType interfaceType : getInterfaces()) {
|
||||||
@@ -445,6 +446,7 @@ public class ResolvableType implements Serializable {
|
|||||||
/**
|
/**
|
||||||
* Return a {@link ResolvableType} representing the direct supertype of this type.
|
* Return a {@link ResolvableType} representing the direct supertype of this type.
|
||||||
* If no supertype is available this method returns {@link #NONE}.
|
* If no supertype is available this method returns {@link #NONE}.
|
||||||
|
* <p>Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}.
|
||||||
* @see #getInterfaces()
|
* @see #getInterfaces()
|
||||||
*/
|
*/
|
||||||
public ResolvableType getSuperType() {
|
public ResolvableType getSuperType() {
|
||||||
@@ -454,7 +456,7 @@ public class ResolvableType implements Serializable {
|
|||||||
}
|
}
|
||||||
ResolvableType superType = this.superType;
|
ResolvableType superType = this.superType;
|
||||||
if (superType == null) {
|
if (superType == null) {
|
||||||
superType = forType(SerializableTypeWrapper.forGenericSuperclass(resolved), asVariableResolver());
|
superType = forType(resolved.getGenericSuperclass(), this);
|
||||||
this.superType = superType;
|
this.superType = superType;
|
||||||
}
|
}
|
||||||
return superType;
|
return superType;
|
||||||
@@ -464,16 +466,21 @@ public class ResolvableType implements Serializable {
|
|||||||
* Return a {@link ResolvableType} array representing the direct interfaces
|
* Return a {@link ResolvableType} array representing the direct interfaces
|
||||||
* implemented by this type. If this type does not implement any interfaces an
|
* implemented by this type. If this type does not implement any interfaces an
|
||||||
* empty array is returned.
|
* empty array is returned.
|
||||||
|
* <p>Note: The resulting {@link ResolvableType} instances may not be {@link Serializable}.
|
||||||
* @see #getSuperType()
|
* @see #getSuperType()
|
||||||
*/
|
*/
|
||||||
public ResolvableType[] getInterfaces() {
|
public ResolvableType[] getInterfaces() {
|
||||||
Class<?> resolved = resolve();
|
Class<?> resolved = resolve();
|
||||||
if (resolved == null || ObjectUtils.isEmpty(resolved.getGenericInterfaces())) {
|
if (resolved == null) {
|
||||||
return EMPTY_TYPES_ARRAY;
|
return EMPTY_TYPES_ARRAY;
|
||||||
}
|
}
|
||||||
ResolvableType[] interfaces = this.interfaces;
|
ResolvableType[] interfaces = this.interfaces;
|
||||||
if (interfaces == null) {
|
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;
|
this.interfaces = interfaces;
|
||||||
}
|
}
|
||||||
return interfaces;
|
return interfaces;
|
||||||
@@ -673,8 +680,11 @@ public class ResolvableType implements Serializable {
|
|||||||
ResolvableType[] generics = this.generics;
|
ResolvableType[] generics = this.generics;
|
||||||
if (generics == null) {
|
if (generics == null) {
|
||||||
if (this.type instanceof Class) {
|
if (this.type instanceof Class) {
|
||||||
Class<?> typeClass = (Class<?>) this.type;
|
Type[] typeParams = ((Class<?>) this.type).getTypeParameters();
|
||||||
generics = forTypes(SerializableTypeWrapper.forTypeParameters(typeClass), this.variableResolver);
|
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) {
|
else if (this.type instanceof ParameterizedType) {
|
||||||
Type[] actualTypeArguments = ((ParameterizedType) this.type).getActualTypeArguments();
|
Type[] actualTypeArguments = ((ParameterizedType) this.type).getActualTypeArguments();
|
||||||
@@ -818,7 +828,7 @@ public class ResolvableType implements Serializable {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Type resolveBounds(Type[] bounds) {
|
private Type resolveBounds(Type[] bounds) {
|
||||||
if (ObjectUtils.isEmpty(bounds) || Object.class == bounds[0]) {
|
if (bounds.length == 0 || bounds[0] == Object.class) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return bounds[0];
|
return bounds[0];
|
||||||
@@ -1309,17 +1319,9 @@ public class ResolvableType implements Serializable {
|
|||||||
return new ResolvableType(arrayClass, null, null, componentType);
|
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}.
|
* 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})
|
* @param type the source type (potentially {@code null})
|
||||||
* @return a {@link ResolvableType} for the specified {@link Type}
|
* @return a {@link ResolvableType} for the specified {@link Type}
|
||||||
* @see #forType(Type, ResolvableType)
|
* @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
|
* 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 type the source type or {@code null}
|
||||||
* @param owner the owner type used to resolve variables
|
* @param owner the owner type used to resolve variables
|
||||||
* @return a {@link ResolvableType} for the specified {@link Type} and owner
|
* @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}.
|
* 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
|
* @param typeReference the reference to obtain the source type from
|
||||||
* @return a {@link ResolvableType} for the specified {@link ParameterizedTypeReference}
|
* @return a {@link ResolvableType} for the specified {@link ParameterizedTypeReference}
|
||||||
* @since 4.3.12
|
* @since 4.3.12
|
||||||
|
|||||||
@@ -41,9 +41,7 @@ import org.springframework.util.ReflectionUtils;
|
|||||||
*
|
*
|
||||||
* <p>{@link #forField(Field) Fields} or {@link #forMethodParameter(MethodParameter)
|
* <p>{@link #forField(Field) Fields} or {@link #forMethodParameter(MethodParameter)
|
||||||
* MethodParameters} can be used as the root source for a serializable type.
|
* MethodParameters} can be used as the root source for a serializable type.
|
||||||
* Alternatively the {@link #forGenericSuperclass(Class) superclass},
|
* Alternatively, a regular {@link Class} can also be used as source.
|
||||||
* {@link #forGenericInterfaces(Class) interfaces} or {@link #forTypeParameters(Class)
|
|
||||||
* type parameters} or 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
|
* <p>The returned type will either be a {@link Class} or a serializable proxy of
|
||||||
* {@link GenericArrayType}, {@link ParameterizedType}, {@link TypeVariable} or
|
* {@link GenericArrayType}, {@link ParameterizedType}, {@link TypeVariable} or
|
||||||
@@ -84,41 +82,6 @@ final class SerializableTypeWrapper {
|
|||||||
return forTypeProvider(new MethodParameterTypeProvider(methodParameter));
|
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.
|
* Unwrap the given type, effectively returning the original non-serializable type.
|
||||||
* @param type the type to unwrap
|
* @param type the type to unwrap
|
||||||
|
|||||||
@@ -1246,8 +1246,6 @@ public class ResolvableTypeTests {
|
|||||||
testSerialization(ResolvableType.forMethodReturnType(Methods.class.getMethod("charSequenceReturn")));
|
testSerialization(ResolvableType.forMethodReturnType(Methods.class.getMethod("charSequenceReturn")));
|
||||||
testSerialization(ResolvableType.forConstructorParameter(Constructors.class.getConstructor(List.class), 0));
|
testSerialization(ResolvableType.forConstructorParameter(Constructors.class.getConstructor(List.class), 0));
|
||||||
testSerialization(ResolvableType.forField(Fields.class.getField("charSequenceList")).getGeneric());
|
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);
|
ResolvableType deserializedNone = testSerialization(ResolvableType.NONE);
|
||||||
assertThat(deserializedNone, sameInstance(ResolvableType.NONE));
|
assertThat(deserializedNone, sameInstance(ResolvableType.NONE));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with 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.Type;
|
||||||
import java.lang.reflect.TypeVariable;
|
import java.lang.reflect.TypeVariable;
|
||||||
import java.lang.reflect.WildcardType;
|
import java.lang.reflect.WildcardType;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -65,27 +64,6 @@ public class SerializableTypeWrapperTests {
|
|||||||
assertSerializable(type);
|
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
|
@Test
|
||||||
public void classType() throws Exception {
|
public void classType() throws Exception {
|
||||||
Type type = SerializableTypeWrapper.forField(Fields.class.getField("classType"));
|
Type type = SerializableTypeWrapper.forField(Fields.class.getField("classType"));
|
||||||
|
|||||||
Reference in New Issue
Block a user