Log warning if meta-annotation is ignored due to types not present
Prior to this commit, if a meta-annotation could not be loaded because its attributes referenced types not present in the classpath, the meta-annotation was silently ignored. To improve diagnostics for such use cases, this commit introduces WARN support in IntrospectionFailureLogger and revises AttributeMethods.canLoad() to log a warning if a meta-annotation is ignored due to an exception thrown while attempting to load its attributes. For example, a warning similar to the following is now logged in such cases. WARN o.s.c.a.MergedAnnotation - Failed to introspect meta-annotation @example.MyAnnotation on class example.Config: java.lang.TypeNotPresentException: Type example.OptionalDependency not present This commit also improves log messages in AnnotationTypeMappings. Closes gh-35927
This commit is contained in:
parent
47f65b3dff
commit
62d09be2ae
|
|
@ -79,7 +79,7 @@ final class AnnotationTypeMappings {
|
|||
private void addAllMappings(Class<? extends Annotation> annotationType,
|
||||
Set<Class<? extends Annotation>> visitedAnnotationTypes) {
|
||||
Deque<AnnotationTypeMapping> queue = new ArrayDeque<>();
|
||||
addIfPossible(queue, null, annotationType, null, visitedAnnotationTypes);
|
||||
addIfPossible(queue, null, annotationType, null, false, visitedAnnotationTypes);
|
||||
while (!queue.isEmpty()) {
|
||||
AnnotationTypeMapping mapping = queue.removeFirst();
|
||||
this.mappings.add(mapping);
|
||||
|
|
@ -109,12 +109,12 @@ final class AnnotationTypeMappings {
|
|||
}
|
||||
|
||||
private void addIfPossible(Deque<AnnotationTypeMapping> queue, AnnotationTypeMapping source, Annotation ann) {
|
||||
addIfPossible(queue, source, ann.annotationType(), ann, new HashSet<>());
|
||||
addIfPossible(queue, source, ann.annotationType(), ann, true, new HashSet<>());
|
||||
}
|
||||
|
||||
private void addIfPossible(Deque<AnnotationTypeMapping> queue, @Nullable AnnotationTypeMapping source,
|
||||
Class<? extends Annotation> annotationType, @Nullable Annotation ann,
|
||||
Set<Class<? extends Annotation>> visitedAnnotationTypes) {
|
||||
boolean meta, Set<Class<? extends Annotation>> visitedAnnotationTypes) {
|
||||
|
||||
try {
|
||||
queue.addLast(new AnnotationTypeMapping(source, annotationType, ann, visitedAnnotationTypes));
|
||||
|
|
@ -122,8 +122,8 @@ final class AnnotationTypeMappings {
|
|||
catch (Exception ex) {
|
||||
AnnotationUtils.rethrowAnnotationConfigurationException(ex);
|
||||
if (failureLogger.isEnabled()) {
|
||||
failureLogger.log("Failed to introspect meta-annotation " + annotationType.getName(),
|
||||
(source != null ? source.getAnnotationType() : null), ex);
|
||||
failureLogger.log("Failed to introspect " + (meta ? "meta-annotation @" : "annotation @") +
|
||||
annotationType.getName(), (source != null ? source.getAnnotationType() : null), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -443,7 +443,7 @@ abstract class AnnotationsScanner {
|
|||
Annotation annotation = annotations[i];
|
||||
//noinspection DataFlowIssue
|
||||
if (isIgnorable(annotation.annotationType()) ||
|
||||
!AttributeMethods.forAnnotationType(annotation.annotationType()).canLoad(annotation)) {
|
||||
!AttributeMethods.forAnnotationType(annotation.annotationType()).canLoad(annotation, source)) {
|
||||
annotations[i] = null;
|
||||
}
|
||||
else {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.core.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
|
@ -38,6 +39,8 @@ import org.springframework.util.ReflectionUtils;
|
|||
*/
|
||||
final class AttributeMethods {
|
||||
|
||||
private static final IntrospectionFailureLogger failureLogger = IntrospectionFailureLogger.WARN;
|
||||
|
||||
static final AttributeMethods NONE = new AttributeMethods(null, new Method[0]);
|
||||
|
||||
static final Map<Class<? extends Annotation>, AttributeMethods> cache = new ConcurrentReferenceHashMap<>();
|
||||
|
|
@ -91,10 +94,11 @@ final class AttributeMethods {
|
|||
* exceptions for {@code Class} values (instead of the more typical early
|
||||
* {@code Class.getAnnotations() failure} on a regular JVM).
|
||||
* @param annotation the annotation to check
|
||||
* @param source the element that the supplied annotation is declared on
|
||||
* @return {@code true} if all values are present
|
||||
* @see #validate(Annotation)
|
||||
*/
|
||||
boolean canLoad(Annotation annotation) {
|
||||
boolean canLoad(Annotation annotation, AnnotatedElement source) {
|
||||
assertAnnotation(annotation);
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (canThrowTypeNotPresentException(i)) {
|
||||
|
|
@ -107,6 +111,10 @@ final class AttributeMethods {
|
|||
}
|
||||
catch (Throwable ex) {
|
||||
// TypeNotPresentException etc. -> annotation type not actually loadable.
|
||||
if (failureLogger.isEnabled()) {
|
||||
failureLogger.log("Failed to introspect meta-annotation @" +
|
||||
getName(annotation.annotationType()), source, ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,9 +24,10 @@ import org.jspecify.annotations.Nullable;
|
|||
* Log facade used to handle annotation introspection failures (in particular
|
||||
* {@code TypeNotPresentExceptions}). Allows annotation processing to continue,
|
||||
* assuming that when Class attribute values are not resolvable the annotation
|
||||
* should effectively disappear.
|
||||
* should effectively be ignored.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @since 5.2
|
||||
*/
|
||||
enum IntrospectionFailureLogger {
|
||||
|
|
@ -51,13 +52,24 @@ enum IntrospectionFailureLogger {
|
|||
public void log(String message) {
|
||||
getLogger().info(message);
|
||||
}
|
||||
},
|
||||
|
||||
WARN {
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return getLogger().isWarnEnabled();
|
||||
}
|
||||
@Override
|
||||
public void log(String message) {
|
||||
getLogger().warn(message);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private static @Nullable Log logger;
|
||||
|
||||
|
||||
void log(String message, @Nullable Object source, Exception ex) {
|
||||
void log(String message, @Nullable Object source, Throwable ex) {
|
||||
String on = (source != null ? " on " + source : "");
|
||||
log(message + on + ": " + ex);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ class AttributeMethodsTests {
|
|||
ClassValue annotation = mockAnnotation(ClassValue.class);
|
||||
given(annotation.value()).willThrow(TypeNotPresentException.class);
|
||||
AttributeMethods attributes = AttributeMethods.forAnnotationType(annotation.annotationType());
|
||||
assertThat(attributes.canLoad(annotation)).isFalse();
|
||||
assertThat(attributes.canLoad(annotation, getClass())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -121,7 +121,7 @@ class AttributeMethodsTests {
|
|||
ClassValue annotation = mock();
|
||||
given(annotation.value()).willReturn((Class) InputStream.class);
|
||||
AttributeMethods attributes = AttributeMethods.forAnnotationType(annotation.annotationType());
|
||||
assertThat(attributes.canLoad(annotation)).isTrue();
|
||||
assertThat(attributes.canLoad(annotation, getClass())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
Loading…
Reference in New Issue