From 81b4020fc6392025e5e55b2d26b7cd5bac1ca7fe Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Tue, 26 Aug 2025 21:58:15 +0200 Subject: [PATCH] Do not load concrete types in annotation metadata This change fixes a regression introduced in the previous commit. Closes gh-35252 --- spring-core/spring-core.gradle | 1 + .../ClassFileAnnotationMetadata.java | 13 +++----- .../DefaultAnnotationMetadataTests.java | 29 +++++++++++++++++- .../SimpleAnnotationMetadataTests.java | 30 ++++++++++++++++++- 4 files changed, 62 insertions(+), 11 deletions(-) diff --git a/spring-core/spring-core.gradle b/spring-core/spring-core.gradle index 24649ec50de..a6445f5438c 100644 --- a/spring-core/spring-core.gradle +++ b/spring-core/spring-core.gradle @@ -88,6 +88,7 @@ dependencies { optional("org.jetbrains.kotlin:kotlin-stdlib") optional("org.jetbrains.kotlinx:kotlinx-coroutines-core") optional("org.jetbrains.kotlinx:kotlinx-coroutines-reactor") + testCompileOnly("com.github.ben-manes.caffeine:caffeine") testFixturesImplementation("io.projectreactor:reactor-test") testFixturesImplementation("org.assertj:assertj-core") testFixturesImplementation("org.junit.jupiter:junit-jupiter") diff --git a/spring-core/src/main/java24/org/springframework/core/type/classreading/ClassFileAnnotationMetadata.java b/spring-core/src/main/java24/org/springframework/core/type/classreading/ClassFileAnnotationMetadata.java index 5cfc0ed49b5..20cd3a8591d 100644 --- a/spring-core/src/main/java24/org/springframework/core/type/classreading/ClassFileAnnotationMetadata.java +++ b/spring-core/src/main/java24/org/springframework/core/type/classreading/ClassFileAnnotationMetadata.java @@ -87,7 +87,7 @@ abstract class ClassFileAnnotationMetadata { return createMergedAnnotation(className, annotationValue.annotation(), classLoader); } case AnnotationValue.OfClass classValue -> { - return loadClass(classValue.className().stringValue(), classLoader); + return fromTypeDescriptor(classValue.className().stringValue()); } case AnnotationValue.OfEnum enumValue -> { return parseEnum(enumValue, classLoader); @@ -105,13 +105,8 @@ abstract class ClassFileAnnotationMetadata { } private static Class loadClass(String className, @Nullable ClassLoader classLoader) { - try { - String name = fromTypeDescriptor(className); - return ClassUtils.forName(name, classLoader); - } - catch (ClassNotFoundException ex) { - return Object.class; - } + String name = fromTypeDescriptor(className); + return ClassUtils.resolveClassName(name, classLoader); } private static Object parseArrayValue(String className, @Nullable ClassLoader classLoader, AnnotationValue.OfArray arrayValue) { @@ -160,7 +155,7 @@ abstract class ClassFileAnnotationMetadata { return MergedAnnotation.class; } case AnnotationValue.OfClass _ -> { - return Class.class; + return String.class; } case AnnotationValue.OfEnum enumValue -> { return loadClass(enumValue.className().stringValue(), classLoader); diff --git a/spring-core/src/test/java/org/springframework/core/type/classreading/DefaultAnnotationMetadataTests.java b/spring-core/src/test/java/org/springframework/core/type/classreading/DefaultAnnotationMetadataTests.java index 301c8e27460..a296ecf41e8 100644 --- a/spring-core/src/test/java/org/springframework/core/type/classreading/DefaultAnnotationMetadataTests.java +++ b/spring-core/src/test/java/org/springframework/core/type/classreading/DefaultAnnotationMetadataTests.java @@ -16,15 +16,26 @@ package org.springframework.core.type.classreading; +import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import com.github.benmanes.caffeine.cache.Caffeine; +import org.junit.jupiter.api.Test; + import org.springframework.core.type.AbstractAnnotationMetadataTests; import org.springframework.core.type.AnnotationMetadata; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; + /** * Tests for {@link SimpleAnnotationMetadata} and * {@link SimpleAnnotationMetadataReadingVisitor} on Java < 24, * and for the ClassFile API variant on Java >= 24. * * @author Phillip Webb + * @author Brian Clozel */ class DefaultAnnotationMetadataTests extends AbstractAnnotationMetadataTests { @@ -34,9 +45,25 @@ class DefaultAnnotationMetadataTests extends AbstractAnnotationMetadataTests { return MetadataReaderFactory.create(source.getClassLoader()) .getMetadataReader(source.getName()).getAnnotationMetadata(); } - catch (Exception ex) { + catch (IOException ex) { throw new IllegalStateException(ex); } } + @Test + void getClassAttributeWhenUnknownClass() { + var annotation = get(WithClassMissingFromClasspath.class).getAnnotations().get(ClassAttributes.class); + assertThat(annotation.getStringArray("types")).contains("com.github.benmanes.caffeine.cache.Caffeine"); + assertThatIllegalArgumentException().isThrownBy(() -> annotation.getClassArray("types")); + } + + @ClassAttributes(types = {Caffeine.class}) + public static class WithClassMissingFromClasspath { + } + + @Retention(RetentionPolicy.RUNTIME) + public @interface ClassAttributes { + Class[] types(); + } + } diff --git a/spring-core/src/test/java/org/springframework/core/type/classreading/SimpleAnnotationMetadataTests.java b/spring-core/src/test/java/org/springframework/core/type/classreading/SimpleAnnotationMetadataTests.java index 868efdb4bd5..562754328d5 100644 --- a/spring-core/src/test/java/org/springframework/core/type/classreading/SimpleAnnotationMetadataTests.java +++ b/spring-core/src/test/java/org/springframework/core/type/classreading/SimpleAnnotationMetadataTests.java @@ -16,14 +16,25 @@ package org.springframework.core.type.classreading; +import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import com.github.benmanes.caffeine.cache.Caffeine; +import org.junit.jupiter.api.Test; + import org.springframework.core.type.AbstractAnnotationMetadataTests; import org.springframework.core.type.AnnotationMetadata; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; + /** * Tests for {@link SimpleAnnotationMetadata} and * {@link SimpleAnnotationMetadataReadingVisitor}. * * @author Phillip Webb + * @author Brian Clozel */ class SimpleAnnotationMetadataTests extends AbstractAnnotationMetadataTests { @@ -34,9 +45,26 @@ class SimpleAnnotationMetadataTests extends AbstractAnnotationMetadataTests { source.getClassLoader()).getMetadataReader( source.getName()).getAnnotationMetadata(); } - catch (Exception ex) { + catch (IOException ex) { throw new IllegalStateException(ex); } } + @Test + void getClassAttributeWhenUnknownClass() { + var annotation = get(WithClassMissingFromClasspath.class).getAnnotations().get(ClassAttributes.class); + assertThat(annotation.getStringArray("types")).contains("com.github.benmanes.caffeine.cache.Caffeine"); + assertThatIllegalArgumentException().isThrownBy(() -> annotation.getClassArray("types")); + } + + @ClassAttributes(types = {Caffeine.class}) + public static class WithClassMissingFromClasspath { + } + + + @Retention(RetentionPolicy.RUNTIME) + public @interface ClassAttributes { + Class[] types(); + } + }