Spring注解工具类

前言

在看Spring源码的时候,经常会有处理注解的时候,比如从方法上获取注解,类上获取注解,注解属性别名。JDK中自带的获取注解API有点简单,不会从父类方法或者接口上的方法去查找,不能为属性定义别名等,因此Spring封装了一个便利的工具类,更加方便的去获取注解信息。

JDK自带方法

AnnotatedElement为获取注解的顶层接口,ClassMethod都实现了这个接口。

关于元注解@Inherited

@Inherited标记的注解是可以被子类继承的,注意必须在类上,接口或者方法上都是不能被继承的。

下面看下API的使用

先准备几个注解用来测试

/**
 * 被@Inherited标记
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface InheriteClassCase {
}

/**
 * 被@Inherited标记
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface InheriteInterfaceCase {
}

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NoInheriteCase {
}

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OtherCase {
}

测试类如下

public class JdkAnnotationTest {

    @InheriteClassCase
    @NoInheriteCase
    private static abstract class ParentClassCase {

        @InheriteClassCase
        abstract void invokeClass();
    }

    @InheriteInterfaceCase
    private interface ParentInterfaceCase {

        void invokeInterface();
    }

    @OtherCase
    private class SonCase extends ParentClassCase implements ParentInterfaceCase {

        @Override
        public void invokeClass() {

        }

        @Override
        public void invokeInterface() {

        }
    }

    @Test
    public void testApi() throws NoSuchMethodException {
        // 获取自己声明的注解, 只能拿到OtherCase
        Annotation[] declaredAnnotations = SonCase.class.getDeclaredAnnotations();
        Assert.assertEquals(1, declaredAnnotations.length);
        Assert.assertEquals(OtherCase.class, declaredAnnotations[0].annotationType());

        /*
         * 获取存在的注解(自己声明的或者继承的)
         * 目标注解必须被@Inherited标记才能被继承
         * 且必须在类上
         */
        InheriteClassCase inheriteClassCase = SonCase.class.getAnnotation(InheriteClassCase.class);
        Assert.assertNotNull(inheriteClassCase);

        /*
         * NoInheriteCase虽然在父类存在, 但是没有被@Inherited标记, 不能被继承
         */
        NoInheriteCase noInheriteCase = SonCase.class.getAnnotation(NoInheriteCase.class);
        Assert.assertNull(noInheriteCase);

        // InheriteInterfaceCase虽然被@Inherited标记, 但是在接口上, 也不能获取到
        InheriteInterfaceCase inheriteInterfaceCase = SonCase.class.getAnnotation(InheriteInterfaceCase.class);
        Assert.assertNull(inheriteInterfaceCase);

        Method invokeClassMethod = SonCase.class.getMethod("invokeClass");
        // InheriteClassCase在父类方法中, 子类方法并没有被标注, 因此不能获取到
        InheriteClassCase classCase = invokeClassMethod.getAnnotation(InheriteClassCase.class);
        Assert.assertNull(classCase);
    }
}

Spring AnnotationUtils

该类主要增强了以下几点

  • 查找元注解,getAnnotationfindAnnotation都支持。
  • 查找范围扩大,可在整个继承体系上查找(类 + 接口),仅findAnnotation支持。
  • 属性别名,通过@AliasFor注解,给属性起一个别名,这样指定一个属性,获取别名属性时也能拿到一样的值,getAnnotationfindAnnotation都支持。

何为元注解,一个注解A可以在注解B上注解,那么注解A称为注解B的元注解。一个类上存在注解B,该工具类可以直接获取到元注解A。

API使用

public class AnnotationUtilsTest {

    private static class MetaAnnotationCase {
        @GetMapping("/request")
        public void request(){}
    }

    @InheriteClassCase
    @NoInheriteCase
    private static abstract class ParentClassCase {

        @GetMapping
        @InheriteClassCase
        abstract void invokeClass();
    }

    @InheriteInterfaceCase
    private interface ParentInterfaceCase {

        @GetMapping
        void invokeInterface();
    }

    @OtherCase
    private class SonCase extends ParentClassCase implements ParentInterfaceCase {

        @Override
        public void invokeClass() {

        }

        @Override
        public void invokeInterface() {

        }
    }

