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 19ef319fd48..3eedc57b9b2 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 @@ -139,7 +139,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 baf73716e4d..d312a3f9569 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 @@ -31,7 +31,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; @@ -91,6 +96,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); @@ -171,6 +184,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); @@ -230,6 +248,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 { } @@ -376,4 +410,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); + } + } + }