Browse Source

Prevented potential infinite recursion in hashCode/equals

Issue: SPR-11219
pull/292/merge
Juergen Hoeller 12 years ago
parent
commit
994efe45fd
  1. 65
      spring-core/src/main/java/org/springframework/core/ResolvableType.java

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

@ -73,11 +73,9 @@ import org.springframework.util.StringUtils;
* @see #forClass(Class) * @see #forClass(Class)
* @see #forType(Type) * @see #forType(Type)
*/ */
@SuppressWarnings("serial")
public final class ResolvableType implements Serializable { public final class ResolvableType implements Serializable {
private static final long serialVersionUID = 1L;
private static ConcurrentReferenceHashMap<ResolvableType, ResolvableType> cache = private static ConcurrentReferenceHashMap<ResolvableType, ResolvableType> cache =
new ConcurrentReferenceHashMap<ResolvableType, ResolvableType>(); new ConcurrentReferenceHashMap<ResolvableType, ResolvableType>();
@ -93,14 +91,14 @@ public final class ResolvableType implements Serializable {
/** /**
* The underlying java type being managed (only ever {@code null} for {@link #NONE}). * The underlying Java type being managed (only ever {@code null} for {@link #NONE}).
*/ */
private final Type type; private final Type type;
/** /**
* Optional provider for the type. * Optional provider for the type.
*/ */
private TypeProvider typeProvider; private final TypeProvider typeProvider;
/** /**
* The {@code VariableResolver} to use or {@code null} if no resolver is available. * The {@code VariableResolver} to use or {@code null} if no resolver is available.
@ -125,12 +123,13 @@ public final class ResolvableType implements Serializable {
/** /**
* Private constructor used to create a new {@link ResolvableType}. * Private constructor used to create a new {@link ResolvableType}.
* @param type the underlying java type (may only be {@code null} for {@link #NONE}) * @param type the underlying Java type (may only be {@code null} for {@link #NONE})
* @param variableResolver the resolver used for {@link TypeVariable}s (may be {@code null}) * @param variableResolver the resolver used for {@link TypeVariable}s (may be {@code null})
* @param componentType an option declared component type for arrays (may be {@code null}) * @param componentType an option declared component type for arrays (may be {@code null})
*/ */
private ResolvableType(Type type, TypeProvider typeProvider, private ResolvableType(
VariableResolver variableResolver, ResolvableType componentType) { Type type, TypeProvider typeProvider, VariableResolver variableResolver, ResolvableType componentType) {
this.type = type; this.type = type;
this.typeProvider = typeProvider; this.typeProvider = typeProvider;
this.variableResolver = variableResolver; this.variableResolver = variableResolver;
@ -590,7 +589,6 @@ public final class ResolvableType implements Serializable {
if (this.type instanceof ParameterizedType) { if (this.type instanceof ParameterizedType) {
return forType(((ParameterizedType) this.type).getRawType(), this.variableResolver); return forType(((ParameterizedType) this.type).getRawType(), this.variableResolver);
} }
if (this.type instanceof WildcardType) { if (this.type instanceof WildcardType) {
Type resolved = resolveBounds(((WildcardType) this.type).getUpperBounds()); Type resolved = resolveBounds(((WildcardType) this.type).getUpperBounds());
if (resolved == null) { if (resolved == null) {
@ -598,10 +596,8 @@ public final class ResolvableType implements Serializable {
} }
return forType(resolved, this.variableResolver); return forType(resolved, this.variableResolver);
} }
if (this.type instanceof TypeVariable) { if (this.type instanceof TypeVariable) {
TypeVariable<?> variable = (TypeVariable<?>) this.type; TypeVariable<?> variable = (TypeVariable<?>) this.type;
// Try default variable resolution // Try default variable resolution
if (this.variableResolver != null) { if (this.variableResolver != null) {
ResolvableType resolved = this.variableResolver.resolveVariable(variable); ResolvableType resolved = this.variableResolver.resolveVariable(variable);
@ -609,11 +605,9 @@ public final class ResolvableType implements Serializable {
return resolved; return resolved;
} }
} }
// Fallback to bounds // Fallback to bounds
return forType(resolveBounds(variable.getBounds()), this.variableResolver); return forType(resolveBounds(variable.getBounds()), this.variableResolver);
} }
return NONE; return NONE;
} }
@ -628,7 +622,6 @@ public final class ResolvableType implements Serializable {
if (this.type instanceof TypeVariable) { if (this.type instanceof TypeVariable) {
return resolveType().resolveVariable(variable); return resolveType().resolveVariable(variable);
} }
if (this.type instanceof ParameterizedType) { if (this.type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) this.type; ParameterizedType parameterizedType = (ParameterizedType) this.type;
TypeVariable<?>[] variables = resolve().getTypeParameters(); TypeVariable<?>[] variables = resolve().getTypeParameters();
@ -638,16 +631,13 @@ public final class ResolvableType implements Serializable {
return forType(actualType, this.variableResolver); return forType(actualType, this.variableResolver);
} }
} }
if (parameterizedType.getOwnerType() != null) { if (parameterizedType.getOwnerType() != null) {
return forType(parameterizedType.getOwnerType(), this.variableResolver).resolveVariable(variable); return forType(parameterizedType.getOwnerType(), this.variableResolver).resolveVariable(variable);
} }
} }
if (this.variableResolver != null) { if (this.variableResolver != null) {
return this.variableResolver.resolveVariable(variable); return this.variableResolver.resolveVariable(variable);
} }
return null; return null;
} }
@ -673,27 +663,22 @@ public final class ResolvableType implements Serializable {
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj == this) { if (this == obj) {
return true; return true;
} }
if (obj instanceof ResolvableType) { if (!(obj instanceof ResolvableType)) {
ResolvableType other = (ResolvableType) obj; return false;
boolean equals = ObjectUtils.nullSafeEquals(this.type, other.type);
equals &= ObjectUtils.nullSafeEquals(getSource(), other.getSource());
equals &= variableResolverSourceEquals(this.variableResolver, other.variableResolver);
equals &= ObjectUtils.nullSafeEquals(this.componentType, other.componentType);
return equals;
} }
return false; ResolvableType other = (ResolvableType) obj;
return (ObjectUtils.nullSafeEquals(this.type, other.type) &&
ObjectUtils.nullSafeEquals(getSource(), other.getSource()) &&
variableResolverSourceEquals(other.variableResolver) &&
ObjectUtils.nullSafeEquals(this.componentType, other.componentType));
} }
@Override @Override
public int hashCode() { public int hashCode() {
int hashCode = ObjectUtils.nullSafeHashCode(this.type); return ObjectUtils.nullSafeHashCode(this.type);
hashCode = hashCode * 31 + ObjectUtils.nullSafeHashCode(
this.variableResolver == null ? null : this.variableResolver.getSource());
hashCode = hashCode * 31 + ObjectUtils.nullSafeHashCode(this.componentType);
return hashCode;
} }
/** /**
@ -713,10 +698,15 @@ public final class ResolvableType implements Serializable {
return new DefaultVariableResolver(); return new DefaultVariableResolver();
} }
private static boolean variableResolverSourceEquals(VariableResolver o1, VariableResolver o2) { private boolean variableResolverSourceEquals(VariableResolver other) {
Object s1 = (o1 == null ? null : o1.getSource()); if (this.variableResolver == null) {
Object s2 = (o2 == null ? null : o2.getSource()); return (other == null);
return ObjectUtils.nullSafeEquals(s1,s2); }
if (other == null) {
return false;
}
Object src = this.variableResolver.getSource();
return (src == this ? src == other.getSource() : ObjectUtils.nullSafeEquals(src, other.getSource()));
} }
private static ResolvableType[] forTypes(Type[] types, VariableResolver owner) { private static ResolvableType[] forTypes(Type[] types, VariableResolver owner) {
@ -840,6 +830,7 @@ public final class ResolvableType implements Serializable {
*/ */
public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex, public static ResolvableType forConstructorParameter(Constructor<?> constructor, int parameterIndex,
Class<?> implementationClass) { Class<?> implementationClass) {
Assert.notNull(constructor, "Constructor must not be null"); Assert.notNull(constructor, "Constructor must not be null");
MethodParameter methodParameter = new MethodParameter(constructor, parameterIndex); MethodParameter methodParameter = new MethodParameter(constructor, parameterIndex);
methodParameter.setContainingClass(implementationClass); methodParameter.setContainingClass(implementationClass);
@ -923,7 +914,7 @@ public final class ResolvableType implements Serializable {
* @param componentType the component type * @param componentType the component type
* @return a {@link ResolvableType} as an array of the specified component type * @return a {@link ResolvableType} as an array of the specified component type
*/ */
public static ResolvableType forArrayComponent(final ResolvableType componentType) { public static ResolvableType forArrayComponent(ResolvableType componentType) {
Assert.notNull(componentType, "ComponentType must not be null"); Assert.notNull(componentType, "ComponentType must not be null");
Class<?> arrayClass = Array.newInstance(componentType.resolve(), 0).getClass(); Class<?> arrayClass = Array.newInstance(componentType.resolve(), 0).getClass();
return new ResolvableType(arrayClass, null, null, componentType); return new ResolvableType(arrayClass, null, null, componentType);
@ -1035,7 +1026,7 @@ public final class ResolvableType implements Serializable {
Object getSource(); Object getSource();
/** /**
* Resolve the specified varaible. * Resolve the specified variable.
* @param variable the variable to resolve * @param variable the variable to resolve
* @return the resolved variable or {@code null} * @return the resolved variable or {@code null}
*/ */

Loading…
Cancel
Save