    /**
     * getAnnotation基本使用和AnnotatedElement.getAnnotation一样
     * 不过扩展了属性别名、元注解查找
     */
    @Test
    public void testGetAnnotation() throws NoSuchMethodException {
        Method requestMethod = MetaAnnotationCase.class.getMethod("request");

        // 直接存在
        GetMapping getMapping = AnnotationUtils.getAnnotation(requestMethod, GetMapping.class);
        Assert.assertNotNull(getMapping);

        // 查找元注解(RequestMapping注解了GetMapping)
        RequestMapping requestMapping = AnnotationUtils.getAnnotation(requestMethod, RequestMapping.class);
        Assert.assertNotNull(requestMapping);

        // JDK @Inherited语义
        InheriteClassCase inheriteClassCase = AnnotationUtils.getAnnotation(SonCase.class, InheriteClassCase.class);
        Assert.assertNotNull(inheriteClassCase);

        // 接口, @Inherited不生效
        InheriteInterfaceCase inheriteInterfaceCase = AnnotationUtils.getAnnotation(SonCase.class, InheriteInterfaceCase.class);
        Assert.assertNull(inheriteInterfaceCase);

        // 方法上, @Inherited不生效
        Method invokeClassMethod = SonCase.class.getDeclaredMethod("invokeClass");
        InheriteClassCase inheriteClassCaseAtMethod = AnnotationUtils.getAnnotation(invokeClassMethod, InheriteClassCase.class);
        Assert.assertNull(inheriteClassCaseAtMethod);
    }

    /**
     * 在getAnnotation基础上,扩展了搜索范围(类+接口)
     */
    @Test
    public void testFindAnnotation() throws NoSuchMethodException {
        Method invokeClassMethod = SonCase.class.getDeclaredMethod("invokeClass");
        Method invokeInterfaceMethod = SonCase.class.getDeclaredMethod("invokeInterface");

        // 命中父类上的注解(即便没有@Inherited)
        NoInheriteCase noInheriteCase = AnnotationUtils.findAnnotation(SonCase.class, NoInheriteCase.class);
        Assert.assertNotNull(noInheriteCase);

        // 命中接口上的注解
        InheriteInterfaceCase inheriteInterfaceCase = AnnotationUtils.findAnnotation(SonCase.class, InheriteInterfaceCase.class);
        Assert.assertNotNull(inheriteInterfaceCase);

        // 命中父类或者接口中的方法上的注解
        GetMapping getMapping = AnnotationUtils.findAnnotation(invokeInterfaceMethod, GetMapping.class);
        Assert.assertNotNull(getMapping);

        // 查找元注解(父类方法存在GetMapping)
        RequestMapping requestMapping = AnnotationUtils.findAnnotation(invokeClassMethod, RequestMapping.class);
        Assert.assertNotNull(requestMapping);
    }

    /**
     * isAnnotationXXX
     */
    @Test
    public void testIsAnnotation() {
        // Class直接声明的注解,不会考虑@Inherited语义,也不会在继承体系上搜索
        Assert.assertFalse(AnnotationUtils.isAnnotationDeclaredLocally(InheriteClassCase.class, SonCase.class));
        Assert.assertTrue(AnnotationUtils.isAnnotationDeclaredLocally(InheriteClassCase.class, ParentClassCase.class));

        // 指定注解存在且是通过@Inherited继承的,不是直接声明的
        Assert.assertTrue(AnnotationUtils.isAnnotationInherited(InheriteClassCase.class, SonCase.class));
        Assert.assertFalse(AnnotationUtils.isAnnotationInherited(InheriteClassCase.class, ParentClassCase.class));

        // 判断一个注解类型是不是另外一个注解类型的元注解
        Assert.assertTrue(AnnotationUtils.isAnnotationMetaPresent(GetMapping.class, RequestMapping.class));
    }
    
    /**
     * 是不是一个候选的类
     * 主要用来做一个简单判断,先过滤掉一定不满足的类,避免后续动作解析,提升性能
     * 该方法返回false, 那么这个类一定不会存在这个注解, 返回true, 只是有可能存在
     *
     * 具体逻辑,如果注解类以java开头,即标准库的注解,直接返回true
     * 如果注解类不是以java开头, 并且测试类以java开头,即标准库的类,直接返回false,因为标准库肯定不包含第三方注解
     */
    @Test
    public void testIsCandidateClass() {
        Assertions.assertTrue(AnnotationUtils.isCandidateClass(InheriteClassCase.class, Transactional.class));
        Assertions.assertFalse(AnnotationUtils.isCandidateClass(String.class, Transactional.class));
    }

