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 0891a419c3e..1a98466a6d8 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 @@ -18,11 +18,14 @@ package org.springframework.core.annotation; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Array; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; +import java.util.Map; import java.util.NoSuchElementException; +import java.util.concurrent.ConcurrentHashMap; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -50,6 +53,8 @@ final class SynthesizedMergedAnnotationInvocationHandler i private final AttributeMethods attributes; + private final Map valueCache = new ConcurrentHashMap<>(8); + @Nullable private volatile Integer hashCode; @@ -167,11 +172,53 @@ final class SynthesizedMergedAnnotationInvocationHandler i } private Object getAttributeValue(Method method) { - String name = method.getName(); - Class type = ClassUtils.resolvePrimitiveIfNecessary(method.getReturnType()); - return this.annotation.getValue(name, type).orElseThrow( - () -> new NoSuchElementException("No value found for attribute named '" + name + - "' in merged annotation " + this.annotation.getType().getName())); + Object value = this.valueCache.computeIfAbsent(method.getName(), attributeName -> { + Class type = ClassUtils.resolvePrimitiveIfNecessary(method.getReturnType()); + return this.annotation.getValue(attributeName, type).orElseThrow( + () -> new NoSuchElementException("No value found for attribute named '" + attributeName + + "' in merged annotation " + this.annotation.getType().getName())); + }); + + // Clone non-empty arrays so that users cannot alter the contents of values in our cache. + if (value.getClass().isArray() && Array.getLength(value) > 0) { + value = cloneArray(value); + } + + return value; + } + + /** + * Clone the provided array, ensuring that original component type is retained. + * @param array the array to clone + */ + private Object cloneArray(Object array) { + if (array instanceof boolean[]) { + return ((boolean[]) array).clone(); + } + if (array instanceof byte[]) { + return ((byte[]) array).clone(); + } + if (array instanceof char[]) { + return ((char[]) array).clone(); + } + if (array instanceof double[]) { + return ((double[]) array).clone(); + } + if (array instanceof float[]) { + return ((float[]) array).clone(); + } + if (array instanceof int[]) { + return ((int[]) array).clone(); + } + if (array instanceof long[]) { + return ((long[]) array).clone(); + } + if (array instanceof short[]) { + return ((short[]) array).clone(); + } + + // else + return ((Object[]) array).clone(); } @SuppressWarnings("unchecked") @@ -183,6 +230,7 @@ final class SynthesizedMergedAnnotationInvocationHandler i return (A) Proxy.newProxyInstance(classLoader, interfaces, handler); } + private static boolean isVisible(ClassLoader classLoader, Class interfaceClass) { if (classLoader == interfaceClass.getClassLoader()) { return true;