Efficient type plus annotation comparisons during converter retrieval

Issue: SPR-14926
Issue: SPR-12926
(cherry picked from commit f6b8b84)
This commit is contained in:
Juergen Hoeller
2016-11-30 22:22:44 +01:00
parent 7ac9f92bc2
commit cbc512f3be
3 changed files with 80 additions and 52 deletions

View File

@@ -72,7 +72,7 @@ public class TypeDescriptor implements Serializable {
private final ResolvableType resolvableType;
private final AnnotatedElement annotatedElement;
private final AnnotatedElementAdapter annotatedElement;
/**
@@ -82,7 +82,6 @@ public class TypeDescriptor implements Serializable {
* @param methodParameter the method parameter
*/
public TypeDescriptor(MethodParameter methodParameter) {
Assert.notNull(methodParameter, "MethodParameter must not be null");
this.resolvableType = ResolvableType.forMethodParameter(methodParameter);
this.type = this.resolvableType.resolve(methodParameter.getParameterType());
this.annotatedElement = new AnnotatedElementAdapter(methodParameter.getParameterIndex() == -1 ?
@@ -95,7 +94,6 @@ public class TypeDescriptor implements Serializable {
* @param field the field
*/
public TypeDescriptor(Field field) {
Assert.notNull(field, "Field must not be null");
this.resolvableType = ResolvableType.forField(field);
this.type = this.resolvableType.resolve(field.getType());
this.annotatedElement = new AnnotatedElementAdapter(field.getAnnotations());
@@ -121,6 +119,7 @@ public class TypeDescriptor implements Serializable {
* @param resolvableType the resolvable type
* @param type the backing type (or {@code null} if it should get resolved)
* @param annotations the type annotations
* @since 4.0
*/
protected TypeDescriptor(ResolvableType resolvableType, Class<?> type, Annotation[] annotations) {
this.resolvableType = resolvableType;
@@ -211,7 +210,7 @@ public class TypeDescriptor implements Serializable {
}
/**
* Returns the name of this type: the fully qualified class name.
* Return the name of this type: the fully qualified class name.
*/
public String getName() {
return ClassUtils.getQualifiedName(getType());
@@ -225,7 +224,7 @@ public class TypeDescriptor implements Serializable {
}
/**
* The annotations associated with this type descriptor, if any.
* Return the annotations associated with this type descriptor, if any.
* @return the annotations, or an empty array if none
*/
public Annotation[] getAnnotations() {
@@ -240,6 +239,11 @@ public class TypeDescriptor implements Serializable {
* @return <tt>true</tt> if the annotation is present
*/
public boolean hasAnnotation(Class<? extends Annotation> annotationType) {
if (this.annotatedElement.isEmpty()) {
// Shortcut: AnnotatedElementUtils would have to expect AnnotatedElement.getAnnotations()
// to return a copy of the array, whereas we can do it more efficiently here.
return false;
}
return AnnotatedElementUtils.isAnnotated(this.annotatedElement, annotationType);
}
@@ -251,6 +255,11 @@ public class TypeDescriptor implements Serializable {
*/
@SuppressWarnings("unchecked")
public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
if (this.annotatedElement.isEmpty()) {
// Shortcut: AnnotatedElementUtils would have to expect AnnotatedElement.getAnnotations()
// to return a copy of the array, whereas we can do it more efficiently here.
return null;
}
return AnnotatedElementUtils.getMergedAnnotation(this.annotatedElement, annotationType);
}
@@ -434,37 +443,51 @@ public class TypeDescriptor implements Serializable {
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(obj instanceof TypeDescriptor)) {
if (!(other instanceof TypeDescriptor)) {
return false;
}
TypeDescriptor other = (TypeDescriptor) obj;
if (!ObjectUtils.nullSafeEquals(this.type, other.type)) {
TypeDescriptor otherDesc = (TypeDescriptor) other;
if (getType() != otherDesc.getType()) {
return false;
}
if (getAnnotations().length != other.getAnnotations().length) {
if (!annotationsMatch(otherDesc)) {
return false;
}
for (Annotation ann : getAnnotations()) {
if (!ann.equals(other.getAnnotation(ann.annotationType()))) {
return false;
}
}
if (isCollection() || isArray()) {
return ObjectUtils.nullSafeEquals(getElementTypeDescriptor(), other.getElementTypeDescriptor());
return ObjectUtils.nullSafeEquals(getElementTypeDescriptor(), otherDesc.getElementTypeDescriptor());
}
else if (isMap()) {
return ObjectUtils.nullSafeEquals(getMapKeyTypeDescriptor(), other.getMapKeyTypeDescriptor()) &&
ObjectUtils.nullSafeEquals(getMapValueTypeDescriptor(), other.getMapValueTypeDescriptor());
return (ObjectUtils.nullSafeEquals(getMapKeyTypeDescriptor(), otherDesc.getMapKeyTypeDescriptor()) &&
ObjectUtils.nullSafeEquals(getMapValueTypeDescriptor(), otherDesc.getMapValueTypeDescriptor()));
}
else {
return true;
}
}
private boolean annotationsMatch(TypeDescriptor otherDesc) {
Annotation[] anns = getAnnotations();
Annotation[] otherAnns = otherDesc.getAnnotations();
if (anns == otherAnns) {
return true;
}
if (anns.length != otherAnns.length) {
return false;
}
if (anns.length > 0) {
for (int i = 0; i < anns.length; i++) {
if (anns[i] != otherAnns[i]) {
return false;
}
}
}
return true;
}
@Override
public int hashCode() {
return getType().hashCode();
@@ -480,6 +503,20 @@ public class TypeDescriptor implements Serializable {
return builder.toString();
}
/**
* Create a new type descriptor for an object.
* <p>Use this factory method to introspect a source object before asking the
* conversion system to convert it to some another type.
* <p>If the provided object is {@code null}, returns {@code null}, else calls
* {@link #valueOf(Class)} to build a TypeDescriptor from the object's class.
* @param source the source object
* @return the type descriptor
*/
public static TypeDescriptor forObject(Object source) {
return (source != null ? valueOf(source.getClass()) : null);
}
/**
* Create a new type descriptor from the given type.
* <p>Use this to instruct the conversion system to convert an object to a
@@ -640,19 +677,6 @@ public class TypeDescriptor implements Serializable {
return nested(new TypeDescriptor(property), nestingLevel);
}
/**
* Create a new type descriptor for an object.
* <p>Use this factory method to introspect a source object before asking the
* conversion system to convert it to some another type.
* <p>If the provided object is {@code null}, returns {@code null}, else calls
* {@link #valueOf(Class)} to build a TypeDescriptor from the object's class.
* @param source the source object
* @return the type descriptor
*/
public static TypeDescriptor forObject(Object source) {
return (source != null ? valueOf(source.getClass()) : null);
}
private static TypeDescriptor nested(TypeDescriptor typeDescriptor, int nestingLevel) {
ResolvableType nested = typeDescriptor.resolvableType;
for (int i = 0; i < nestingLevel; i++) {
@@ -723,6 +747,10 @@ public class TypeDescriptor implements Serializable {
return getAnnotations();
}
public boolean isEmpty() {
return ObjectUtils.isEmpty(this.annotations);
}
@Override
public boolean equals(Object other) {
return (this == other || (other instanceof AnnotatedElementAdapter &&