Skip to content

Nested annotations no longer supported in ASM-based annotation processing #24375

Closed
@AndreasKl

Description

@AndreasKl

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.

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)type: regressionA bug that is also a regression

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions