From cfbacfd89bfb4ef93d1603f4ceca7a4906fc5c3f Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sun, 22 Jul 2018 17:49:53 +0200 Subject: [PATCH] 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 --- .../factory/config/DependencyDescriptor.java | 2 +- .../springframework/core/ResolvableType.java | 39 ++++++++++--------- .../core/SerializableTypeWrapper.java | 39 +------------------ .../core/ResolvableTypeTests.java | 2 - .../core/SerializableTypeWrapperTests.java | 24 +----------- 5 files changed, 24 insertions(+), 82 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java index dcb217446c9..146f781077e 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/DependencyDescriptor.java +++ b/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; @Nullable - private volatile ResolvableType resolvableType; + private transient volatile ResolvableType resolvableType; /** 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 dca9dd14c9c..0381967b589 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -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}. + *

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. + *

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}. + *

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. + *

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}. + *

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 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 1fee0a57732..578499ea8e9 100644 --- a/spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java +++ b/spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java @@ -41,9 +41,7 @@ import org.springframework.util.ReflectionUtils; * *

{@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. * *

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 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 ddb84f1de86..3dde706f04a 100644 --- a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java +++ b/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.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)); } diff --git a/spring-core/src/test/java/org/springframework/core/SerializableTypeWrapperTests.java b/spring-core/src/test/java/org/springframework/core/SerializableTypeWrapperTests.java index 85bad142eaf..96b3c5ff10b 100644 --- a/spring-core/src/test/java/org/springframework/core/SerializableTypeWrapperTests.java +++ b/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"); * 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")); - assertSerializable(type); - } - - @Test - public void forGenericInterfaces() throws Exception { - Type type = SerializableTypeWrapper.forGenericInterfaces(List.class)[0]; - assertThat(type.toString(), equalTo("java.util.Collection")); - 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"));