    @Test
    public void testAttrAliasFor() throws NoSuchMethodException {
        Method requestMethod = MetaAnnotationCase.class.getDeclaredMethod("request");
        String[] value = new String[] {"/request"};
        GetMapping getMapping = AnnotationUtils.getAnnotation(requestMethod, GetMapping.class);
        Objects.requireNonNull(getMapping);
        Assert.assertArrayEquals(value, getMapping.value());
        // 虽然只指定了value的值, 但是path属性=value属性, 因为value和path通过@AliasFor互相指定了别名
        Assert.assertArrayEquals(value, getMapping.path());

        AnnotationAttributes annotationAttributes = AnnotationUtils.getAnnotationAttributes(requestMethod, getMapping);
        Assert.assertArrayEquals(value, annotationAttributes.getStringArray("value"));
        Assert.assertArrayEquals(value, annotationAttributes.getStringArray("path"));
    }
}

Spring AnnotatedElementUtils

此类进一步增强了@AliasFor注解,可以合并属性

还是以@GetMapping、@RequestMapping为例

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.GET)
public @interface GetMapping {

    /**
     * Alias for {@link RequestMapping#name}.
     */
    @AliasFor(annotation = RequestMapping.class)
    String name() default "";

    /**
     * Alias for {@link RequestMapping#value}.
     */
    @AliasFor(annotation = RequestMapping.class)
    String[] value() default {};
}

@GetMapping的定义对于value属性,是@RequestMapping注解的value属性别名,SpringMVC中真实起作用的其实是@RequestMapping注解,但是我们使用@GetMapping指定value属性时,SpringMVC在解析时需要拿到@RequestMapping注解的value属性,此时可以通过该工具类来获取。

例子:

public class AnnotatedElementUtilsTest {

    private static class MetaAnnotationCase {
        @GetMapping("/request")
        public void request() {
        }
    }

    @Test
    public void testGetMetaAnnotationTypes() throws NoSuchMethodException {
        Method requestMethod = MetaAnnotationCase.class.getMethod("request");

        String[] res = {RequestMapping.class.getName(), Mapping.class.getName()};
        // 获取元注解类型, 会排除java.lang包下面的注解, 会递归搜索
        Set<String> metaAnnotationTypes = AnnotatedElementUtils.getMetaAnnotationTypes(requestMethod, GetMapping.class);
        Assert.assertArrayEquals(res, metaAnnotationTypes.toArray());
    }

    /**
     * 合并@AliasFor指定的别名属性
     */
    @Test
    public void testGetMergedAnnotationAttributes() throws NoSuchMethodException {
        Method requestMethod = MetaAnnotationCase.class.getDeclaredMethod("request");
        String[] value = new String[] {"/request"};
        String[] defaultValue = new String[] {};
        RequestMapping requestMapping = AnnotationUtils.getAnnotation(requestMethod, RequestMapping.class);
        Objects.requireNonNull(requestMapping);
        // 指定了GetMapping的value属性, 但是通过AnnotationUtils方式拿不到值
        Assert.assertArrayEquals(defaultValue, requestMapping.value());

        // 通过AnnotatedElementUtils的merge系列方法可以合并属性值
        // getXXX, 只会搜索自己和@Inherited语义继承的注解
        requestMapping = AnnotatedElementUtils.getMergedAnnotation(requestMethod, RequestMapping.class);
        Assert.assertNotNull(requestMapping);
        Assert.assertArrayEquals(value, requestMapping.value());

        // findXXX会寻找父类+接口
        requestMapping = AnnotatedElementUtils.findMergedAnnotation(requestMethod, RequestMapping.class);
        Assert.assertNotNull(requestMapping);
        Assert.assertArrayEquals(value, requestMapping.value());

        AnnotationAttributes mergedAnnotationAttributes = AnnotatedElementUtils.getMergedAnnotationAttributes(requestMethod, RequestMapping.class);
        Assert.assertNotNull(mergedAnnotationAttributes);
        Assert.assertArrayEquals(value, mergedAnnotationAttributes.getStringArray("value"));

        mergedAnnotationAttributes = AnnotatedElementUtils.findMergedAnnotationAttributes(requestMethod, RequestMapping.class, false, false);
        Assert.assertNotNull(mergedAnnotationAttributes);
        Assert.assertArrayEquals(value, mergedAnnotationAttributes.getStringArray("value"));

    }

}

getOrFindAll系列

public class AnnotatedElementUtilsTest {

