Skip to content

Commit

Permalink
Optimize toString() for synthesized annotations
Browse files Browse the repository at this point in the history
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
72b1abd.

Closes gh-24970
  • Loading branch information
sbrannen committed Apr 26, 2020
1 parent db45b80 commit 59ecd49
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ final class SynthesizedMergedAnnotationInvocationHandler<A extends Annotation> i
@Nullable
private volatile Integer hashCode;

@Nullable
private volatile String string;


private SynthesizedMergedAnnotationInvocationHandler(MergedAnnotation<A> annotation, Class<A> type) {
Assert.notNull(annotation, "MergedAnnotation must not be null");
Expand All @@ -78,7 +81,7 @@ public Object invoke(Object proxy, Method method, Object[] args) {
return annotationHashCode();
}
if (ReflectionUtils.isToStringMethod(method)) {
return this.annotation.toString();
return annotationToString();
}
if (isAnnotationTypeMethod(method)) {
return this.type;
Expand Down Expand Up @@ -171,6 +174,44 @@ private int getValueHashCode(Object value) {
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());
Expand All @@ -188,7 +229,7 @@ private Object getAttributeValue(Method method) {
}

/**
* 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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,6 @@ final class TypeMappedAnnotation<A extends Annotation> 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,
Expand Down Expand Up @@ -346,48 +343,6 @@ private boolean isSynthesizable() {
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> T getAttributeValue(String attributeName, Class<T> type) {
Expand Down

0 comments on commit 59ecd49

Please sign in to comment.