diff --git a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java index 669d7f2c09a..514e9bd01bf 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/AnnotationBeanNameGenerator.java @@ -143,7 +143,17 @@ public class AnnotationBeanNameGenerator implements BeanNameGenerator { Set visited = new HashSet<>(); for (MergedAnnotation mergedAnnotation : mergedAnnotations) { - AnnotationAttributes attributes = mergedAnnotation.asAnnotationAttributes(ADAPTATIONS); + AnnotationAttributes attributes = null; + try { + attributes = mergedAnnotation.asAnnotationAttributes(ADAPTATIONS); + } + catch (Throwable ex) { + // Ignore exception and current MergedAnnotation, assuming that values of the + // MergedAnnotation could not be adapted to a Map/AnnotationAttributes due to + // missing types referenced via annotation attributes. + continue; + } + if (visited.add(attributes)) { String annotationType = mergedAnnotation.getType().getName(); Set metaAnnotationTypes = this.metaAnnotationTypesCache.computeIfAbsent(annotationType, diff --git a/spring-context/src/test/java/org/springframework/context/annotation/AnnotationBeanNameGeneratorTests.java b/spring-context/src/test/java/org/springframework/context/annotation/AnnotationBeanNameGeneratorTests.java index b6c37972fdb..913111309fd 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/AnnotationBeanNameGeneratorTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/AnnotationBeanNameGeneratorTests.java @@ -34,7 +34,12 @@ import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefiniti import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.SimpleBeanDefinitionRegistry; +import org.springframework.core.OverridingClassLoader; import org.springframework.core.annotation.AliasFor; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.core.type.classreading.SimpleMetadataReaderFactory; import org.springframework.stereotype.Component; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Service; @@ -94,6 +99,14 @@ class AnnotationBeanNameGeneratorTests { "myComponent", "myService"); } + @Test // gh-gh-36524 + void generateBeanNameForConventionBasedComponentWithMissingAnnotationAttributeTypeViaAsm() throws Exception { + FilteringClassLoader classLoader = new FilteringClassLoader(getClass().getClassLoader()); + MetadataReaderFactory readerFactory = new SimpleMetadataReaderFactory(classLoader); + MetadataReader reader = readerFactory.getMetadataReader(ConventionBasedComponentWithMissingAnnotationAttributeType.class.getName()); + assertGeneratedName(reader.getAnnotationMetadata(), "myComponent"); + } + @Test void generateBeanNameForComponentWithConflictingNames() { BeanDefinition bd = annotatedBeanDef(ComponentWithMultipleConflictingNames.class); @@ -193,6 +206,11 @@ class AnnotationBeanNameGeneratorTests { assertThat(generateBeanName(bd)).isNotBlank().isEqualTo(expectedName); } + private void assertGeneratedName(AnnotationMetadata annotationMetadata, String expectedName) { + BeanDefinition bd = new AnnotatedGenericBeanDefinition(annotationMetadata); + assertThat(generateBeanName(bd)).isNotBlank().isEqualTo(expectedName); + } + private void assertGeneratedNameIsDefault(Class clazz) { BeanDefinition bd = annotatedBeanDef(clazz); String expectedName = this.beanNameGenerator.buildDefaultBeanName(bd); @@ -252,6 +270,22 @@ class AnnotationBeanNameGeneratorTests { static class ConventionBasedComponentWithMultipleConflictingNames { } + static class FilteredType { + } + + @Retention(RetentionPolicy.RUNTIME) + @interface ExampleAnnotation { + + Class value() default Void.class; + + String description() default ""; + } + + @ExampleAnnotation(value = FilteredType.class, description = "optional") + @ConventionBasedComponent1("myComponent") + static class ConventionBasedComponentWithMissingAnnotationAttributeType { + } + @Component private static class AnonymousComponent { } @@ -398,4 +432,24 @@ class AnnotationBeanNameGeneratorTests { static class StereotypeWithGeneratedName { } + static class FilteringClassLoader extends OverridingClassLoader { + + FilteringClassLoader(ClassLoader parent) { + super(parent); + } + + @Override + protected boolean isEligibleForOverriding(String className) { + return className.startsWith(AnnotationBeanNameGeneratorTests.class.getName()); + } + + @Override + protected Class loadClassForOverriding(String name) throws ClassNotFoundException { + if (name.contains("Filtered")) { + throw new ClassNotFoundException(name); + } + return super.loadClassForOverriding(name); + } + } + }