From 47b7d31d2ddad8f40b7393fa77aa026afa08f374 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Sat, 16 Mar 2024 14:21:34 +0100 Subject: [PATCH] Avoid cloning empty Annotation array in TypeDescriptor (backport) Closes gh-32405 --- .../core/convert/TypeDescriptor.java | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java index 0c0c7dec8c4e..96c9b02ac0a8 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java +++ b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 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. @@ -52,8 +52,6 @@ @SuppressWarnings("serial") public class TypeDescriptor implements Serializable { - private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0]; - private static final Map, TypeDescriptor> commonTypesCache = new HashMap<>(32); private static final Class[] CACHED_COMMON_TYPES = { @@ -84,7 +82,7 @@ public class TypeDescriptor implements Serializable { public TypeDescriptor(MethodParameter methodParameter) { this.resolvableType = ResolvableType.forMethodParameter(methodParameter); this.type = this.resolvableType.resolve(methodParameter.getNestedParameterType()); - this.annotatedElement = new AnnotatedElementAdapter(methodParameter.getParameterIndex() == -1 ? + this.annotatedElement = AnnotatedElementAdapter.from(methodParameter.getParameterIndex() == -1 ? methodParameter.getMethodAnnotations() : methodParameter.getParameterAnnotations()); } @@ -96,7 +94,7 @@ public TypeDescriptor(MethodParameter methodParameter) { public TypeDescriptor(Field field) { this.resolvableType = ResolvableType.forField(field); this.type = this.resolvableType.resolve(field.getType()); - this.annotatedElement = new AnnotatedElementAdapter(field.getAnnotations()); + this.annotatedElement = AnnotatedElementAdapter.from(field.getAnnotations()); } /** @@ -109,7 +107,7 @@ public TypeDescriptor(Property property) { Assert.notNull(property, "Property must not be null"); this.resolvableType = ResolvableType.forMethodParameter(property.getMethodParameter()); this.type = this.resolvableType.resolve(property.getType()); - this.annotatedElement = new AnnotatedElementAdapter(property.getAnnotations()); + this.annotatedElement = AnnotatedElementAdapter.from(property.getAnnotations()); } /** @@ -125,7 +123,7 @@ public TypeDescriptor(Property property) { public TypeDescriptor(ResolvableType resolvableType, @Nullable Class type, @Nullable Annotation[] annotations) { this.resolvableType = resolvableType; this.type = (type != null ? type : resolvableType.toClass()); - this.annotatedElement = new AnnotatedElementAdapter(annotations); + this.annotatedElement = AnnotatedElementAdapter.from(annotations); } @@ -513,12 +511,16 @@ public int hashCode() { public String toString() { StringBuilder builder = new StringBuilder(); for (Annotation ann : getAnnotations()) { - builder.append('@').append(ann.annotationType().getName()).append(' '); + builder.append('@').append(getName(ann.annotationType())).append(' '); } builder.append(getResolvableType()); return builder.toString(); } + private static String getName(Class clazz) { + String canonicalName = clazz.getCanonicalName(); + return (canonicalName != null ? canonicalName : clazz.getName()); + } /** * Create a new type descriptor for an object. @@ -734,15 +736,23 @@ private static TypeDescriptor getRelatedIfResolvable(TypeDescriptor source, Reso * @see AnnotatedElementUtils#isAnnotated(AnnotatedElement, Class) * @see AnnotatedElementUtils#getMergedAnnotation(AnnotatedElement, Class) */ - private class AnnotatedElementAdapter implements AnnotatedElement, Serializable { + private static final class AnnotatedElementAdapter implements AnnotatedElement, Serializable { + + private static final AnnotatedElementAdapter EMPTY = new AnnotatedElementAdapter(new Annotation[0]); - @Nullable private final Annotation[] annotations; - public AnnotatedElementAdapter(@Nullable Annotation[] annotations) { + private AnnotatedElementAdapter(Annotation[] annotations) { this.annotations = annotations; } + private static AnnotatedElementAdapter from(@Nullable Annotation[] annotations) { + if (annotations == null || annotations.length == 0) { + return EMPTY; + } + return new AnnotatedElementAdapter(annotations); + } + @Override public boolean isAnnotationPresent(Class annotationClass) { for (Annotation annotation : getAnnotations()) { @@ -767,7 +777,7 @@ public T getAnnotation(Class annotationClass) { @Override public Annotation[] getAnnotations() { - return (this.annotations != null ? this.annotations.clone() : EMPTY_ANNOTATION_ARRAY); + return (isEmpty() ? this.annotations : this.annotations.clone()); } @Override @@ -776,7 +786,7 @@ public Annotation[] getDeclaredAnnotations() { } public boolean isEmpty() { - return ObjectUtils.isEmpty(this.annotations); + return (this.annotations.length == 0); } @Override @@ -792,7 +802,7 @@ public int hashCode() { @Override public String toString() { - return TypeDescriptor.this.toString(); + return "AnnotatedElementAdapter annotations=" + Arrays.toString(this.annotations); } }