Browse Source

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
pull/1893/merge
Juergen Hoeller 8 years ago
parent
commit
cfbacfd89b
  1. 2
      spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java
  2. 39
      spring-core/src/main/java/org/springframework/core/ResolvableType.java
  3. 39
      spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java
  4. 2
      spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java
  5. 24
      spring-core/src/test/java/org/springframework/core/SerializableTypeWrapperTests.java

2
spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java

@ -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;
/** /**

39
spring-core/src/main/java/org/springframework/core/ResolvableType.java

@ -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

39
spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java

@ -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

2
spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java

@ -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));
} }

24
spring-core/src/test/java/org/springframework/core/SerializableTypeWrapperTests.java

@ -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"));

Loading…
Cancel
Save