Add ClassFile variant for class metadata reading
Prior to this commit, Spring Framework would use its own ASM fork to read class/method/annotation metadata from bytecode. This is typically used in configuration class parsing to build bean definitions without actually loading classes at runtime at that step. This commit adds support for a new metadata reading implementation that uses the ClassFile API available as of Java 24. For now, this is turned on by default for Java 24+. Closes gh-33616
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,14 +16,17 @@
|
||||
|
||||
package org.springframework.core.type;
|
||||
|
||||
import java.lang.annotation.Repeatable;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import kotlin.Metadata;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
import org.springframework.core.type.AbstractAnnotationMetadataTests.TestMemberClass.TestMemberClassInnerClass;
|
||||
import org.springframework.core.type.AbstractAnnotationMetadataTests.TestMemberClass.TestMemberClassInnerInterface;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -34,376 +37,493 @@ import static org.assertj.core.api.Assertions.entry;
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public abstract class AbstractAnnotationMetadataTests {
|
||||
|
||||
@Test
|
||||
void verifyEquals() {
|
||||
AnnotationMetadata testClass1 = get(TestClass.class);
|
||||
AnnotationMetadata testClass2 = get(TestClass.class);
|
||||
AnnotationMetadata testMemberClass1 = get(TestMemberClass.class);
|
||||
AnnotationMetadata testMemberClass2 = get(TestMemberClass.class);
|
||||
|
||||
assertThat(testClass1).isNotEqualTo(null);
|
||||
|
||||
assertThat(testClass1).isEqualTo(testClass1);
|
||||
assertThat(testClass2).isEqualTo(testClass2);
|
||||
assertThat(testClass1).isEqualTo(testClass2);
|
||||
assertThat(testClass2).isEqualTo(testClass1);
|
||||
|
||||
assertThat(testMemberClass1).isEqualTo(testMemberClass1);
|
||||
assertThat(testMemberClass2).isEqualTo(testMemberClass2);
|
||||
assertThat(testMemberClass1).isEqualTo(testMemberClass2);
|
||||
assertThat(testMemberClass2).isEqualTo(testMemberClass1);
|
||||
|
||||
assertThat(testClass1).isNotEqualTo(testMemberClass1);
|
||||
assertThat(testMemberClass1).isNotEqualTo(testClass1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void verifyHashCode() {
|
||||
AnnotationMetadata testClass1 = get(TestClass.class);
|
||||
AnnotationMetadata testClass2 = get(TestClass.class);
|
||||
AnnotationMetadata testMemberClass1 = get(TestMemberClass.class);
|
||||
AnnotationMetadata testMemberClass2 = get(TestMemberClass.class);
|
||||
|
||||
assertThat(testClass1).hasSameHashCodeAs(testClass2);
|
||||
assertThat(testMemberClass1).hasSameHashCodeAs(testMemberClass2);
|
||||
|
||||
assertThat(testClass1).doesNotHaveSameHashCodeAs(testMemberClass1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void verifyToString() {
|
||||
assertThat(get(TestClass.class).toString()).isEqualTo(TestClass.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getClassNameReturnsClassName() {
|
||||
assertThat(get(TestClass.class).getClassName()).isEqualTo(TestClass.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void isInterfaceWhenInterfaceReturnsTrue() {
|
||||
assertThat(get(TestInterface.class).isInterface()).isTrue();
|
||||
assertThat(get(TestAnnotation.class).isInterface()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isInterfaceWhenNotInterfaceReturnsFalse() {
|
||||
assertThat(get(TestClass.class).isInterface()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAnnotationWhenAnnotationReturnsTrue() {
|
||||
assertThat(get(TestAnnotation.class).isAnnotation()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAnnotationWhenNotAnnotationReturnsFalse() {
|
||||
assertThat(get(TestClass.class).isAnnotation()).isFalse();
|
||||
assertThat(get(TestInterface.class).isAnnotation()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isFinalWhenFinalReturnsTrue() {
|
||||
assertThat(get(TestFinalClass.class).isFinal()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isFinalWhenNonFinalReturnsFalse() {
|
||||
assertThat(get(TestClass.class).isFinal()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isIndependentWhenIndependentReturnsTrue() {
|
||||
assertThat(get(AbstractAnnotationMetadataTests.class).isIndependent()).isTrue();
|
||||
assertThat(get(TestClass.class).isIndependent()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isIndependentWhenNotIndependentReturnsFalse() {
|
||||
assertThat(get(TestNonStaticInnerClass.class).isIndependent()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getEnclosingClassNameWhenHasEnclosingClassReturnsEnclosingClass() {
|
||||
assertThat(get(TestClass.class).getEnclosingClassName()).isEqualTo(
|
||||
AbstractAnnotationMetadataTests.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getEnclosingClassNameWhenHasNoEnclosingClassReturnsNull() {
|
||||
assertThat(get(AbstractAnnotationMetadataTests.class).getEnclosingClassName()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSuperClassNameWhenHasSuperClassReturnsName() {
|
||||
assertThat(get(TestSubclass.class).getSuperClassName()).isEqualTo(TestClass.class.getName());
|
||||
assertThat(get(TestClass.class).getSuperClassName()).isEqualTo(Object.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSuperClassNameWhenHasNoSuperClassReturnsNull() {
|
||||
assertThat(get(Object.class).getSuperClassName()).isNull();
|
||||
assertThat(get(TestInterface.class).getSuperClassName()).isNull();
|
||||
assertThat(get(TestSubInterface.class).getSuperClassName()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getInterfaceNamesWhenHasInterfacesReturnsNames() {
|
||||
assertThat(get(TestSubclass.class).getInterfaceNames()).containsExactlyInAnyOrder(TestInterface.class.getName());
|
||||
assertThat(get(TestSubInterface.class).getInterfaceNames()).containsExactlyInAnyOrder(TestInterface.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getInterfaceNamesWhenHasNoInterfacesReturnsEmptyArray() {
|
||||
assertThat(get(TestClass.class).getInterfaceNames()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMemberClassNamesWhenHasMemberClassesReturnsNames() {
|
||||
assertThat(get(TestMemberClass.class).getMemberClassNames()).containsExactlyInAnyOrder(
|
||||
TestMemberClassInnerClass.class.getName(), TestMemberClassInnerInterface.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMemberClassNamesWhenHasNoMemberClassesReturnsEmptyArray() {
|
||||
assertThat(get(TestClass.class).getMemberClassNames()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAnnotationsReturnsDirectAnnotations() {
|
||||
assertThat(get(WithDirectAnnotations.class).getAnnotations().stream())
|
||||
.filteredOn(MergedAnnotation::isDirectlyPresent)
|
||||
.extracting(a -> a.getType().getName())
|
||||
.containsExactlyInAnyOrder(DirectAnnotation1.class.getName(), DirectAnnotation2.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAnnotatedWhenMatchesDirectAnnotationReturnsTrue() {
|
||||
assertThat(get(WithDirectAnnotations.class).isAnnotated(DirectAnnotation1.class.getName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAnnotatedWhenMatchesMetaAnnotationReturnsTrue() {
|
||||
assertThat(get(WithMetaAnnotations.class).isAnnotated(MetaAnnotation2.class.getName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAnnotatedWhenDoesNotMatchDirectOrMetaAnnotationReturnsFalse() {
|
||||
assertThat(get(TestClass.class).isAnnotated(DirectAnnotation1.class.getName())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAnnotationAttributesReturnsAttributes() {
|
||||
assertThat(get(WithAnnotationAttributes.class).getAnnotationAttributes(AnnotationAttributes.class.getName()))
|
||||
.containsOnly(entry("name", "test"), entry("size", 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllAnnotationAttributesReturnsAllAttributes() {
|
||||
MultiValueMap<String, Object> attributes =
|
||||
get(WithMetaAnnotationAttributes.class).getAllAnnotationAttributes(AnnotationAttributes.class.getName());
|
||||
assertThat(attributes).containsOnlyKeys("name", "size");
|
||||
assertThat(attributes.get("name")).containsExactlyInAnyOrder("m1", "m2");
|
||||
assertThat(attributes.get("size")).containsExactlyInAnyOrder(1, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAnnotationTypesReturnsDirectAnnotations() {
|
||||
AnnotationMetadata metadata = get(WithDirectAnnotations.class);
|
||||
assertThat(metadata.getAnnotationTypes()).containsExactlyInAnyOrder(
|
||||
DirectAnnotation1.class.getName(), DirectAnnotation2.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMetaAnnotationTypesReturnsMetaAnnotations() {
|
||||
AnnotationMetadata metadata = get(WithMetaAnnotations.class);
|
||||
assertThat(metadata.getMetaAnnotationTypes(MetaAnnotationRoot.class.getName()))
|
||||
.containsExactlyInAnyOrder(MetaAnnotation1.class.getName(), MetaAnnotation2.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasAnnotationWhenMatchesDirectAnnotationReturnsTrue() {
|
||||
assertThat(get(WithDirectAnnotations.class).hasAnnotation(DirectAnnotation1.class.getName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasAnnotationWhenMatchesMetaAnnotationReturnsFalse() {
|
||||
assertThat(get(WithMetaAnnotations.class).hasAnnotation(MetaAnnotation1.class.getName())).isFalse();
|
||||
assertThat(get(WithMetaAnnotations.class).hasAnnotation(MetaAnnotation2.class.getName())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasAnnotationWhenDoesNotMatchDirectOrMetaAnnotationReturnsFalse() {
|
||||
assertThat(get(TestClass.class).hasAnnotation(DirectAnnotation1.class.getName())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasMetaAnnotationWhenMatchesDirectReturnsFalse() {
|
||||
assertThat(get(WithDirectAnnotations.class).hasMetaAnnotation(DirectAnnotation1.class.getName())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasMetaAnnotationWhenMatchesMetaAnnotationReturnsTrue() {
|
||||
assertThat(get(WithMetaAnnotations.class).hasMetaAnnotation(MetaAnnotation1.class.getName())).isTrue();
|
||||
assertThat(get(WithMetaAnnotations.class).hasMetaAnnotation(MetaAnnotation2.class.getName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasMetaAnnotationWhenDoesNotMatchDirectOrMetaAnnotationReturnsFalse() {
|
||||
assertThat(get(TestClass.class).hasMetaAnnotation(MetaAnnotation1.class.getName())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasAnnotatedMethodsWhenMatchesDirectAnnotationReturnsTrue() {
|
||||
assertThat(get(WithAnnotatedMethod.class).hasAnnotatedMethods(DirectAnnotation1.class.getName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasAnnotatedMethodsWhenMatchesMetaAnnotationReturnsTrue() {
|
||||
assertThat(get(WithMetaAnnotatedMethod.class).hasAnnotatedMethods(MetaAnnotation2.class.getName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasAnnotatedMethodsWhenDoesNotMatchAnyAnnotationReturnsFalse() {
|
||||
assertThat(get(WithAnnotatedMethod.class).hasAnnotatedMethods(MetaAnnotation2.class.getName())).isFalse();
|
||||
assertThat(get(WithNonAnnotatedMethod.class).hasAnnotatedMethods(DirectAnnotation1.class.getName())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAnnotatedMethodsReturnsMatchingAnnotatedAndMetaAnnotatedMethods() {
|
||||
assertThat(get(WithDirectAndMetaAnnotatedMethods.class).getAnnotatedMethods(MetaAnnotation2.class.getName()))
|
||||
.extracting(MethodMetadata::getMethodName)
|
||||
.containsExactlyInAnyOrder("direct", "meta");
|
||||
}
|
||||
|
||||
protected abstract AnnotationMetadata get(Class<?> source);
|
||||
|
||||
@Nested
|
||||
class TypeTests {
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface DirectAnnotation1 {
|
||||
}
|
||||
@Test
|
||||
void classEquals() {
|
||||
AnnotationMetadata testClass1 = get(TestClass.class);
|
||||
AnnotationMetadata testClass2 = get(TestClass.class);
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface DirectAnnotation2 {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@MetaAnnotation1
|
||||
public @interface MetaAnnotationRoot {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@MetaAnnotation2
|
||||
public @interface MetaAnnotation1 {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MetaAnnotation2 {
|
||||
}
|
||||
|
||||
public static class TestClass {
|
||||
}
|
||||
|
||||
public interface TestInterface {
|
||||
}
|
||||
|
||||
public interface TestSubInterface extends TestInterface {
|
||||
}
|
||||
|
||||
public @interface TestAnnotation {
|
||||
}
|
||||
|
||||
public static final class TestFinalClass {
|
||||
}
|
||||
|
||||
public class TestNonStaticInnerClass {
|
||||
}
|
||||
|
||||
public static class TestSubclass extends TestClass implements TestInterface {
|
||||
}
|
||||
|
||||
@DirectAnnotation1
|
||||
@DirectAnnotation2
|
||||
public static class WithDirectAnnotations {
|
||||
}
|
||||
|
||||
@MetaAnnotationRoot
|
||||
public static class WithMetaAnnotations {
|
||||
}
|
||||
|
||||
public static class TestMemberClass {
|
||||
|
||||
public static class TestMemberClassInnerClass {
|
||||
assertThat(testClass1).isEqualTo(testClass1);
|
||||
assertThat(testClass2).isEqualTo(testClass2);
|
||||
assertThat(testClass1).isEqualTo(testClass2);
|
||||
assertThat(testClass2).isEqualTo(testClass1);
|
||||
}
|
||||
|
||||
interface TestMemberClassInnerInterface {
|
||||
@Test
|
||||
void memberClassEquals() {
|
||||
AnnotationMetadata testMemberClass1 = get(TestMemberClass.class);
|
||||
AnnotationMetadata testMemberClass2 = get(TestMemberClass.class);
|
||||
|
||||
assertThat(testMemberClass1).isEqualTo(testMemberClass1);
|
||||
assertThat(testMemberClass2).isEqualTo(testMemberClass2);
|
||||
assertThat(testMemberClass1).isEqualTo(testMemberClass2);
|
||||
assertThat(testMemberClass2).isEqualTo(testMemberClass1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void classHashCode() {
|
||||
AnnotationMetadata testClass1 = get(TestClass.class);
|
||||
AnnotationMetadata testClass2 = get(TestClass.class);
|
||||
|
||||
assertThat(testClass1).hasSameHashCodeAs(testClass2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void memberClassHashCode() {
|
||||
AnnotationMetadata testMemberClass1 = get(TestMemberClass.class);
|
||||
AnnotationMetadata testMemberClass2 = get(TestMemberClass.class);
|
||||
|
||||
assertThat(testMemberClass1).hasSameHashCodeAs(testMemberClass2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void classToString() {
|
||||
assertThat(get(TestClass.class).toString()).isEqualTo(TestClass.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getClassNameReturnsClassName() {
|
||||
assertThat(get(TestClass.class).getClassName()).isEqualTo(TestClass.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void isInterfaceWhenInterfaceReturnsTrue() {
|
||||
assertThat(get(TestInterface.class).isInterface()).isTrue();
|
||||
assertThat(get(TestAnnotation.class).isInterface()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isInterfaceWhenNotInterfaceReturnsFalse() {
|
||||
assertThat(get(TestClass.class).isInterface()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAnnotationWhenAnnotationReturnsTrue() {
|
||||
assertThat(get(TestAnnotation.class).isAnnotation()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAnnotationWhenNotAnnotationReturnsFalse() {
|
||||
assertThat(get(TestClass.class).isAnnotation()).isFalse();
|
||||
assertThat(get(TestInterface.class).isAnnotation()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isFinalWhenFinalReturnsTrue() {
|
||||
assertThat(get(TestFinalClass.class).isFinal()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isFinalWhenNonFinalReturnsFalse() {
|
||||
assertThat(get(TestClass.class).isFinal()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isIndependentWhenIndependentReturnsTrue() {
|
||||
assertThat(get(AbstractAnnotationMetadataTests.class).isIndependent()).isTrue();
|
||||
assertThat(get(TestClass.class).isIndependent()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isIndependentWhenNotIndependentReturnsFalse() {
|
||||
assertThat(get(TestNonStaticInnerClass.class).isIndependent()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getEnclosingClassNameWhenHasEnclosingClassReturnsEnclosingClass() {
|
||||
assertThat(get(TestClass.class).getEnclosingClassName()).isEqualTo(
|
||||
AbstractAnnotationMetadataTests.TypeTests.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getEnclosingClassNameWhenHasNoEnclosingClassReturnsNull() {
|
||||
assertThat(get(AbstractAnnotationMetadataTests.class).getEnclosingClassName()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSuperClassNameWhenHasSuperClassReturnsName() {
|
||||
assertThat(get(TestSubclass.class).getSuperClassName()).isEqualTo(TestClass.class.getName());
|
||||
assertThat(get(TestClass.class).getSuperClassName()).isEqualTo(Object.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getSuperClassNameWhenHasNoSuperClassReturnsNull() {
|
||||
assertThat(get(Object.class).getSuperClassName()).isNull();
|
||||
assertThat(get(TestInterface.class).getSuperClassName()).isIn(null, "java.lang.Object");
|
||||
assertThat(get(TestSubInterface.class).getSuperClassName()).isIn(null, "java.lang.Object");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getInterfaceNamesWhenHasInterfacesReturnsNames() {
|
||||
assertThat(get(TestSubclass.class).getInterfaceNames()).containsExactly(TestInterface.class.getName());
|
||||
assertThat(get(TestSubInterface.class).getInterfaceNames()).containsExactly(TestInterface.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getInterfaceNamesWhenHasNoInterfacesReturnsEmptyArray() {
|
||||
assertThat(get(TestClass.class).getInterfaceNames()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMemberClassNamesWhenHasMemberClassesReturnsNames() {
|
||||
assertThat(get(TestMemberClass.class).getMemberClassNames()).containsExactlyInAnyOrder(
|
||||
TestMemberClass.TestMemberClassInnerClass.class.getName(), TestMemberClass.TestMemberClassInnerInterface.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMemberClassNamesWhenHasNoMemberClassesReturnsEmptyArray() {
|
||||
assertThat(get(TestClass.class).getMemberClassNames()).isEmpty();
|
||||
}
|
||||
|
||||
public static class TestClass {
|
||||
}
|
||||
|
||||
public interface TestInterface {
|
||||
}
|
||||
|
||||
public interface TestSubInterface extends TestInterface {
|
||||
}
|
||||
|
||||
public @interface TestAnnotation {
|
||||
}
|
||||
|
||||
public static final class TestFinalClass {
|
||||
}
|
||||
|
||||
public class TestNonStaticInnerClass {
|
||||
}
|
||||
|
||||
public static class TestSubclass extends TestClass implements TestInterface {
|
||||
}
|
||||
|
||||
public static class TestMemberClass {
|
||||
|
||||
public static class TestMemberClassInnerClass {
|
||||
}
|
||||
|
||||
interface TestMemberClassInnerInterface {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class WithAnnotatedMethod {
|
||||
@Nested
|
||||
class AnnotationTests {
|
||||
|
||||
@Test
|
||||
void getAnnotationsReturnsDirectAnnotations() {
|
||||
assertThat(get(WithDirectAnnotations.class).getAnnotations().stream())
|
||||
.filteredOn(MergedAnnotation::isDirectlyPresent)
|
||||
.extracting(a -> a.getType().getName())
|
||||
.containsExactlyInAnyOrder(DirectAnnotation1.class.getName(), DirectAnnotation2.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAnnotatedWhenMatchesDirectAnnotationReturnsTrue() {
|
||||
assertThat(get(WithDirectAnnotations.class).isAnnotated(DirectAnnotation1.class.getName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAnnotatedWhenMatchesMetaAnnotationReturnsTrue() {
|
||||
assertThat(get(WithMetaAnnotations.class).isAnnotated(MetaAnnotation2.class.getName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAnnotatedWhenDoesNotMatchDirectOrMetaAnnotationReturnsFalse() {
|
||||
assertThat(get(NoAnnotationClass.class).isAnnotated(DirectAnnotation1.class.getName())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAnnotationAttributesReturnsAttributes() {
|
||||
assertThat(get(WithAnnotationAttributes.class).getAnnotationAttributes(AnnotationAttributes.class.getName()))
|
||||
.containsOnly(entry("name", "test"), entry("size", 1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllAnnotationAttributesReturnsAllAttributes() {
|
||||
MultiValueMap<String, Object> attributes =
|
||||
get(WithMetaAnnotationAttributes.class).getAllAnnotationAttributes(AnnotationAttributes.class.getName());
|
||||
assertThat(attributes).containsOnlyKeys("name", "size");
|
||||
assertThat(attributes.get("name")).containsExactlyInAnyOrder("m1", "m2");
|
||||
assertThat(attributes.get("size")).containsExactlyInAnyOrder(1, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getComplexAttributeTypesReturnsAll() {
|
||||
MultiValueMap<String, Object> attributes =
|
||||
get(WithComplexAttributeTypes.class).getAllAnnotationAttributes(ComplexAttributes.class.getName());
|
||||
assertThat(attributes).containsOnlyKeys("names", "count", "type", "subAnnotation");
|
||||
assertThat(attributes.get("names")).hasSize(1);
|
||||
assertThat(attributes.get("names").get(0)).isEqualTo(new String[]{"first", "second"});
|
||||
assertThat(attributes.get("count")).containsExactlyInAnyOrder(TestEnum.ONE);
|
||||
assertThat(attributes.get("type")).containsExactlyInAnyOrder(TestEnum.class);
|
||||
assertThat(attributes.get("subAnnotation")).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getComplexAttributeTypesReturnsAllWithKotlinMetadata() {
|
||||
MultiValueMap<String, Object> attributes =
|
||||
get(WithComplexAttributeTypes.class).getAllAnnotationAttributes(Metadata.class.getName());
|
||||
assertThat(attributes).containsKeys("k", "mv");
|
||||
int[] values = {42};
|
||||
assertThat(attributes.get("mv")).hasSize(1);
|
||||
assertThat(attributes.get("mv").get(0)).isEqualTo(values);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getRepeatableReturnsAttributes() {
|
||||
MultiValueMap<String, Object> attributes =
|
||||
get(WithRepeatableAnnotations.class).getAllAnnotationAttributes(RepeatableAnnotations.class.getName());
|
||||
assertThat(attributes).containsKeys("value");
|
||||
assertThat(attributes.get("value")).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAnnotationTypesReturnsDirectAnnotations() {
|
||||
AnnotationMetadata metadata = get(WithDirectAnnotations.class);
|
||||
assertThat(metadata.getAnnotationTypes()).containsExactlyInAnyOrder(
|
||||
DirectAnnotation1.class.getName(), DirectAnnotation2.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getMetaAnnotationTypesReturnsMetaAnnotations() {
|
||||
AnnotationMetadata metadata = get(WithMetaAnnotations.class);
|
||||
assertThat(metadata.getMetaAnnotationTypes(MetaAnnotationRoot.class.getName()))
|
||||
.containsExactlyInAnyOrder(MetaAnnotation1.class.getName(), MetaAnnotation2.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasAnnotationWhenMatchesDirectAnnotationReturnsTrue() {
|
||||
assertThat(get(WithDirectAnnotations.class).hasAnnotation(DirectAnnotation1.class.getName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasAnnotationWhenMatchesMetaAnnotationReturnsFalse() {
|
||||
assertThat(get(WithMetaAnnotations.class).hasAnnotation(MetaAnnotation1.class.getName())).isFalse();
|
||||
assertThat(get(WithMetaAnnotations.class).hasAnnotation(MetaAnnotation2.class.getName())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasAnnotationWhenDoesNotMatchDirectOrMetaAnnotationReturnsFalse() {
|
||||
assertThat(get(NoAnnotationClass.class).hasAnnotation(DirectAnnotation1.class.getName())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasMetaAnnotationWhenMatchesDirectReturnsFalse() {
|
||||
assertThat(get(WithDirectAnnotations.class).hasMetaAnnotation(DirectAnnotation1.class.getName())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasMetaAnnotationWhenMatchesMetaAnnotationReturnsTrue() {
|
||||
assertThat(get(WithMetaAnnotations.class).hasMetaAnnotation(MetaAnnotation1.class.getName())).isTrue();
|
||||
assertThat(get(WithMetaAnnotations.class).hasMetaAnnotation(MetaAnnotation2.class.getName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasMetaAnnotationWhenDoesNotMatchDirectOrMetaAnnotationReturnsFalse() {
|
||||
assertThat(get(NoAnnotationClass.class).hasMetaAnnotation(MetaAnnotation1.class.getName())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasAnnotatedMethodsWhenMatchesDirectAnnotationReturnsTrue() {
|
||||
assertThat(get(WithAnnotatedMethod.class).hasAnnotatedMethods(DirectAnnotation1.class.getName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasAnnotatedMethodsWhenMatchesMetaAnnotationReturnsTrue() {
|
||||
assertThat(get(WithMetaAnnotatedMethod.class).hasAnnotatedMethods(MetaAnnotation2.class.getName())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasAnnotatedMethodsWhenDoesNotMatchAnyAnnotationReturnsFalse() {
|
||||
assertThat(get(WithAnnotatedMethod.class).hasAnnotatedMethods(MetaAnnotation2.class.getName())).isFalse();
|
||||
assertThat(get(WithNonAnnotatedMethod.class).hasAnnotatedMethods(DirectAnnotation1.class.getName())).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAnnotatedMethodsReturnsMatchingAnnotatedAndMetaAnnotatedMethods() {
|
||||
assertThat(get(WithDirectAndMetaAnnotatedMethods.class).getAnnotatedMethods(MetaAnnotation2.class.getName()))
|
||||
.extracting(MethodMetadata::getMethodName)
|
||||
.containsExactlyInAnyOrder("direct", "meta");
|
||||
}
|
||||
|
||||
public static class WithAnnotatedMethod {
|
||||
|
||||
@DirectAnnotation1
|
||||
public void test() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class WithMetaAnnotatedMethod {
|
||||
|
||||
@MetaAnnotationRoot
|
||||
public void test() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class WithNonAnnotatedMethod {
|
||||
|
||||
}
|
||||
|
||||
public static class WithDirectAndMetaAnnotatedMethods {
|
||||
|
||||
@MetaAnnotation2
|
||||
public void direct() {
|
||||
}
|
||||
|
||||
@MetaAnnotationRoot
|
||||
public void meta() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@AnnotationAttributes(name = "test", size = 1)
|
||||
public static class WithAnnotationAttributes {
|
||||
}
|
||||
|
||||
@MetaAnnotationAttributes1
|
||||
@MetaAnnotationAttributes2
|
||||
public static class WithMetaAnnotationAttributes {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@AnnotationAttributes(name = "m1", size = 1)
|
||||
public @interface MetaAnnotationAttributes1 {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@AnnotationAttributes(name = "m2", size = 2)
|
||||
public @interface MetaAnnotationAttributes2 {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface AnnotationAttributes {
|
||||
|
||||
String name();
|
||||
|
||||
int size();
|
||||
|
||||
}
|
||||
|
||||
@ComplexAttributes(names = {"first", "second"}, count = TestEnum.ONE,
|
||||
type = TestEnum.class, subAnnotation = @SubAnnotation(name="spring"))
|
||||
@Metadata(mv = {42})
|
||||
public static class WithComplexAttributeTypes {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ComplexAttributes {
|
||||
|
||||
String[] names();
|
||||
|
||||
TestEnum count();
|
||||
|
||||
Class<?> type();
|
||||
|
||||
SubAnnotation subAnnotation();
|
||||
}
|
||||
|
||||
public @interface SubAnnotation {
|
||||
|
||||
String name();
|
||||
}
|
||||
|
||||
public enum TestEnum {
|
||||
ONE, TWO, THREE
|
||||
}
|
||||
|
||||
@RepeatableAnnotation(name = "first")
|
||||
@RepeatableAnnotation(name = "second")
|
||||
public static class WithRepeatableAnnotations {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Repeatable(RepeatableAnnotations.class)
|
||||
public @interface RepeatableAnnotation {
|
||||
|
||||
String name();
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface RepeatableAnnotations {
|
||||
|
||||
RepeatableAnnotation[] value();
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface DirectAnnotation1 {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface DirectAnnotation2 {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@MetaAnnotation1
|
||||
public @interface MetaAnnotationRoot {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@MetaAnnotation2
|
||||
public @interface MetaAnnotation1 {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MetaAnnotation2 {
|
||||
}
|
||||
|
||||
@DirectAnnotation1
|
||||
public void test() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class WithMetaAnnotatedMethod {
|
||||
|
||||
@MetaAnnotationRoot
|
||||
public void test() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class WithNonAnnotatedMethod {
|
||||
|
||||
public void test() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class WithDirectAndMetaAnnotatedMethods {
|
||||
|
||||
@MetaAnnotation2
|
||||
public void direct() {
|
||||
@DirectAnnotation2
|
||||
public static class WithDirectAnnotations {
|
||||
}
|
||||
|
||||
@MetaAnnotationRoot
|
||||
public void meta() {
|
||||
public static class WithMetaAnnotations {
|
||||
}
|
||||
|
||||
static class NoAnnotationClass {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@AnnotationAttributes(name = "test", size = 1)
|
||||
public static class WithAnnotationAttributes {
|
||||
}
|
||||
@Nested
|
||||
class MethodTests {
|
||||
|
||||
@MetaAnnotationAttributes1
|
||||
@MetaAnnotationAttributes2
|
||||
public static class WithMetaAnnotationAttributes {
|
||||
}
|
||||
@Test
|
||||
void declaredMethodsToString() {
|
||||
List<String> methods = get(TestMethods.class).getDeclaredMethods().stream().map(Object::toString).toList();
|
||||
List<String> expected = Arrays.stream(TestMethods.class.getDeclaredMethods()).map(Object::toString).toList();
|
||||
assertThat(methods).containsExactlyInAnyOrderElementsOf(expected);
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@AnnotationAttributes(name = "m1", size = 1)
|
||||
public @interface MetaAnnotationAttributes1 {
|
||||
}
|
||||
static class TestMethods {
|
||||
public String test1(String argument) {
|
||||
return "test";
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@AnnotationAttributes(name = "m2", size = 2)
|
||||
public @interface MetaAnnotationAttributes2 {
|
||||
}
|
||||
public String test2(String argument) {
|
||||
return "test";
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface AnnotationAttributes {
|
||||
|
||||
String name();
|
||||
|
||||
int size();
|
||||
public String test3(String argument) {
|
||||
return "test";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,7 +21,8 @@ import org.springframework.core.type.AnnotationMetadata;
|
||||
|
||||
/**
|
||||
* Tests for {@link SimpleAnnotationMetadata} and
|
||||
* {@link SimpleAnnotationMetadataReadingVisitor}.
|
||||
* {@link SimpleAnnotationMetadataReadingVisitor} on Java < 24,
|
||||
* and for the ClassFile API variant on Java >= 24.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@@ -30,9 +31,8 @@ class SimpleAnnotationMetadataTests extends AbstractAnnotationMetadataTests {
|
||||
@Override
|
||||
protected AnnotationMetadata get(Class<?> source) {
|
||||
try {
|
||||
return new SimpleMetadataReaderFactory(
|
||||
source.getClassLoader()).getMetadataReader(
|
||||
source.getName()).getAnnotationMetadata();
|
||||
return MetadataReaderFactory.create(source.getClassLoader())
|
||||
.getMetadataReader(source.getName()).getAnnotationMetadata();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
|
||||
Reference in New Issue
Block a user