Skip to content

Commit 716916b

Browse files
committed
AnnotationUtils favors local composed annotations over interface annotations and consistently logs introspection failures via lazily initialized logger
Issue: SPR-12355 Issue: SPR-12325 Issue: SPR-12329
1 parent 77a62ec commit 716916b

File tree

2 files changed

+60
-62
lines changed

2 files changed

+60
-62
lines changed

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

+50-51
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,15 @@ public abstract class AnnotationUtils {
6666
/** The attribute name for annotations with a single element */
6767
public static final String VALUE = "value";
6868

69-
private static final Log logger = LogFactory.getLog(AnnotationUtils.class);
7069

7170
private static final Map<AnnotationCacheKey, Annotation> findAnnotationCache =
7271
new ConcurrentReferenceHashMap<AnnotationCacheKey, Annotation>(256);
7372

7473
private static final Map<Class<?>, Boolean> annotatedInterfaceCache =
7574
new ConcurrentReferenceHashMap<Class<?>, Boolean>(256);
7675

76+
private static transient Log logger;
77+
7778

7879
/**
7980
* Get a single {@link Annotation} of {@code annotationType} from the supplied
@@ -93,10 +94,7 @@ public static <T extends Annotation> T getAnnotation(Annotation ann, Class<T> an
9394
}
9495
catch (Exception ex) {
9596
// Assuming nested Class values not resolvable within annotation attributes...
96-
// We're probably hitting a non-present optional arrangement - let's back out.
97-
if (logger.isInfoEnabled()) {
98-
logger.info("Failed to introspect annotations on [" + ann.annotationType() + "]: " + ex);
99-
}
97+
logIntrospectionFailure(ann.annotationType(), ex);
10098
return null;
10199
}
102100
}
@@ -125,10 +123,7 @@ public static <T extends Annotation> T getAnnotation(AnnotatedElement annotatedE
125123
}
126124
catch (Exception ex) {
127125
// Assuming nested Class values not resolvable within annotation attributes...
128-
// We're probably hitting a non-present optional arrangement - let's back out.
129-
if (logger.isInfoEnabled()) {
130-
logger.info("Failed to introspect annotations on [" + annotatedElement + "]: " + ex);
131-
}
126+
logIntrospectionFailure(annotatedElement, ex);
132127
return null;
133128
}
134129
}
@@ -146,10 +141,7 @@ public static Annotation[] getAnnotations(Method method) {
146141
}
147142
catch (Exception ex) {
148143
// Assuming nested Class values not resolvable within annotation attributes...
149-
// We're probably hitting a non-present optional arrangement - let's back out.
150-
if (logger.isInfoEnabled()) {
151-
logger.info("Failed to introspect annotations on [" + method + "]: " + ex);
152-
}
144+
logIntrospectionFailure(method, ex);
153145
return null;
154146
}
155147
}
@@ -208,10 +200,7 @@ public static <A extends Annotation> Set<A> getRepeatableAnnotation(AnnotatedEle
208200
}
209201
catch (Exception ex) {
210202
// Assuming nested Class values not resolvable within annotation attributes...
211-
// We're probably hitting a non-present optional arrangement - let's back out.
212-
if (logger.isInfoEnabled()) {
213-
logger.info("Failed to introspect annotations on [" + annotatedElement + "]: " + ex);
214-
}
203+
logIntrospectionFailure(annotatedElement, ex);
215204
}
216205
return Collections.emptySet();
217206
}
@@ -293,10 +282,7 @@ private static boolean isInterfaceWithAnnotatedMethods(Class<?> iface) {
293282
}
294283
catch (Exception ex) {
295284
// Assuming nested Class values not resolvable within annotation attributes...
296-
// We're probably hitting a non-present optional arrangement - let's back out.
297-
if (logger.isInfoEnabled()) {
298-
logger.info("Failed to introspect annotations on [" + ifcMethod + "]: " + ex);
299-
}
285+
logIntrospectionFailure(ifcMethod, ex);
300286
}
301287
}
302288
annotatedInterfaceCache.put(iface, found);
@@ -347,35 +333,39 @@ public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> a
347333
* @param visited the set of annotations that have already been visited
348334
* @return the annotation if found, or {@code null} if not found
349335
*/
336+
@SuppressWarnings("unchecked")
350337
private static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType, Set<Annotation> visited) {
351338
Assert.notNull(clazz, "Class must not be null");
352-
if (isAnnotationDeclaredLocally(annotationType, clazz)) {
353-
try {
354-
return clazz.getAnnotation(annotationType);
339+
340+
try {
341+
Annotation[] anns = clazz.getDeclaredAnnotations();
342+
for (Annotation ann : anns) {
343+
if (ann.annotationType().equals(annotationType)) {
344+
return (A) ann;
345+
}
355346
}
356-
catch (Exception ex) {
357-
// Assuming nested Class values not resolvable within annotation attributes...
358-
// We're probably hitting a non-present optional arrangement - let's back out.
359-
if (logger.isInfoEnabled()) {
360-
logger.info("Failed to introspect annotations on [" + clazz + "]: " + ex);
347+
for (Annotation ann : anns) {
348+
if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
349+
A annotation = findAnnotation(ann.annotationType(), annotationType, visited);
350+
if (annotation != null) {
351+
return annotation;
352+
}
361353
}
362-
return null;
363354
}
364355
}
356+
catch (Exception ex) {
357+
// Assuming nested Class values not resolvable within annotation attributes...
358+
// We're probably hitting a non-present optional arrangement - let's back out.
359+
return null;
360+
}
361+
365362
for (Class<?> ifc : clazz.getInterfaces()) {
366363
A annotation = findAnnotation(ifc, annotationType, visited);
367364
if (annotation != null) {
368365
return annotation;
369366
}
370367
}
371-
for (Annotation ann : clazz.getDeclaredAnnotations()) {
372-
if (!isInJavaLangAnnotationPackage(ann) && visited.add(ann)) {
373-
A annotation = findAnnotation(ann.annotationType(), annotationType, visited);
374-
if (annotation != null) {
375-
return annotation;
376-
}
377-
}
378-
}
368+
379369
Class<?> superclass = clazz.getSuperclass();
380370
if (superclass == null || superclass.equals(Object.class)) {
381371
return null;
@@ -473,19 +463,16 @@ public static boolean isAnnotationDeclaredLocally(Class<? extends Annotation> an
473463
Assert.notNull(clazz, "Class must not be null");
474464
boolean declaredLocally = false;
475465
try {
476-
for (Annotation annotation : clazz.getDeclaredAnnotations()) {
477-
if (annotation.annotationType().equals(annotationType)) {
466+
for (Annotation ann : clazz.getDeclaredAnnotations()) {
467+
if (ann.annotationType().equals(annotationType)) {
478468
declaredLocally = true;
479469
break;
480470
}
481471
}
482472
}
483473
catch (Exception ex) {
484474
// Assuming nested Class values not resolvable within annotation attributes...
485-
// We're probably hitting a non-present optional arrangement - let's back out.
486-
if (logger.isInfoEnabled()) {
487-
logger.info("Failed to introspect annotations on [" + clazz + "]: " + ex);
488-
}
475+
logIntrospectionFailure(clazz, ex);
489476
}
490477
return declaredLocally;
491478
}
@@ -710,6 +697,18 @@ public static Object getDefaultValue(Class<? extends Annotation> annotationType,
710697
}
711698

712699

700+
private static void logIntrospectionFailure(AnnotatedElement annotatedElement, Exception ex) {
701+
Log loggerToUse = logger;
702+
if (loggerToUse == null) {
703+
loggerToUse = LogFactory.getLog(AnnotationUtils.class);
704+
logger = loggerToUse;
705+
}
706+
if (loggerToUse.isInfoEnabled()) {
707+
loggerToUse.info("Failed to introspect annotations on [" + annotatedElement + "]: " + ex);
708+
}
709+
}
710+
711+
713712
/**
714713
* Cache key for the AnnotatedElement cache.
715714
*/
@@ -767,15 +766,15 @@ public Set<A> getResult(AnnotatedElement element) {
767766
@SuppressWarnings("unchecked")
768767
private void process(AnnotatedElement annotatedElement) {
769768
if (this.visited.add(annotatedElement)) {
770-
for (Annotation annotation : annotatedElement.getAnnotations()) {
771-
if (ObjectUtils.nullSafeEquals(this.annotationType, annotation.annotationType())) {
772-
this.result.add((A) annotation);
769+
for (Annotation ann : annotatedElement.getAnnotations()) {
770+
if (ObjectUtils.nullSafeEquals(this.annotationType, ann.annotationType())) {
771+
this.result.add((A) ann);
773772
}
774-
else if (ObjectUtils.nullSafeEquals(this.containerAnnotationType, annotation.annotationType())) {
775-
this.result.addAll(getValue(annotation));
773+
else if (ObjectUtils.nullSafeEquals(this.containerAnnotationType, ann.annotationType())) {
774+
this.result.addAll(getValue(ann));
776775
}
777-
else if (!isInJavaLangAnnotationPackage(annotation)) {
778-
process(annotation.annotationType());
776+
else if (!isInJavaLangAnnotationPackage(ann)) {
777+
process(ann.annotationType());
779778
}
780779
}
781780
}

spring-core/src/test/java/org/springframework/core/annotation/AnnotationUtilsTests.java

+10-11
Original file line numberDiff line numberDiff line change
@@ -99,25 +99,24 @@ public void findMethodAnnotationOnBridgeMethod() throws Exception {
9999
// assertNotNull(o);
100100
// }
101101

102+
/**
103+
* @since 4.1.2
104+
*/
102105
@Test
103-
public void findAnnotationFavorsInterfacesOverLocalMetaAnnotations() {
106+
public void findAnnotationFavorsLocalMetaAnnotationsOverInterfaces() {
104107
Component component = AnnotationUtils.findAnnotation(
105-
ClassWithLocalMetaAnnotationAndMetaAnnotatedInterface.class, Component.class);
108+
ClassWithLocalMetaAnnotationAndMetaAnnotatedInterface.class, Component.class);
106109
assertNotNull(component);
107-
108-
// By inspecting ClassWithLocalMetaAnnotationAndMetaAnnotatedInterface, one
109-
// might expect that "meta2" should be found; however, with the current
110-
// implementation "meta1" will be found.
111-
assertEquals("meta1", component.value());
110+
assertEquals("meta2", component.value());
112111
}
113112

114113
/**
115114
* @since 4.0.3
116115
*/
117116
@Test
118117
public void findAnnotationFavorsInheritedAnnotationsOverMoreLocallyDeclaredComposedAnnotations() {
119-
Transactional transactional = AnnotationUtils.findAnnotation(SubSubClassWithInheritedAnnotation.class,
120-
Transactional.class);
118+
Transactional transactional = AnnotationUtils.findAnnotation(
119+
SubSubClassWithInheritedAnnotation.class, Transactional.class);
121120
assertNotNull(transactional);
122121
assertTrue("readOnly flag for SubSubClassWithInheritedAnnotation", transactional.readOnly());
123122
}
@@ -127,8 +126,8 @@ public void findAnnotationFavorsInheritedAnnotationsOverMoreLocallyDeclaredCompo
127126
*/
128127
@Test
129128
public void findAnnotationFavorsInheritedComposedAnnotationsOverMoreLocallyDeclaredComposedAnnotations() {
130-
Component component = AnnotationUtils.findAnnotation(SubSubClassWithInheritedMetaAnnotation.class,
131-
Component.class);
129+
Component component = AnnotationUtils.findAnnotation(
130+
SubSubClassWithInheritedMetaAnnotation.class, Component.class);
132131
assertNotNull(component);
133132
assertEquals("meta2", component.value());
134133
}

0 commit comments

Comments
 (0)