Skip to content

Commit f4b3333

Browse files
committed
Avoid reflection for annotation attribute method invocations
As a follow up to 332b25b, this commit consistently avoids the use of reflection for annotation attribute method invocations. See gh-29301 Closes gh-29448
1 parent e5878ab commit f4b3333

File tree

6 files changed

+44
-41
lines changed

6 files changed

+44
-41
lines changed

spring-core/src/main/java/org/springframework/core/annotation/AnnotationTypeMapping.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import org.springframework.core.annotation.AnnotationTypeMapping.MirrorSets.MirrorSet;
3333
import org.springframework.lang.Nullable;
3434
import org.springframework.util.ObjectUtils;
35-
import org.springframework.util.ReflectionUtils;
3635
import org.springframework.util.StringUtils;
3736

3837
/**
@@ -241,7 +240,7 @@ private void processAliases(int attributeIndex, List<Method> aliases) {
241240
mapping.claimedAliases.addAll(aliases);
242241
if (mapping.annotation != null) {
243242
int[] resolvedMirrors = mapping.mirrorSets.resolve(null,
244-
mapping.annotation, ReflectionUtils::invokeMethod);
243+
mapping.annotation, AnnotationUtils::invokeAnnotationMethod);
245244
for (int i = 0; i < mapping.attributes.size(); i++) {
246245
if (aliases.contains(mapping.attributes.get(i))) {
247246
this.annotationValueMappings[attributeIndex] = resolvedMirrors[i];
@@ -505,7 +504,7 @@ Object getMappedAnnotationValue(int attributeIndex, boolean metaAnnotationsOnly)
505504
if (source == this && metaAnnotationsOnly) {
506505
return null;
507506
}
508-
return ReflectionUtils.invokeMethod(source.attributes.get(mappedIndex), source.annotation);
507+
return AnnotationUtils.invokeAnnotationMethod(source.attributes.get(mappedIndex), source.annotation);
509508
}
510509

511510
/**
@@ -594,7 +593,7 @@ private static boolean areEquivalent(Annotation annotation, @Nullable Object ext
594593
AttributeMethods attributes = AttributeMethods.forAnnotationType(annotation.annotationType());
595594
for (int i = 0; i < attributes.size(); i++) {
596595
Method attribute = attributes.get(i);
597-
Object value1 = ReflectionUtils.invokeMethod(attribute, annotation);
596+
Object value1 = AnnotationUtils.invokeAnnotationMethod(attribute, annotation);
598597
Object value2;
599598
if (extractedValue instanceof TypeMappedAnnotation) {
600599
value2 = ((TypeMappedAnnotation<?>) extractedValue).getValue(attribute.getName()).orElse(null);

spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@
1919
import java.lang.annotation.Annotation;
2020
import java.lang.reflect.AnnotatedElement;
2121
import java.lang.reflect.Array;
22-
import java.lang.reflect.InvocationTargetException;
22+
import java.lang.reflect.InvocationHandler;
2323
import java.lang.reflect.Method;
2424
import java.lang.reflect.Modifier;
25+
import java.lang.reflect.Proxy;
2526
import java.util.Collection;
2627
import java.util.Collections;
2728
import java.util.List;
@@ -1052,23 +1053,42 @@ public static Object getValue(@Nullable Annotation annotation, @Nullable String
10521053
}
10531054
try {
10541055
Method method = annotation.annotationType().getDeclaredMethod(attributeName);
1055-
ReflectionUtils.makeAccessible(method);
1056-
return method.invoke(annotation);
1056+
return invokeAnnotationMethod(method, annotation);
10571057
}
10581058
catch (NoSuchMethodException ex) {
10591059
return null;
10601060
}
1061-
catch (InvocationTargetException ex) {
1062-
rethrowAnnotationConfigurationException(ex.getTargetException());
1063-
throw new IllegalStateException("Could not obtain value for annotation attribute '" +
1064-
attributeName + "' in " + annotation, ex);
1065-
}
10661061
catch (Throwable ex) {
1062+
rethrowAnnotationConfigurationException(ex);
10671063
handleIntrospectionFailure(annotation.getClass(), ex);
10681064
return null;
10691065
}
10701066
}
10711067

1068+
/**
1069+
* Invoke the supplied annotation attribute {@link Method} on the supplied
1070+
* {@link Annotation}.
1071+
* <p>An attempt will first be made to invoke the method via the annotation's
1072+
* {@link InvocationHandler} (if the annotation instance is a JDK dynamic proxy).
1073+
* If that fails, an attempt will be made to invoke the method via reflection.
1074+
* @param method the method to invoke
1075+
* @param annotation the annotation on which to invoke the method
1076+
* @return the value returned from the method invocation
1077+
* @since 5.3.24
1078+
*/
1079+
static Object invokeAnnotationMethod(Method method, Object annotation) {
1080+
if (Proxy.isProxyClass(annotation.getClass())) {
1081+
try {
1082+
InvocationHandler handler = Proxy.getInvocationHandler(annotation);
1083+
return handler.invoke(annotation, method, null);
1084+
}
1085+
catch (Throwable ex) {
1086+
// ignore and fall back to reflection below
1087+
}
1088+
}
1089+
return ReflectionUtils.invokeMethod(method, annotation);
1090+
}
1091+
10721092
/**
10731093
* If the supplied throwable is an {@link AnnotationConfigurationException},
10741094
* it will be cast to an {@code AnnotationConfigurationException} and thrown,

spring-core/src/main/java/org/springframework/core/annotation/AttributeMethods.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ boolean isValid(Annotation annotation) {
109109
for (int i = 0; i < size(); i++) {
110110
if (canThrowTypeNotPresentException(i)) {
111111
try {
112-
get(i).invoke(annotation);
112+
AnnotationUtils.invokeAnnotationMethod(get(i), annotation);
113113
}
114114
catch (Throwable ex) {
115115
return false;
@@ -134,7 +134,7 @@ void validate(Annotation annotation) {
134134
for (int i = 0; i < size(); i++) {
135135
if (canThrowTypeNotPresentException(i)) {
136136
try {
137-
get(i).invoke(annotation);
137+
AnnotationUtils.invokeAnnotationMethod(get(i), annotation);
138138
}
139139
catch (Throwable ex) {
140140
throw new IllegalStateException("Could not obtain annotation attribute value for " +

spring-core/src/main/java/org/springframework/core/annotation/RepeatableContainers.java

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,13 @@
1818

1919
import java.lang.annotation.Annotation;
2020
import java.lang.annotation.Repeatable;
21-
import java.lang.reflect.InvocationHandler;
2221
import java.lang.reflect.Method;
23-
import java.lang.reflect.Proxy;
2422
import java.util.Map;
2523

2624
import org.springframework.lang.Nullable;
2725
import org.springframework.util.Assert;
2826
import org.springframework.util.ConcurrentReferenceHashMap;
2927
import org.springframework.util.ObjectUtils;
30-
import org.springframework.util.ReflectionUtils;
3128

3229
/**
3330
* Strategy used to determine annotations that act as containers for other
@@ -133,19 +130,6 @@ public static RepeatableContainers none() {
133130
return NoRepeatableContainers.INSTANCE;
134131
}
135132

136-
private static Object invokeAnnotationMethod(Annotation annotation, Method method) {
137-
if (Proxy.isProxyClass(annotation.getClass())) {
138-
try {
139-
InvocationHandler handler = Proxy.getInvocationHandler(annotation);
140-
return handler.invoke(annotation, method, null);
141-
}
142-
catch (Throwable ex) {
143-
// ignore and fall back to reflection below
144-
}
145-
}
146-
return ReflectionUtils.invokeMethod(method, annotation);
147-
}
148-
149133

150134
/**
151135
* Standard {@link RepeatableContainers} implementation that searches using
@@ -168,7 +152,7 @@ private static class StandardRepeatableContainers extends RepeatableContainers {
168152
Annotation[] findRepeatedAnnotations(Annotation annotation) {
169153
Method method = getRepeatedAnnotationsMethod(annotation.annotationType());
170154
if (method != null) {
171-
return (Annotation[]) invokeAnnotationMethod(annotation, method);
155+
return (Annotation[]) AnnotationUtils.invokeAnnotationMethod(method, annotation);
172156
}
173157
return super.findRepeatedAnnotations(annotation);
174158
}
@@ -255,7 +239,7 @@ private Class<? extends Annotation> deduceContainer(Class<? extends Annotation>
255239
@Nullable
256240
Annotation[] findRepeatedAnnotations(Annotation annotation) {
257241
if (this.container.isAssignableFrom(annotation.annotationType())) {
258-
return (Annotation[]) invokeAnnotationMethod(annotation, this.valueMethod);
242+
return (Annotation[]) AnnotationUtils.invokeAnnotationMethod(this.valueMethod, annotation);
259243
}
260244
return super.findRepeatedAnnotations(annotation);
261245
}

spring-core/src/main/java/org/springframework/core/annotation/SynthesizedMergedAnnotationInvocationHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ private boolean annotationEquals(Object other) {
111111
for (int i = 0; i < this.attributes.size(); i++) {
112112
Method attribute = this.attributes.get(i);
113113
Object thisValue = getAttributeValue(attribute);
114-
Object otherValue = ReflectionUtils.invokeMethod(attribute, other);
114+
Object otherValue = AnnotationUtils.invokeAnnotationMethod(attribute, other);
115115
if (!ObjectUtils.nullSafeEquals(thisValue, otherValue)) {
116116
return false;
117117
}

spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import org.springframework.lang.Nullable;
3434
import org.springframework.util.Assert;
3535
import org.springframework.util.ClassUtils;
36-
import org.springframework.util.ReflectionUtils;
3736

3837
/**
3938
* {@link MergedAnnotation} that adapts attributes from a root annotation by
@@ -43,9 +42,9 @@
4342
* {@code BiFunction}. This allows various different annotation models to be
4443
* supported by the same class. For example, the attributes source might be an
4544
* actual {@link Annotation} instance where methods on the annotation instance
46-
* are {@linkplain ReflectionUtils#invokeMethod(Method, Object) invoked} to extract
47-
* values. Equally, the source could be a simple {@link Map} with values
48-
* extracted using {@link Map#get(Object)}.
45+
* are {@linkplain AnnotationUtils#invokeAnnotationMethod(Method, Object) invoked}
46+
* to extract values. Similarly, the source could be a simple {@link Map} with
47+
* values extracted using {@link Map#get(Object)}.
4948
*
5049
* <p>Extracted root attribute values must be compatible with the attribute
5150
* return type, namely:
@@ -434,7 +433,7 @@ private Object getValueFromMetaAnnotation(int attributeIndex, boolean forMirrorR
434433
}
435434
if (value == null) {
436435
Method attribute = this.mapping.getAttributes().get(attributeIndex);
437-
value = ReflectionUtils.invokeMethod(attribute, this.mapping.getAnnotation());
436+
value = AnnotationUtils.invokeAnnotationMethod(attribute, this.mapping.getAnnotation());
438437
}
439438
return value;
440439
}
@@ -555,7 +554,7 @@ private MergedAnnotation<?> adaptToMergedAnnotation(Object value, Class<? extend
555554

556555
private ValueExtractor getValueExtractor(Object value) {
557556
if (value instanceof Annotation) {
558-
return ReflectionUtils::invokeMethod;
557+
return AnnotationUtils::invokeAnnotationMethod;
559558
}
560559
if (value instanceof Map) {
561560
return TypeMappedAnnotation::extractFromMap;
@@ -615,7 +614,8 @@ private ClassLoader getClassLoader() {
615614
static <A extends Annotation> MergedAnnotation<A> from(@Nullable Object source, A annotation) {
616615
Assert.notNull(annotation, "Annotation must not be null");
617616
AnnotationTypeMappings mappings = AnnotationTypeMappings.forAnnotationType(annotation.annotationType());
618-
return new TypeMappedAnnotation<>(mappings.get(0), null, source, annotation, ReflectionUtils::invokeMethod, 0);
617+
return new TypeMappedAnnotation<>(
618+
mappings.get(0), null, source, annotation, AnnotationUtils::invokeAnnotationMethod, 0);
619619
}
620620

621621
static <A extends Annotation> MergedAnnotation<A> of(
@@ -649,7 +649,7 @@ static <A extends Annotation> TypeMappedAnnotation<A> createIfPossible(
649649
int aggregateIndex, IntrospectionFailureLogger logger) {
650650

651651
return createIfPossible(mapping, source, annotation,
652-
ReflectionUtils::invokeMethod, aggregateIndex, logger);
652+
AnnotationUtils::invokeAnnotationMethod, aggregateIndex, logger);
653653
}
654654

655655
@Nullable

0 commit comments

Comments
 (0)