    private static class MetaAnnotationCase {
        @GetMapping("/request")
        public void request() {
        }

        @GetMapping("/getMapping")
        @RequestMapping("/requestMapping")
        public void repeat() {
        }
    }

    /**
     * 拿到所有指定注解列表
     */
    @Test
    public void testGetOrFindAll() throws NoSuchMethodException {
        Method repeatMethod = MetaAnnotationCase.class.getDeclaredMethod("repeat");
        // getXXX, 只会搜索自己和@Inherited语义继承的注解, 返回所有的, 不仅仅是第一个
        // 返回的是LinkedHashSet, 因此有顺序
        Set<RequestMapping> requestMappingSet = AnnotatedElementUtils.getAllMergedAnnotations(repeatMethod, RequestMapping.class);
        String[][] value = {{"/requestMapping"}, {"/getMapping"}};
        int i = 0;
        for (RequestMapping requestMapping : requestMappingSet) {
            Assert.assertArrayEquals(value[i++], requestMapping.value());
        }

        // findXXX, 会搜索类+接口
        requestMappingSet = AnnotatedElementUtils.findAllMergedAnnotations(repeatMethod, RequestMapping.class);
        i = 0;
        for (RequestMapping requestMapping : requestMappingSet) {
            Assert.assertArrayEquals(value[i++], requestMapping.value());
        }
    }

}

Spring MergedAnnotations

这是一套新的接口,是Spring 5.2出现的新功能,主要聚焦于属性别名(@AliasFor)以及组合注解(元注解关系),可以替代掉AnnotatedElementUtils工具类,目前AnnotatedElementUtils大部分方法已经换成MergedAnnotations来实现的。

并且此API搜索范围更清晰,通过枚举类SearchStrategy来决定。

  • DIRECT,直接声明的注解,不会考虑@Inherited

  • INHERITED_ANNOTATIONS,所有直接声明的注解以及通过父类继承的注解(@Inherited)

  • SUPERCLASS,扩大搜索范围,会从父类上(会递归到最顶层)搜索,此时已不需要@Inherited

  • TYPE_HIERARCHY,继续扩大搜索范围,会从整个类型体系上搜索,包括父类和接口,相比SUPERCLASS多了接口

测试例子如下

public class MergedAnnotationsTest {

    @Repeatable(Identity.List.class)
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    private @interface Identity {

        String value();

        @Target({ElementType.TYPE, ElementType.METHOD})
        @Retention(RetentionPolicy.RUNTIME)
        @Documented
        @interface List {
            Identity[] value();
        }
    }

    private interface BaseInterface {

        @Transactional
        void execute();
    }

    private static class MetaAnnotationCase implements BaseInterface {
        @GetMapping("/request")
        @Transactional
        public void request() {
        }

        @Identity("老师")
        @Identity("父亲")
        public void repeat() {
        }

        @GetMapping("/execute")
        @Override
        public void execute() {

        }
    }

    @Test
    public void testGet() throws NoSuchMethodException {
        Method requestMethod = MergedAnnotationsTest.MetaAnnotationCase.class.getMethod("request");
        MergedAnnotations mergedAnnotations = MergedAnnotations.from(requestMethod, MergedAnnotations.SearchStrategy.INHERITED_ANNOTATIONS);
        MergedAnnotation<GetMapping> mergedAnnotation = mergedAnnotations.get(GetMapping.class);
        Assertions.assertTrue(mergedAnnotation.isPresent());

        // 查找元注解
        MergedAnnotation<RequestMapping> requestMergedAnnotation = mergedAnnotations.get(RequestMapping.class);
        Assertions.assertTrue(requestMergedAnnotation.isPresent());

        // 找不到时不会返回null, 返回的是MergedAnnotation#missing()
        MergedAnnotation<Bean> beanMergedAnnotation = mergedAnnotations.get(Bean.class);
        Assertions.assertFalse(beanMergedAnnotation.isPresent());
    }

