From 59ecd4997ce1a63da4554f3dcfd7dd176b38d51b Mon Sep 17 00:00:00 2001 From: Sam Brannen Date: Sun, 26 Apr 2020 14:14:07 +0200 Subject: [PATCH] Optimize toString() for synthesized annotations This commit moves the toString() implementation for synthesized annotations from TypeMappedAnnotation to SynthesizedMergedAnnotationInvocationHandler in order to take advantage of the synthesized annotation attribute value cache introduced in 72b1abd226. Closes gh-24970 --- ...izedMergedAnnotationInvocationHandler.java | 45 ++++++++++++++++++- .../core/annotation/TypeMappedAnnotation.java | 45 ------------------- 2 files changed, 43 insertions(+), 47 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedMergedAnnotationInvocationHandler.java b/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedMergedAnnotationInvocationHandler.java index 1a98466a6d8..16a5e82b4b6 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedMergedAnnotationInvocationHandler.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/SynthesizedMergedAnnotationInvocationHandler.java @@ -58,6 +58,9 @@ final class SynthesizedMergedAnnotationInvocationHandler i @Nullable private volatile Integer hashCode; + @Nullable + private volatile String string; + private SynthesizedMergedAnnotationInvocationHandler(MergedAnnotation annotation, Class type) { Assert.notNull(annotation, "MergedAnnotation must not be null"); @@ -78,7 +81,7 @@ final class SynthesizedMergedAnnotationInvocationHandler i return annotationHashCode(); } if (ReflectionUtils.isToStringMethod(method)) { - return this.annotation.toString(); + return annotationToString(); } if (isAnnotationTypeMethod(method)) { return this.type; @@ -171,6 +174,44 @@ final class SynthesizedMergedAnnotationInvocationHandler i return value.hashCode(); } + private String annotationToString() { + String string = this.string; + if (string == null) { + StringBuilder builder = new StringBuilder("@").append(this.type.getName()).append("("); + for (int i = 0; i < this.attributes.size(); i++) { + Method attribute = this.attributes.get(i); + if (i > 0) { + builder.append(", "); + } + builder.append(attribute.getName()); + builder.append("="); + builder.append(toString(getAttributeValue(attribute))); + } + builder.append(")"); + string = builder.toString(); + this.string = string; + } + return string; + } + + private String toString(Object value) { + if (value instanceof Class) { + return ((Class) value).getName(); + } + if (value.getClass().isArray()) { + StringBuilder builder = new StringBuilder("["); + for (int i = 0; i < Array.getLength(value); i++) { + if (i > 0) { + builder.append(", "); + } + builder.append(toString(Array.get(value, i))); + } + builder.append("]"); + return builder.toString(); + } + return String.valueOf(value); + } + private Object getAttributeValue(Method method) { Object value = this.valueCache.computeIfAbsent(method.getName(), attributeName -> { Class type = ClassUtils.resolvePrimitiveIfNecessary(method.getReturnType()); @@ -188,7 +229,7 @@ final class SynthesizedMergedAnnotationInvocationHandler i } /** - * Clone the provided array, ensuring that original component type is retained. + * Clone the provided array, ensuring that the original component type is retained. * @param array the array to clone */ private Object cloneArray(Object array) { diff --git a/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java b/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java index 0ff07a09922..ede74c09baf 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java @@ -110,9 +110,6 @@ final class TypeMappedAnnotation extends AbstractMergedAnn private final int[] resolvedMirrors; - @Nullable - private String string; - private TypeMappedAnnotation(AnnotationTypeMapping mapping, @Nullable ClassLoader classLoader, @Nullable Object source, @Nullable Object rootAttributes, ValueExtractor valueExtractor, @@ -346,48 +343,6 @@ final class TypeMappedAnnotation extends AbstractMergedAnn return this.mapping.isSynthesizable(); } - @Override - public String toString() { - String string = this.string; - if (string == null) { - StringBuilder builder = new StringBuilder(); - builder.append("@"); - builder.append(getType().getName()); - builder.append("("); - for (int i = 0; i < this.mapping.getAttributes().size(); i++) { - Method attribute = this.mapping.getAttributes().get(i); - builder.append(i == 0 ? "" : ", "); - builder.append(attribute.getName()); - builder.append("="); - builder.append(toString(getValue(i, Object.class))); - } - builder.append(")"); - string = builder.toString(); - this.string = string; - } - return string; - } - - private Object toString(@Nullable Object value) { - if (value == null) { - return ""; - } - if (value instanceof Class) { - return ((Class) value).getName(); - } - if (value.getClass().isArray()) { - StringBuilder builder = new StringBuilder(); - builder.append("["); - for (int i = 0; i < Array.getLength(value); i++) { - builder.append(i == 0 ? "" : ", "); - builder.append(toString(Array.get(value, i))); - } - builder.append("]"); - return builder.toString(); - } - return String.valueOf(value); - } - @Override @Nullable protected T getAttributeValue(String attributeName, Class type) {