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:
parent
e5878ab15b
commit
f4b3333fa8
|
@ -32,7 +32,6 @@ import java.util.Set;
|
|||
import org.springframework.core.annotation.AnnotationTypeMapping.MirrorSets.MirrorSet;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
|
@ -241,7 +240,7 @@ final class AnnotationTypeMapping {
|
|||
mapping.claimedAliases.addAll(aliases);
|
||||
if (mapping.annotation != null) {
|
||||
int[] resolvedMirrors = mapping.mirrorSets.resolve(null,
|
||||
mapping.annotation, ReflectionUtils::invokeMethod);
|
||||
mapping.annotation, AnnotationUtils::invokeAnnotationMethod);
|
||||
for (int i = 0; i < mapping.attributes.size(); i++) {
|
||||
if (aliases.contains(mapping.attributes.get(i))) {
|
||||
this.annotationValueMappings[attributeIndex] = resolvedMirrors[i];
|
||||
|
@ -505,7 +504,7 @@ final class AnnotationTypeMapping {
|
|||
if (source == this && metaAnnotationsOnly) {
|
||||
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());
|
||||
for (int i = 0; i < attributes.size(); i++) {
|
||||
Method attribute = attributes.get(i);
|
||||
Object value1 = ReflectionUtils.invokeMethod(attribute, annotation);
|
||||
Object value1 = AnnotationUtils.invokeAnnotationMethod(attribute, annotation);
|
||||
Object value2;
|
||||
if (extractedValue instanceof TypeMappedAnnotation) {
|
||||
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.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
@ -1052,23 +1053,42 @@ public abstract class AnnotationUtils {
|
|||
}
|
||||
try {
|
||||
Method method = annotation.annotationType().getDeclaredMethod(attributeName);
|
||||
ReflectionUtils.makeAccessible(method);
|
||||
return method.invoke(annotation);
|
||||
return invokeAnnotationMethod(method, annotation);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
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) {
|
||||
rethrowAnnotationConfigurationException(ex);
|
||||
handleIntrospectionFailure(annotation.getClass(), ex);
|
||||
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},
|
||||
* it will be cast to an {@code AnnotationConfigurationException} and thrown,
|
||||
|
|
|
@ -109,7 +109,7 @@ final class AttributeMethods {
|
|||
for (int i = 0; i < size(); i++) {
|
||||
if (canThrowTypeNotPresentException(i)) {
|
||||
try {
|
||||
get(i).invoke(annotation);
|
||||
AnnotationUtils.invokeAnnotationMethod(get(i), annotation);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
return false;
|
||||
|
@ -134,7 +134,7 @@ final class AttributeMethods {
|
|||
for (int i = 0; i < size(); i++) {
|
||||
if (canThrowTypeNotPresentException(i)) {
|
||||
try {
|
||||
get(i).invoke(annotation);
|
||||
AnnotationUtils.invokeAnnotationMethod(get(i), annotation);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
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.Repeatable;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Strategy used to determine annotations that act as containers for other
|
||||
|
@ -133,19 +130,6 @@ public abstract class RepeatableContainers {
|
|||
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
|
||||
|
@ -168,7 +152,7 @@ public abstract class RepeatableContainers {
|
|||
Annotation[] findRepeatedAnnotations(Annotation annotation) {
|
||||
Method method = getRepeatedAnnotationsMethod(annotation.annotationType());
|
||||
if (method != null) {
|
||||
return (Annotation[]) invokeAnnotationMethod(annotation, method);
|
||||
return (Annotation[]) AnnotationUtils.invokeAnnotationMethod(method, annotation);
|
||||
}
|
||||
return super.findRepeatedAnnotations(annotation);
|
||||
}
|
||||
|
@ -255,7 +239,7 @@ public abstract class RepeatableContainers {
|
|||
@Nullable
|
||||
Annotation[] findRepeatedAnnotations(Annotation annotation) {
|
||||
if (this.container.isAssignableFrom(annotation.annotationType())) {
|
||||
return (Annotation[]) invokeAnnotationMethod(annotation, this.valueMethod);
|
||||
return (Annotation[]) AnnotationUtils.invokeAnnotationMethod(this.valueMethod, 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++) {
|
||||
Method attribute = this.attributes.get(i);
|
||||
Object thisValue = getAttributeValue(attribute);
|
||||
Object otherValue = ReflectionUtils.invokeMethod(attribute, other);
|
||||
Object otherValue = AnnotationUtils.invokeAnnotationMethod(attribute, other);
|
||||
if (!ObjectUtils.nullSafeEquals(thisValue, otherValue)) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,6 @@ import java.util.function.Predicate;
|
|||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* {@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
|
||||
* supported by the same class. For example, the attributes source might be an
|
||||
* actual {@link Annotation} instance where methods on the annotation instance
|
||||
* are {@linkplain ReflectionUtils#invokeMethod(Method, Object) invoked} to extract
|
||||
* values. Equally, the source could be a simple {@link Map} with values
|
||||
* extracted using {@link Map#get(Object)}.
|
||||
* are {@linkplain AnnotationUtils#invokeAnnotationMethod(Method, Object) invoked}
|
||||
* to extract values. Similarly, the source could be a simple {@link Map} with
|
||||
* values extracted using {@link Map#get(Object)}.
|
||||
*
|
||||
* <p>Extracted root attribute values must be compatible with the attribute
|
||||
* return type, namely:
|
||||
|
@ -434,7 +433,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
|||
}
|
||||
if (value == null) {
|
||||
Method attribute = this.mapping.getAttributes().get(attributeIndex);
|
||||
value = ReflectionUtils.invokeMethod(attribute, this.mapping.getAnnotation());
|
||||
value = AnnotationUtils.invokeAnnotationMethod(attribute, this.mapping.getAnnotation());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
@ -555,7 +554,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
|||
|
||||
private ValueExtractor getValueExtractor(Object value) {
|
||||
if (value instanceof Annotation) {
|
||||
return ReflectionUtils::invokeMethod;
|
||||
return AnnotationUtils::invokeAnnotationMethod;
|
||||
}
|
||||
if (value instanceof Map) {
|
||||
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) {
|
||||
Assert.notNull(annotation, "Annotation must not be null");
|
||||
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(
|
||||
|
@ -649,7 +649,7 @@ final class TypeMappedAnnotation<A extends Annotation> extends AbstractMergedAnn
|
|||
int aggregateIndex, IntrospectionFailureLogger logger) {
|
||||
|
||||
return createIfPossible(mapping, source, annotation,
|
||||
ReflectionUtils::invokeMethod, aggregateIndex, logger);
|
||||
AnnotationUtils::invokeAnnotationMethod, aggregateIndex, logger);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
|
Loading…
Reference in New Issue