    /**
     * 测试其他get方法
     * getDistance
     * getAggregateIndex
     * getSource
     * getMetaSource
     * getRoot
     * getMetaTypes
     */
    @Test
    public void testOtherGetMethod() throws NoSuchMethodException {
        Method requestMethod = MergedAnnotationsTest.MetaAnnotationCase.class.getMethod("execute");
        MergedAnnotations mergedAnnotations = MergedAnnotations.from(requestMethod, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
        MergedAnnotation<GetMapping> getMappingMergedAnnotation = mergedAnnotations.get(GetMapping.class);
        MergedAnnotation<Mapping> mappingMergedAnnotation = mergedAnnotations.get(Mapping.class);
        MergedAnnotation<Transactional> transactionalMergedAnnotation = mergedAnnotations.get(Transactional.class);

        // 直接注解就是0
        Assertions.assertEquals(0, getMappingMergedAnnotation.getDistance());
        // @Mapping是@RequestMapping的元注解, 而@RequestMapping是@GetMapping的元注解, 因此为2
        Assertions.assertEquals(2, mappingMergedAnnotation.getDistance());
        Assertions.assertEquals(0, transactionalMergedAnnotation.getDistance());

        // getAggregateIndex获取的继承体系上的声明层级
        Assertions.assertEquals(0, getMappingMergedAnnotation.getAggregateIndex());
        Assertions.assertEquals(0, mappingMergedAnnotation.getAggregateIndex());
        // @Transactional是在接口上声明的, 因此为1
        Assertions.assertEquals(1, transactionalMergedAnnotation.getAggregateIndex());

        // getSource获取的是注解声明时的载体
        Assertions.assertEquals(requestMethod, getMappingMergedAnnotation.getSource());
        // 元注解@Mapping的Source和根注解@GetMapping是一致的
        Assertions.assertEquals(requestMethod, mappingMergedAnnotation.getSource());
        // @Transactional是在接口上声明的
        Assertions.assertEquals(
            MergedAnnotationsTest.BaseInterface.class.getMethod("execute"),
            transactionalMergedAnnotation.getSource()
        );

        // getMetaSource获取的是元注解声明的目标注解, 直接注解返回null
        Assertions.assertNull(getMappingMergedAnnotation.getMetaSource());
        // @Mapping注解是在@RequestMapping注解声明的
        Assertions.assertEquals(RequestMapping.class, Objects.requireNonNull(mappingMergedAnnotation.getMetaSource()).getType());

        // getRoot获取的是元注解声明的根注解, 直接注解返回自己
        Assertions.assertEquals(GetMapping.class, getMappingMergedAnnotation.getRoot().getType());
        // @Mapping注解是在@RequestMapping注解声明的, @RequestMapping注解是在GetMapping注解声明的
        Assertions.assertEquals(GetMapping.class, mappingMergedAnnotation.getRoot().getType());

        // getMetaTypes获取的是自己到根注解之间所有的注解类型
        Assertions.assertEquals(GetMapping.class, getMappingMergedAnnotation.getMetaTypes().get(0));
        List<Class<? extends Annotation>> metaTypes = mappingMergedAnnotation.getMetaTypes();
        Assertions.assertEquals(GetMapping.class, metaTypes.get(0));
        Assertions.assertEquals(RequestMapping.class, metaTypes.get(1));
        Assertions.assertEquals(Mapping.class, metaTypes.get(2));

    }

    @Test
    public void testStream() throws NoSuchMethodException {
        Method requestMethod = MergedAnnotationsTest.MetaAnnotationCase.class.getMethod("request");
        Class<?>[] res = {GetMapping.class, Transactional.class, RequestMapping.class, Mapping.class};
        MergedAnnotations mergedAnnotations = MergedAnnotations.from(requestMethod, MergedAnnotations.SearchStrategy.INHERITED_ANNOTATIONS);
        /*
         * 获取所有的注解, 包括注解上面的元注解, 会排除标准库中java.lang下面的注解
         * 排序行为: 先根据aggregateIndex升序,再根据distance升序
         */
        Class<?>[] classArr = mergedAnnotations.stream().map(MergedAnnotation::getType).toArray(Class[]::new);
        Assertions.assertArrayEquals(res, classArr);

        // 加上参数, 只会包含指定注解
        classArr = mergedAnnotations.stream(GetMapping.class).map(MergedAnnotation::getType).toArray(Class[]::new);
        System.out.println(Arrays.toString(classArr));
        res = new Class<?>[]{GetMapping.class};
        Assertions.assertArrayEquals(res, classArr);

        // 加上参数, 只会包含指定注解(元注解也是可以的), 从GetMapping注解上找到RequestMapping
        classArr = mergedAnnotations.stream(RequestMapping.class).map(MergedAnnotation::getType).toArray(Class[]::new);
        System.out.println(Arrays.toString(classArr));
        res = new Class<?>[]{RequestMapping.class};
        Assertions.assertArrayEquals(res, classArr);
    }

    @Test
    public void testIsSeriesMethod() throws NoSuchMethodException {
        Method requestMethod = MergedAnnotationsTest.MetaAnnotationCase.class.getMethod("request");
        MergedAnnotations mergedAnnotations = MergedAnnotations.from(requestMethod, MergedAnnotations.SearchStrategy.INHERITED_ANNOTATIONS);
        Class<?>[] classArr = mergedAnnotations.stream()
            // 排除掉元注解, 只需要直接存在的注解(是否包含父类或者接口上的注解由SearchStrategy来决定)
            .filter(MergedAnnotation::isDirectlyPresent)
            .map(MergedAnnotation::getType).toArray(Class[]::new);
        Class<?>[] res = {GetMapping.class, Transactional.class};
        Assertions.assertArrayEquals(res, classArr);

        classArr = mergedAnnotations.stream()
            // 只需要元注解
            .filter(MergedAnnotation::isMetaPresent)
            .map(MergedAnnotation::getType).toArray(Class[]::new);
        res = new Class<?>[] {RequestMapping.class, Mapping.class};
        Assertions.assertArrayEquals(res, classArr);

        classArr = mergedAnnotations.stream()
            // 不排除, isPresent = isDirectlyPresent + isMetaPresent
            .filter(MergedAnnotation::isPresent)
            .map(MergedAnnotation::getType).toArray(Class[]::new);
        res = new Class<?>[] {GetMapping.class, Transactional.class, RequestMapping.class, Mapping.class};
        Assertions.assertArrayEquals(res, classArr);
    }

    /**
     * 测试注解属性获取, 完整支持@AliasFor注解
     */
    @Test
    public void testGetAttr() throws NoSuchMethodException {
        Method requestMethod = MergedAnnotationsTest.MetaAnnotationCase.class.getMethod("request");
        MergedAnnotations mergedAnnotations = MergedAnnotations.from(requestMethod, MergedAnnotations.SearchStrategy.INHERITED_ANNOTATIONS);

        MergedAnnotation<RequestMapping> requestMergedAnnotation = mergedAnnotations.get(RequestMapping.class);
        Assertions.assertTrue(requestMergedAnnotation.isPresent());

        // @AliasFor注解会生效, 即便value属性给的是@GetMapping, 但是也能从@RequestMapping上获取到
        String[] value = requestMergedAnnotation.getStringArray("value");
        Assertions.assertArrayEquals(new String[]{"/request"}, value);

        // value属性别名
        value = requestMergedAnnotation.getStringArray("path");
        Assertions.assertArrayEquals(new String[]{"/request"}, value);
    }

    /**
     * 测试可以重复的注解
     */
    @Test
    public void testRepeatable() throws NoSuchMethodException {
        Method requestMethod = MergedAnnotationsTest.MetaAnnotationCase.class.getMethod("repeat");
        MergedAnnotations mergedAnnotations = MergedAnnotations.from(requestMethod, MergedAnnotations.SearchStrategy.DIRECT);

        // 只能拿到最近的那一个
        MergedAnnotation<Identity> identityMergedAnnotation = mergedAnnotations.get(Identity.class);
        Assertions.assertEquals("老师", identityMergedAnnotation.getString("value"));

        // 获取不到容器注解
        MergedAnnotation<Identity.List> identityListMergedAnnotation = mergedAnnotations.get(Identity.List.class);
        Assertions.assertFalse(identityListMergedAnnotation.isPresent());

        // 关闭可重复行为, 默认是RepeatableContainers.standardRepeatables
        mergedAnnotations = MergedAnnotations.from(
            requestMethod,
            MergedAnnotations.SearchStrategy.DIRECT,
            RepeatableContainers.none()
        );

        // 此时只能拿到容器注解
        identityMergedAnnotation = mergedAnnotations.get(Identity.class);
        Assertions.assertFalse(identityMergedAnnotation.isPresent());

        identityListMergedAnnotation = mergedAnnotations.get(Identity.List.class);
        Assertions.assertTrue(identityListMergedAnnotation.isPresent());

        MergedAnnotation<Identity>[] values = identityListMergedAnnotation.getAnnotationArray("value", Identity.class);
        Assertions.assertEquals("老师", values[0].getString("value"));
        Assertions.assertEquals("父亲", values[1].getString("value"));
    }
}

posted on 2023-09-19 22:06  wastonl  阅读(419)  评论(0)    收藏  举报