Skip to content

Commit 7fa2a28

Browse files
kilinksbrannen
authored andcommitted
Avoid cloning empty Annotation array in TypeDescriptor
Rework AnnotatedElementAdapter to avoid cloning the underlying Annotation array if it is empty when getAnnotations() is called. Additionally, make the class static and add a factory method that returns a singleton instance for null or empty Annotation arrays. Closes gh-32405
1 parent 8172d7a commit 7fa2a28

File tree

1 file changed

+19
-12
lines changed

1 file changed

+19
-12
lines changed

spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.springframework.core.MethodParameter;
3131
import org.springframework.core.ResolvableType;
3232
import org.springframework.core.annotation.AnnotatedElementUtils;
33+
import org.springframework.lang.NonNull;
3334
import org.springframework.lang.Nullable;
3435
import org.springframework.util.Assert;
3536
import org.springframework.util.ClassUtils;
@@ -52,8 +53,6 @@
5253
@SuppressWarnings("serial")
5354
public class TypeDescriptor implements Serializable {
5455

55-
private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
56-
5756
private static final Map<Class<?>, TypeDescriptor> commonTypesCache = new HashMap<>(32);
5857

5958
private static final Class<?>[] CACHED_COMMON_TYPES = {
@@ -84,7 +83,7 @@ public class TypeDescriptor implements Serializable {
8483
public TypeDescriptor(MethodParameter methodParameter) {
8584
this.resolvableType = ResolvableType.forMethodParameter(methodParameter);
8685
this.type = this.resolvableType.resolve(methodParameter.getNestedParameterType());
87-
this.annotatedElement = new AnnotatedElementAdapter(methodParameter.getParameterIndex() == -1 ?
86+
this.annotatedElement = AnnotatedElementAdapter.from(methodParameter.getParameterIndex() == -1 ?
8887
methodParameter.getMethodAnnotations() : methodParameter.getParameterAnnotations());
8988
}
9089

@@ -96,7 +95,7 @@ public TypeDescriptor(MethodParameter methodParameter) {
9695
public TypeDescriptor(Field field) {
9796
this.resolvableType = ResolvableType.forField(field);
9897
this.type = this.resolvableType.resolve(field.getType());
99-
this.annotatedElement = new AnnotatedElementAdapter(field.getAnnotations());
98+
this.annotatedElement = AnnotatedElementAdapter.from(field.getAnnotations());
10099
}
101100

102101
/**
@@ -109,7 +108,7 @@ public TypeDescriptor(Property property) {
109108
Assert.notNull(property, "Property must not be null");
110109
this.resolvableType = ResolvableType.forMethodParameter(property.getMethodParameter());
111110
this.type = this.resolvableType.resolve(property.getType());
112-
this.annotatedElement = new AnnotatedElementAdapter(property.getAnnotations());
111+
this.annotatedElement = AnnotatedElementAdapter.from(property.getAnnotations());
113112
}
114113

115114
/**
@@ -125,7 +124,7 @@ public TypeDescriptor(Property property) {
125124
public TypeDescriptor(ResolvableType resolvableType, @Nullable Class<?> type, @Nullable Annotation[] annotations) {
126125
this.resolvableType = resolvableType;
127126
this.type = (type != null ? type : resolvableType.toClass());
128-
this.annotatedElement = new AnnotatedElementAdapter(annotations);
127+
this.annotatedElement = AnnotatedElementAdapter.from(annotations);
129128
}
130129

131130

@@ -742,15 +741,23 @@ public static TypeDescriptor nested(Property property, int nestingLevel) {
742741
* @see AnnotatedElementUtils#isAnnotated(AnnotatedElement, Class)
743742
* @see AnnotatedElementUtils#getMergedAnnotation(AnnotatedElement, Class)
744743
*/
745-
private class AnnotatedElementAdapter implements AnnotatedElement, Serializable {
744+
private static final class AnnotatedElementAdapter implements AnnotatedElement, Serializable {
745+
private static final AnnotatedElementAdapter EMPTY = new AnnotatedElementAdapter(new Annotation[0]);
746746

747-
@Nullable
747+
@NonNull
748748
private final Annotation[] annotations;
749749

750-
public AnnotatedElementAdapter(@Nullable Annotation[] annotations) {
750+
private AnnotatedElementAdapter(@NonNull Annotation[] annotations) {
751751
this.annotations = annotations;
752752
}
753753

754+
private static AnnotatedElementAdapter from(@Nullable Annotation[] annotations) {
755+
if (annotations == null || annotations.length == 0) {
756+
return EMPTY;
757+
}
758+
return new AnnotatedElementAdapter(annotations);
759+
}
760+
754761
@Override
755762
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
756763
for (Annotation annotation : getAnnotations()) {
@@ -775,7 +782,7 @@ public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
775782

776783
@Override
777784
public Annotation[] getAnnotations() {
778-
return (this.annotations != null ? this.annotations.clone() : EMPTY_ANNOTATION_ARRAY);
785+
return isEmpty() ? this.annotations : this.annotations.clone();
779786
}
780787

781788
@Override
@@ -784,7 +791,7 @@ public Annotation[] getDeclaredAnnotations() {
784791
}
785792

786793
public boolean isEmpty() {
787-
return ObjectUtils.isEmpty(this.annotations);
794+
return this.annotations.length == 0;
788795
}
789796

790797
@Override
@@ -800,7 +807,7 @@ public int hashCode() {
800807

801808
@Override
802809
public String toString() {
803-
return TypeDescriptor.this.toString();
810+
return "{AnnotatedElementAdapter annotations=" + Arrays.toString(this.annotations) + "}";
804811
}
805812
}
806813

0 commit comments

Comments
 (0)