Skip to content

Commit 58bd057

Browse files
committed
Avoid cloning empty Annotation array in TypeDescriptor (backport)
Closes gh-32405
1 parent a78704a commit 58bd057

File tree

1 file changed

+24
-14
lines changed

1 file changed

+24
-14
lines changed

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

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -52,8 +52,6 @@
5252
@SuppressWarnings("serial")
5353
public class TypeDescriptor implements Serializable {
5454

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

5957
private static final Class<?>[] CACHED_COMMON_TYPES = {
@@ -84,7 +82,7 @@ public class TypeDescriptor implements Serializable {
8482
public TypeDescriptor(MethodParameter methodParameter) {
8583
this.resolvableType = ResolvableType.forMethodParameter(methodParameter);
8684
this.type = this.resolvableType.resolve(methodParameter.getNestedParameterType());
87-
this.annotatedElement = new AnnotatedElementAdapter(methodParameter.getParameterIndex() == -1 ?
85+
this.annotatedElement = AnnotatedElementAdapter.from(methodParameter.getParameterIndex() == -1 ?
8886
methodParameter.getMethodAnnotations() : methodParameter.getParameterAnnotations());
8987
}
9088

@@ -96,7 +94,7 @@ public TypeDescriptor(MethodParameter methodParameter) {
9694
public TypeDescriptor(Field field) {
9795
this.resolvableType = ResolvableType.forField(field);
9896
this.type = this.resolvableType.resolve(field.getType());
99-
this.annotatedElement = new AnnotatedElementAdapter(field.getAnnotations());
97+
this.annotatedElement = AnnotatedElementAdapter.from(field.getAnnotations());
10098
}
10199

102100
/**
@@ -109,7 +107,7 @@ public TypeDescriptor(Property property) {
109107
Assert.notNull(property, "Property must not be null");
110108
this.resolvableType = ResolvableType.forMethodParameter(property.getMethodParameter());
111109
this.type = this.resolvableType.resolve(property.getType());
112-
this.annotatedElement = new AnnotatedElementAdapter(property.getAnnotations());
110+
this.annotatedElement = AnnotatedElementAdapter.from(property.getAnnotations());
113111
}
114112

115113
/**
@@ -125,7 +123,7 @@ public TypeDescriptor(Property property) {
125123
public TypeDescriptor(ResolvableType resolvableType, @Nullable Class<?> type, @Nullable Annotation[] annotations) {
126124
this.resolvableType = resolvableType;
127125
this.type = (type != null ? type : resolvableType.toClass());
128-
this.annotatedElement = new AnnotatedElementAdapter(annotations);
126+
this.annotatedElement = AnnotatedElementAdapter.from(annotations);
129127
}
130128

131129

@@ -547,12 +545,16 @@ public int hashCode() {
547545
public String toString() {
548546
StringBuilder builder = new StringBuilder();
549547
for (Annotation ann : getAnnotations()) {
550-
builder.append('@').append(ann.annotationType().getName()).append(' ');
548+
builder.append('@').append(getName(ann.annotationType())).append(' ');
551549
}
552550
builder.append(getResolvableType());
553551
return builder.toString();
554552
}
555553

554+
private static String getName(Class<?> clazz) {
555+
String canonicalName = clazz.getCanonicalName();
556+
return (canonicalName != null ? canonicalName : clazz.getName());
557+
}
556558

557559
/**
558560
* Create a new type descriptor for an object.
@@ -742,15 +744,23 @@ public static TypeDescriptor nested(Property property, int nestingLevel) {
742744
* @see AnnotatedElementUtils#isAnnotated(AnnotatedElement, Class)
743745
* @see AnnotatedElementUtils#getMergedAnnotation(AnnotatedElement, Class)
744746
*/
745-
private class AnnotatedElementAdapter implements AnnotatedElement, Serializable {
747+
private static final class AnnotatedElementAdapter implements AnnotatedElement, Serializable {
748+
749+
private static final AnnotatedElementAdapter EMPTY = new AnnotatedElementAdapter(new Annotation[0]);
746750

747-
@Nullable
748751
private final Annotation[] annotations;
749752

750-
public AnnotatedElementAdapter(@Nullable Annotation[] annotations) {
753+
private AnnotatedElementAdapter(Annotation[] annotations) {
751754
this.annotations = annotations;
752755
}
753756

757+
private static AnnotatedElementAdapter from(@Nullable Annotation[] annotations) {
758+
if (annotations == null || annotations.length == 0) {
759+
return EMPTY;
760+
}
761+
return new AnnotatedElementAdapter(annotations);
762+
}
763+
754764
@Override
755765
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
756766
for (Annotation annotation : getAnnotations()) {
@@ -775,7 +785,7 @@ public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
775785

776786
@Override
777787
public Annotation[] getAnnotations() {
778-
return (this.annotations != null ? this.annotations.clone() : EMPTY_ANNOTATION_ARRAY);
788+
return (isEmpty() ? this.annotations : this.annotations.clone());
779789
}
780790

781791
@Override
@@ -784,7 +794,7 @@ public Annotation[] getDeclaredAnnotations() {
784794
}
785795

786796
public boolean isEmpty() {
787-
return ObjectUtils.isEmpty(this.annotations);
797+
return (this.annotations.length == 0);
788798
}
789799

790800
@Override
@@ -800,7 +810,7 @@ public int hashCode() {
800810

801811
@Override
802812
public String toString() {
803-
return TypeDescriptor.this.toString();
813+
return "AnnotatedElementAdapter annotations=" + Arrays.toString(this.annotations);
804814
}
805815
}
806816

0 commit comments

Comments
 (0)