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 eefc51f8a0c..1c400202f3f 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 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. @@ -117,36 +117,35 @@ public class ResolvableType implements Serializable { @Nullable private final ResolvableType componentType; - /** - * Copy of the resolved value. - */ @Nullable - private final Class resolved; + private final Integer hash; @Nullable - private final Integer hash; + private Class resolved; @Nullable - private ResolvableType superType; + private volatile ResolvableType superType; @Nullable - private ResolvableType[] interfaces; + private volatile ResolvableType[] interfaces; @Nullable - private ResolvableType[] generics; + private volatile ResolvableType[] generics; /** * Private constructor used to create a new {@link ResolvableType} for cache key purposes, * with no upfront resolution. */ - private ResolvableType(Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) { + private ResolvableType( + Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) { + this.type = type; this.typeProvider = typeProvider; this.variableResolver = variableResolver; this.componentType = null; - this.resolved = null; this.hash = calculateHashCode(); + this.resolved = null; } /** @@ -161,8 +160,8 @@ public class ResolvableType implements Serializable { this.typeProvider = typeProvider; this.variableResolver = variableResolver; this.componentType = null; - this.resolved = resolveClass(); this.hash = hash; + this.resolved = resolveClass(); } /** @@ -176,8 +175,8 @@ public class ResolvableType implements Serializable { this.typeProvider = typeProvider; this.variableResolver = variableResolver; this.componentType = componentType; - this.resolved = resolveClass(); this.hash = null; + this.resolved = resolveClass(); } /** @@ -453,10 +452,12 @@ public class ResolvableType implements Serializable { if (resolved == null || resolved.getGenericSuperclass() == null) { return NONE; } - if (this.superType == null) { - this.superType = forType(SerializableTypeWrapper.forGenericSuperclass(resolved), asVariableResolver()); + ResolvableType superType = this.superType; + if (superType == null) { + superType = forType(SerializableTypeWrapper.forGenericSuperclass(resolved), asVariableResolver()); + this.superType = superType; } - return this.superType; + return superType; } /** @@ -470,10 +471,12 @@ public class ResolvableType implements Serializable { if (resolved == null || ObjectUtils.isEmpty(resolved.getGenericInterfaces())) { return EMPTY_TYPES_ARRAY; } - if (this.interfaces == null) { - this.interfaces = forTypes(SerializableTypeWrapper.forGenericInterfaces(resolved), asVariableResolver()); + ResolvableType[] interfaces = this.interfaces; + if (interfaces == null) { + interfaces = forTypes(SerializableTypeWrapper.forGenericInterfaces(resolved), asVariableResolver()); + this.interfaces = interfaces; } - return this.interfaces; + return interfaces; } /** @@ -667,24 +670,25 @@ public class ResolvableType implements Serializable { if (this == NONE) { return EMPTY_TYPES_ARRAY; } - if (this.generics == null) { + ResolvableType[] generics = this.generics; + if (generics == null) { if (this.type instanceof Class) { Class typeClass = (Class) this.type; - this.generics = forTypes(SerializableTypeWrapper.forTypeParameters(typeClass), this.variableResolver); + generics = forTypes(SerializableTypeWrapper.forTypeParameters(typeClass), this.variableResolver); } else if (this.type instanceof ParameterizedType) { Type[] actualTypeArguments = ((ParameterizedType) this.type).getActualTypeArguments(); - ResolvableType[] generics = new ResolvableType[actualTypeArguments.length]; + generics = new ResolvableType[actualTypeArguments.length]; for (int i = 0; i < actualTypeArguments.length; i++) { generics[i] = forType(actualTypeArguments[i], this.variableResolver); } - this.generics = generics; } else { - this.generics = resolveType().getGenerics(); + generics = resolveType().getGenerics(); } + this.generics = generics; } - return this.generics; + return generics; } /** @@ -748,7 +752,7 @@ public class ResolvableType implements Serializable { */ @Nullable public Class resolve() { - return (this.resolved != null ? this.resolved : null); + return this.resolved; } /** @@ -1372,7 +1376,9 @@ public class ResolvableType implements Serializable { * @param variableResolver the variable resolver or {@code null} * @return a {@link ResolvableType} for the specified {@link Type} and {@link VariableResolver} */ - static ResolvableType forType(@Nullable Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) { + static ResolvableType forType( + @Nullable Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) { + if (type == null && typeProvider != null) { type = SerializableTypeWrapper.forTypeProvider(typeProvider); } @@ -1390,13 +1396,14 @@ public class ResolvableType implements Serializable { cache.purgeUnreferencedEntries(); // Check the cache - we may have a ResolvableType which has been resolved before... - ResolvableType key = new ResolvableType(type, typeProvider, variableResolver); - ResolvableType resolvableType = cache.get(key); - if (resolvableType == null) { - resolvableType = new ResolvableType(type, typeProvider, variableResolver, key.hash); - cache.put(resolvableType, resolvableType); - } - return resolvableType; + ResolvableType resultType = new ResolvableType(type, typeProvider, variableResolver); + ResolvableType cachedType = cache.get(resultType); + if (cachedType == null) { + cachedType = new ResolvableType(type, typeProvider, variableResolver, resultType.hash); + cache.put(cachedType, cachedType); + } + resultType.resolved = cachedType.resolved; + return resultType; } /** 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 ef84035d08f..9da4e13e116 100644 --- a/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java +++ b/spring-core/src/test/java/org/springframework/core/ResolvableTypeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 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. @@ -176,11 +176,13 @@ public class ResolvableTypeTests { ResolvableType type = ResolvableType.forField(field); assertThat(type.getType(), equalTo(field.getGenericType())); assertThat(type.resolve(), equalTo((Class) List.class)); + assertThat(type.getSource(), sameInstance(field)); Field field2 = Fields.class.getDeclaredField("otherPrivateField"); ResolvableType type2 = ResolvableType.forField(field2); assertThat(type2.getType(), equalTo(field2.getGenericType())); assertThat(type2.resolve(), equalTo((Class) List.class)); + assertThat(type2.getSource(), sameInstance(field2)); assertEquals(type, type2); assertEquals(type.hashCode(), type2.hashCode());