diff --git a/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java b/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java index b7b7bdf1ddc..e7937c815cd 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/TypeMappedAnnotation.java @@ -450,7 +450,12 @@ final class TypeMappedAnnotation extends AbstractMergedAnn value = clazz.getName(); } else if (value instanceof String str && type == Class.class) { - value = ClassUtils.resolveClassName(str, getClassLoader()); + try { + value = ClassUtils.forName(str, getClassLoader()); + } + catch (ClassNotFoundException | LinkageError ex) { + throw new TypeNotPresentException(str, ex); + } } else if (value instanceof Class[] classes && type == String[].class) { String[] names = new String[classes.length]; @@ -461,8 +466,14 @@ final class TypeMappedAnnotation extends AbstractMergedAnn } else if (value instanceof String[] names && type == Class[].class) { Class[] classes = new Class[names.length]; + ClassLoader classLoader = getClassLoader(); for (int i = 0; i < names.length; i++) { - classes[i] = ClassUtils.resolveClassName(names[i], getClassLoader()); + try { + classes[i] = ClassUtils.forName(names[i], classLoader); + } + catch (ClassNotFoundException | LinkageError ex) { + throw new TypeNotPresentException(names[i], ex); + } } value = classes; } diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/MergedAnnotationReadingVisitor.java b/spring-core/src/main/java/org/springframework/core/type/classreading/MergedAnnotationReadingVisitor.java index 0af13e6d002..8d2a82cf406 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/MergedAnnotationReadingVisitor.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/MergedAnnotationReadingVisitor.java @@ -101,7 +101,13 @@ class MergedAnnotationReadingVisitor extends AnnotationVis @SuppressWarnings("unchecked") public > void visitEnum(String descriptor, String value, Consumer consumer) { String className = Type.getType(descriptor).getClassName(); - Class type = (Class) ClassUtils.resolveClassName(className, this.classLoader); + Class type = null; + try { + type = (Class) ClassUtils.forName(className, this.classLoader); + } + catch (ClassNotFoundException | LinkageError ex) { + throw new TypeNotPresentException(className, ex); + } consumer.accept(Enum.valueOf(type, value)); } @@ -113,7 +119,13 @@ class MergedAnnotationReadingVisitor extends AnnotationVis if (AnnotationFilter.PLAIN.matches(className)) { return null; } - Class type = (Class) ClassUtils.resolveClassName(className, this.classLoader); + Class type = null; + try { + type = (Class) ClassUtils.forName(className, this.classLoader); + } + catch (ClassNotFoundException | LinkageError ex) { + throw new TypeNotPresentException(className, ex); + } return new MergedAnnotationReadingVisitor<>(this.classLoader, this.source, type, consumer); } diff --git a/spring-core/src/main/java24/org/springframework/core/type/classreading/ClassFileAnnotationDelegate.java b/spring-core/src/main/java24/org/springframework/core/type/classreading/ClassFileAnnotationDelegate.java index 810636236da..45934d608ee 100644 --- a/spring-core/src/main/java24/org/springframework/core/type/classreading/ClassFileAnnotationDelegate.java +++ b/spring-core/src/main/java24/org/springframework/core/type/classreading/ClassFileAnnotationDelegate.java @@ -139,7 +139,12 @@ abstract class ClassFileAnnotationDelegate { private static Class loadEnumClass(AnnotationValue.OfEnum enumValue, @Nullable ClassLoader classLoader) { String className = ClassFileAnnotationMetadata.resolveTypeName(enumValue.classSymbol()); - return ClassUtils.resolveClassName(className, classLoader); + try { + return ClassUtils.forName(className, classLoader); + } + catch (ClassNotFoundException | LinkageError ex) { + throw new TypeNotPresentException(className, ex); + } } private static Class resolveArrayElementType(List values, @Nullable ClassLoader classLoader) { diff --git a/spring-core/src/test/java/org/springframework/core/annotation/TypeMappedAnnotationTests.java b/spring-core/src/test/java/org/springframework/core/annotation/TypeMappedAnnotationTests.java index e2d7f4f049c..b46fd990af7 100644 --- a/spring-core/src/test/java/org/springframework/core/annotation/TypeMappedAnnotationTests.java +++ b/spring-core/src/test/java/org/springframework/core/annotation/TypeMappedAnnotationTests.java @@ -31,6 +31,7 @@ import org.junit.jupiter.api.Test; import org.springframework.core.annotation.MergedAnnotation.Adapt; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; import static org.assertj.core.api.InstanceOfAssertFactories.throwable; @@ -143,16 +144,16 @@ class TypeMappedAnnotationTests { Map.of(attributeName, "com.example.DoesNotExist")); // 0) Sanity check MergedAnnotation.getClass() behavior. - assertThatIllegalArgumentException() + assertThatExceptionOfType(TypeNotPresentException.class) .isThrownBy(() -> mergedAnnotation.getClass(attributeName)) - .withMessage("Could not find class [com.example.DoesNotExist]") - .withCauseInstanceOf(ClassNotFoundException.class); + .withMessageContaining("com.example.DoesNotExist") + .withCauseExactlyInstanceOf(ClassNotFoundException.class); var map = mergedAnnotation.asMap(Adapt.values(false, true)); // 1) Attribute should be present, even though its value is an exception. assertThat(map).containsKey(attributeName); - assertThat(map.get(attributeName)).asInstanceOf(throwable(IllegalArgumentException.class)) + assertThat(map.get(attributeName)).asInstanceOf(throwable(TypeNotPresentException.class)) .hasMessageContaining("com.example.DoesNotExist"); } @@ -163,16 +164,16 @@ class TypeMappedAnnotationTests { Map.of(attributeName, new String[] { "com.example.DoesNotExist" })); // 0) Sanity check MergedAnnotation.getClassArray() behavior. - assertThatIllegalArgumentException() + assertThatExceptionOfType(TypeNotPresentException.class) .isThrownBy(() -> mergedAnnotation.getClassArray(attributeName)) - .withMessage("Could not find class [com.example.DoesNotExist]") + .withMessageContaining("com.example.DoesNotExist") .withCauseExactlyInstanceOf(ClassNotFoundException.class); var map = mergedAnnotation.asMap(Adapt.values(false, true)); // 1) Attribute should be present, even though its value is an exception. assertThat(map).containsKey(attributeName); - assertThat(map.get(attributeName)).asInstanceOf(throwable(IllegalArgumentException.class)) + assertThat(map.get(attributeName)).asInstanceOf(throwable(TypeNotPresentException.class)) .hasMessageContaining("com.example.DoesNotExist"); } } @@ -188,16 +189,16 @@ class TypeMappedAnnotationTests { Map.of(attributeName, "com.example.DoesNotExist")); // 0) Sanity check MergedAnnotation.getClass() behavior. - assertThatIllegalArgumentException() + assertThatExceptionOfType(TypeNotPresentException.class) .isThrownBy(() -> mergedAnnotation.getClass(attributeName)) - .withMessage("Could not find class [com.example.DoesNotExist]") - .withCauseInstanceOf(ClassNotFoundException.class); + .withMessageContaining("com.example.DoesNotExist") + .withCauseExactlyInstanceOf(ClassNotFoundException.class); var attributes = mergedAnnotation.asAnnotationAttributes(Adapt.values(false, true)); // 1) Attribute should be present, even though its value is an exception. assertThat(attributes).containsKey(attributeName); - assertThat(attributes.get(attributeName)).asInstanceOf(throwable(IllegalArgumentException.class)) + assertThat(attributes.get(attributeName)).asInstanceOf(throwable(TypeNotPresentException.class)) .hasMessageContaining("com.example.DoesNotExist"); // 2) Accessing the attribute via AnnotationAttributes.getClassArray() should throw an @@ -213,16 +214,16 @@ class TypeMappedAnnotationTests { Map.of(attributeName, new String[] { "com.example.DoesNotExist" })); // 0) Sanity check MergedAnnotation.getClassArray() behavior. - assertThatIllegalArgumentException() + assertThatExceptionOfType(TypeNotPresentException.class) .isThrownBy(() -> mergedAnnotation.getClassArray(attributeName)) - .withMessage("Could not find class [com.example.DoesNotExist]") - .withCauseInstanceOf(ClassNotFoundException.class); + .withMessageContaining("com.example.DoesNotExist") + .withCauseExactlyInstanceOf(ClassNotFoundException.class); var attributes = mergedAnnotation.asAnnotationAttributes(Adapt.values(false, true)); // 1) Attribute should be present, even though its value is an exception. assertThat(attributes).containsKey(attributeName); - assertThat(attributes.get(attributeName)).asInstanceOf(throwable(IllegalArgumentException.class)) + assertThat(attributes.get(attributeName)).asInstanceOf(throwable(TypeNotPresentException.class)) .hasMessageContaining("com.example.DoesNotExist"); // 2) Accessing the attribute via AnnotationAttributes.getClassArray() should throw an @@ -236,9 +237,9 @@ class TypeMappedAnnotationTests { .isThrownBy(throwingCallable) .withMessageMatching(""" Attribute '%s' for annotation \\[.+?\\] was not resolvable \ - due to exception \\[.+?IllegalArgumentException.+?\\]""".formatted(attributeName)) + due to exception \\[.+?TypeNotPresentException.+?\\]""".formatted(attributeName)) .havingCause() - .isExactlyInstanceOf(IllegalArgumentException.class) + .isExactlyInstanceOf(TypeNotPresentException.class) .withMessageContaining("com.example.DoesNotExist") .havingCause() .isExactlyInstanceOf(ClassNotFoundException.class) 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 58f64bb3da5..025c7ebf2df 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 @@ -27,7 +27,7 @@ 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; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; /** * Tests for {@link SimpleAnnotationMetadata} and @@ -59,7 +59,9 @@ class DefaultAnnotationMetadataTests extends AbstractAnnotationMetadataTests { .getAnnotations() .get(ClassAttributes.class); assertThat(mergedAnnotation.getStringArray("types")).contains("javax.annotation.meta.When"); - assertThatIllegalArgumentException().isThrownBy(() -> mergedAnnotation.getClassArray("types")); + assertThatExceptionOfType(TypeNotPresentException.class) + .isThrownBy(() -> mergedAnnotation.getClassArray("types")) + .withMessageContaining("javax.annotation.meta.When"); } 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 28432205943..3b60299835c 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 @@ -27,7 +27,7 @@ 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; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; /** * Tests for {@link SimpleAnnotationMetadata} and @@ -58,7 +58,9 @@ class SimpleAnnotationMetadataTests extends AbstractAnnotationMetadataTests { .getAnnotations() .get(ClassAttributes.class); assertThat(mergedAnnotation.getStringArray("types")).contains("javax.annotation.meta.When"); - assertThatIllegalArgumentException().isThrownBy(() -> mergedAnnotation.getClassArray("types")); + assertThatExceptionOfType(TypeNotPresentException.class) + .isThrownBy(() -> mergedAnnotation.getClassArray("types")) + .withMessageContaining("javax.annotation.meta.When"); }