Avoid reflection for annotation attribute method invocations
As a follow up to 332b25b680, this commit consistently avoids the use of
reflection for annotation attribute method invocations.
See gh-29301
Closes gh-29448
This commit is contained in:
@@ -32,7 +32,6 @@ import java.util.Set;
|
|||||||
import org.springframework.core.annotation.AnnotationTypeMapping.MirrorSets.MirrorSet;
|
import org.springframework.core.annotation.AnnotationTypeMapping.MirrorSets.MirrorSet;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -241,7 +240,7 @@ final class AnnotationTypeMapping {
|
|||||||
mapping.claimedAliases.addAll(aliases);
|
mapping.claimedAliases.addAll(aliases);
|
||||||
if (mapping.annotation != null) {
|
if (mapping.annotation != null) {
|
||||||
int[] resolvedMirrors = mapping.mirrorSets.resolve(null,
|
int[] resolvedMirrors = mapping.mirrorSets.resolve(null,
|
||||||
mapping.annotation, ReflectionUtils::invokeMethod);
|
mapping.annotation, AnnotationUtils::invokeAnnotationMethod);
|
||||||
for (int i = 0; i < mapping.attributes.size(); i++) {
|
for (int i = 0; i < mapping.attributes.size(); i++) {
|
||||||
if (aliases.contains(mapping.attributes.get(i))) {
|
if (aliases.contains(mapping.attributes.get(i))) {
|
||||||
this.annotationValueMappings[attributeIndex] = resolvedMirrors[i];
|
this.annotationValueMappings[attributeIndex] = resolvedMirrors[i];
|
||||||
@@ -505,7 +504,7 @@ final class AnnotationTypeMapping {
|
|||||||
if (source == this && metaAnnotationsOnly) {
|
if (source == this && metaAnnotationsOnly) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return ReflectionUtils.invokeMethod(source.attributes.get(mappedIndex), source.annotation);
|
return AnnotationUtils.invokeAnnotationMethod(source.attributes.get(mappedIndex), source.annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -594,7 +593,7 @@ final class AnnotationTypeMapping {
|
|||||||
AttributeMethods attributes = AttributeMethods.forAnnotationType(annotation.annotationType());
|
AttributeMethods attributes = AttributeMethods.forAnnotationType(annotation.annotationType());
|
||||||
for (int i = 0; i < attributes.size(); i++) {
|
for (int i = 0; i < attributes.size(); i++) {
|
||||||
Method attribute = attributes.get(i);
|
Method attribute = attributes.get(i);
|
||||||
Object value1 = ReflectionUtils.invokeMethod(attribute, annotation);
|
Object value1 = AnnotationUtils.invokeAnnotationMethod(attribute, annotation);
|
||||||
Object value2;
|
Object value2;
|
||||||
if (extractedValue instanceof TypeMappedAnnotation) {
|
if (extractedValue instanceof TypeMappedAnnotation) {
|
||||||
value2 = ((TypeMappedAnnotation<?>) extractedValue).getValue(attribute.getName()).orElse(null);
|
value2 = ((TypeMappedAnnotation<?>) extractedValue).getValue(attribute.getName()).orElse(null);
|
||||||
|
|||||||
@@ -19,9 +19,10 @@ package org.springframework.core.annotation;
|
|||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.AnnotatedElement;
|
import java.lang.reflect.AnnotatedElement;
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationHandler;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -1052,23 +1053,42 @@ public abstract class AnnotationUtils {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Method method = annotation.annotationType().getDeclaredMethod(attributeName);
|
Method method = annotation.annotationType().getDeclaredMethod(attributeName);
|
||||||
ReflectionUtils.makeAccessible(method);
|
return invokeAnnotationMethod(method, annotation);
|
||||||
return method.invoke(annotation);
|
|
||||||
}
|
}
|
||||||
catch (NoSuchMethodException ex) {
|
catch (NoSuchMethodException ex) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
catch (InvocationTargetException ex) {
|
|
||||||
rethrowAnnotationConfigurationException(ex.getTargetException());
|
|
||||||
throw new IllegalStateException("Could not obtain value for annotation attribute '" +
|
|
||||||
attributeName + "' in " + annotation, ex);
|
|
||||||
}
|
|
||||||
catch (Throwable ex) {
|
catch (Throwable ex) {
|
||||||
|
rethrowAnnotationConfigurationException(ex);
|
||||||
handleIntrospectionFailure(annotation.getClass(), ex);
|
handleIntrospectionFailure(annotation.getClass(), ex);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoke the supplied annotation attribute {@link Method} on the supplied
|
||||||
|
* {@link Annotation}.
|
||||||
|
* <p>An attempt will first be made to invoke the method via the annotation's
|
||||||
|
* {@link InvocationHandler} (if the annotation instance is a JDK dynamic proxy).
|
||||||
|
* If that fails, an attempt will be made to invoke the method via reflection.
|
||||||
|
* @param method the method to invoke
|
||||||
|
* @param annotation the annotation on which to invoke the method
|
||||||
|
* @return the value returned from the method invocation
|
||||||
|
* @since 5.3.24
|
||||||
|
*/
|
||||||
|
static Object invokeAnnotationMethod(Method method, Object annotation) {
|
||||||
|
if (Proxy.isProxyClass(annotation.getClass())) {
|
||||||
|
try {
|
||||||
|
InvocationHandler handler = Proxy.getInvocationHandler(annotation);
|
||||||
|
return handler.invoke(annotation, method, null);
|
||||||
|
}
|
||||||
|
catch (Throwable ex) {
|
||||||
|
// ignore and fall back to reflection below
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ReflectionUtils.invokeMethod(method, annotation);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the supplied throwable is an {@link AnnotationConfigurationException},
|
* If the supplied throwable is an {@link AnnotationConfigurationException},
|
||||||
* it will be cast to an {@code AnnotationConfigurationException} and thrown,
|
* it will be cast to an {@code AnnotationConfigurationException} and thrown,
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ final class AttributeMethods {
|
|||||||
for (int i = 0; i < size(); i++) {
|
for (int i = 0; i < size(); i++) {
|
||||||
if (canThrowTypeNotPresentException(i)) {
|
if (canThrowTypeNotPresentException(i)) {
|
||||||
try {
|
try {
|
||||||
get(i).invoke(annotation);
|
AnnotationUtils.invokeAnnotationMethod(get(i), annotation);
|
||||||
}
|
}
|
||||||
catch (Throwable ex) {
|
catch (Throwable ex) {
|
||||||
return false;
|
return false;
|
||||||
@@ -134,7 +134,7 @@ final class AttributeMethods {
|
|||||||
for (int i = 0; i < size(); i++) {
|
for (int i = 0; i < size(); i++) {
|
||||||
if (canThrowTypeNotPresentException(i)) {
|
if (canThrowTypeNotPresentException(i)) {
|
||||||
try {
|
try {
|
||||||
get(i).invoke(annotation);
|
AnnotationUtils.invokeAnnotationMethod(get(i), annotation);
|
||||||
}
|
}
|
||||||
catch (Throwable ex) {
|
catch (Throwable ex) {
|
||||||
throw new IllegalStateException("Could not obtain annotation attribute value for " +
|
throw new IllegalStateException("Could not obtain annotation attribute value for " +
|
||||||
|
|||||||
@@ -18,16 +18,13 @@ package org.springframework.core.annotation;
|
|||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.annotation.Repeatable;
|
import java.lang.annotation.Repeatable;
|
||||||
import java.lang.reflect.InvocationHandler;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Proxy;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Strategy used to determine annotations that act as containers for other
|
* Strategy used to determine annotations that act as containers for other
|
||||||
@@ -133,19 +130,6 @@ public abstract class RepeatableContainers {
|
|||||||
return NoRepeatableContainers.INSTANCE;
|
return NoRepeatableContainers.INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Object invokeAnnotationMethod(Annotation annotation, Method method) {
|
|
||||||
if (Proxy.isProxyClass(annotation.getClass())) {
|
|
||||||
try {
|
|
||||||
InvocationHandler handler = Proxy.getInvocationHandler(annotation);
|
|
||||||
return handler.invoke(annotation, method, null);
|
|
||||||
}
|
|
||||||
catch (Throwable ex) {
|
|
||||||
// ignore and fall back to reflection below
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ReflectionUtils.invokeMethod(method, annotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Standard {@link RepeatableContainers} implementation that searches using
|
* Standard {@link RepeatableContainers} implementation that searches using
|
||||||
@@ -168,7 +152,7 @@ public abstract class RepeatableContainers {
|
|||||||
Annotation[] findRepeatedAnnotations(Annotation annotation) {
|
Annotation[] findRepeatedAnnotations(Annotation annotation) {
|
||||||
Method method = getRepeatedAnnotationsMethod(annotation.annotationType());
|
Method method = getRepeatedAnnotationsMethod(annotation.annotationType());
|
||||||
if (method != null) {
|
if (method != null) {
|
||||||
return (Annotation[]) invokeAnnotationMethod(annotation, method);
|
return (Annotation[]) AnnotationUtils.invokeAnnotationMethod(method, annotation);
|
||||||
}
|
}
|
||||||
return super.findRepeatedAnnotations(annotation);
|
return super.findRepeatedAnnotations(annotation);
|
||||||
}
|
}
|
||||||
@@ -255,7 +239,7 @@ public abstract class RepeatableContainers {
|
|||||||
@Nullable
|
@Nullable
|
||||||
Annotation[] findRepeatedAnnotations(Annotation annotation) {
|
Annotation[] findRepeatedAnnotations(Annotation annotation) {
|
||||||
if (this.container.isAssignableFrom(annotation.annotationType())) {
|
if (this.container.isAssignableFrom(annotation.annotationType())) {
|
||||||
return (Annotation[]) invokeAnnotationMethod(annotation, this.valueMethod);
|
return (Annotation[]) AnnotationUtils.invokeAnnotationMethod(this.valueMethod, annotation);
|
||||||
}
|
}
|
||||||
return super.findRepeatedAnnotations(annotation);
|
return super.findRepeatedAnnotations(annotation);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ final class SynthesizedMergedAnnotationInvocationHandler<A extends Annotation> i
|
|||||||
for (int i = 0; i < this.attributes.size(); i++) {
|
for (int i = 0; i < this.attributes.size(); i++) {
|
||||||
Method attribute = this.attributes.get(i);
|
Method attribute = this.attributes.get(i);
|
||||||
Object thisValue = getAttributeValue(attribute);
|
Object thisValue = getAttributeValue(attribute);
|
||||||
Object otherValue = ReflectionUtils.invokeMethod(attribute, other);
|
Object otherValue = AnnotationUtils.invokeAnnotationMethod(attribute, other);
|
||||||
if (!ObjectUtils.nullSafeEquals(thisValue, otherValue)) {
|
if (!ObjectUtils.nullSafeEquals(thisValue, otherValue)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import java.util.function.Predicate;
|
|||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.ReflectionUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link MergedAnnotation} that adapts attributes from a root annotation by
|
* {@link MergedAnnotation} that adapts attributes from a root annotation by
|
||||||
@@ -43,9 +42,9 @@ import org.springframework.util.ReflectionUtils;
|
|||||||
* {@code BiFunction}. This allows various different annotation models to be
|
* {@code BiFunction}. This allows various different annotation models to be
|
||||||
* supported by the same class. For example, the attributes source might be an
|
* supported by the same class. For example, the attributes source might be an
|
||||||
* actual {@link Annotation} instance where methods on the annotation instance
|
* actual {@link Annotation} instance where methods on the annotation instance
|
||||||
* are {@linkplain ReflectionUtils#invokeMethod(Method, Object) invoked} to extract
|
* are {@linkplain AnnotationUtils#invokeAnnotationMethod(Method, Object) invoked}
|
||||||
* values. Equally, the source could be a simple {@link Map} with values
|
* to extract values. Similarly, the source could be a simple {@link Map} with
|
||||||
* extracted using {@link Map#get(Object)}.
|
* values extracted using {@link Map#get(Object)}.
|
||||||
*
|
*
|
||||||
* <p>Extracted root attribute values must be compatible with the attribute
|
* <p>Extracted root attribute values must be compatible with the attribute
|
||||||
* return type, namely:
|
* return type, namely:
|
||||||
@@ -434,7 +433,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
|||||||
}
|
}
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
Method attribute = this.mapping.getAttributes().get(attributeIndex);
|
Method attribute = this.mapping.getAttributes().get(attributeIndex);
|
||||||
value = ReflectionUtils.invokeMethod(attribute, this.mapping.getAnnotation());
|
value = AnnotationUtils.invokeAnnotationMethod(attribute, this.mapping.getAnnotation());
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@@ -555,7 +554,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
|||||||
|
|
||||||
private ValueExtractor getValueExtractor(Object value) {
|
private ValueExtractor getValueExtractor(Object value) {
|
||||||
if (value instanceof Annotation) {
|
if (value instanceof Annotation) {
|
||||||
return ReflectionUtils::invokeMethod;
|
return AnnotationUtils::invokeAnnotationMethod;
|
||||||
}
|
}
|
||||||
if (value instanceof Map) {
|
if (value instanceof Map) {
|
||||||
return TypeMappedAnnotation::extractFromMap;
|
return TypeMappedAnnotation::extractFromMap;
|
||||||
@@ -615,7 +614,8 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
|||||||
static <A extends Annotation> MergedAnnotation<A> from(@Nullable Object source, A annotation) {
|
static <A extends Annotation> MergedAnnotation<A> from(@Nullable Object source, A annotation) {
|
||||||
Assert.notNull(annotation, "Annotation must not be null");
|
Assert.notNull(annotation, "Annotation must not be null");
|
||||||
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(annotation.annotationType());
|
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(annotation.annotationType());
|
||||||
return new TypeMappedAnnotation<>(mappings.get(0), null, source, annotation, ReflectionUtils::invokeMethod, 0);
|
return new TypeMappedAnnotation<>(
|
||||||
|
mappings.get(0), null, source, annotation, AnnotationUtils::invokeAnnotationMethod, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static <A extends Annotation> MergedAnnotation<A> of(
|
static <A extends Annotation> MergedAnnotation<A> of(
|
||||||
@@ -649,7 +649,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
|||||||
int aggregateIndex, IntrospectionFailureLogger logger) {
|
int aggregateIndex, IntrospectionFailureLogger logger) {
|
||||||
|
|
||||||
return createIfPossible(mapping, source, annotation,
|
return createIfPossible(mapping, source, annotation,
|
||||||
ReflectionUtils::invokeMethod, aggregateIndex, logger);
|
AnnotationUtils::invokeAnnotationMethod, aggregateIndex, logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|||||||
Reference in New Issue
Block a user