Closed
Description
tl;dr
Build https://github.com/AndreasKl/spring-issue-in-TypeMappedAnnotation the test fails with the following exception:
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to read candidate component class: file [.../demo/DemoApplicationTests.class]; nested exception is java.lang.ClassCastException: class org.springframework.core.annotation.TypeMappedAnnotation cannot be cast to class java.util.Map (org.springframework.core.annotation.TypeMappedAnnotation is in unnamed module of loader 'app'; java.util.Map is in module java.base of loader 'bootstrap')
This is caused by the @AliasFor("value")
and @AliasFor("hallo")
on public @interface Bad
.
We do not see this behaviour with Spring Boot 2.1.11 (Spring Core 5.1.12.RELEASE) but can reproduce the issue on (Spring Core 5.2.2.RELEASE and 5.2.4.BUILD-SNAPSHOT).
Failing test case:
public class Gh24375Tests {
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface A {
@AliasFor("value")
B versus() default @B;
@AliasFor("versus")
B value() default @B;
}
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface B {
String name() default "";
}
@Test
@A(versus = @B)
public void gh24375() {
new ClassPathScanningCandidateComponentProvider(true)
.findCandidateComponents(Integer.class.getPackage().getName());
}
}
The root cause could be in:
org.springframework.core.annotation.AnnotationTypeMapping#areEquivalent(java.lang.Object, java.lang.Object, java.util.function.BiFunction<java.lang.reflect.Method,java.lang.Object,java.lang.Object>)
and could be mitigated by adding:
if (value instanceof Annotation && extractedValue instanceof TypeMappedAnnotation) {
return areEquivalent((Annotation) value, (TypeMappedAnnotation<?>)extractedValue, valueExtractor);
}
private static boolean areEquivalent(Annotation annotation, @Nullable TypeMappedAnnotation<?> extractedValue, BiFunction<Method, Object, Object> valueExtractor) {
AttributeMethods attributes = AttributeMethods.forAnnotationType(annotation.annotationType());
for (int i = 0; i < attributes.size(); i++) {
Method attribute = attributes.get(i);
String name = attributes.get(i).getName();
if (!areEquivalent(ReflectionUtils.invokeMethod(attribute, annotation),
extractedValue.getValue(name).orElse(null), valueExtractor)) {
return false;
}
}
return true;
}
As this is core framework code I'm not really feeling confident to provide a PR without breaking something else.