首页 > 其他分享 >Spring注解工具类

Spring注解工具类

时间:2023-09-19 22:25:10浏览次数:46  
标签:RequestMapping Spring value public Assert 注解 工具 class

前言

在看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));
    }

    @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());
        }
    }

}

标签:RequestMapping,Spring,value,public,Assert,注解,工具,class
From: https://www.cnblogs.com/wt20/p/17715972.html

相关文章

  • 3种 Springboot 全局时间格式化方式,别再写重复代码了
    From: https://developer.aliyun.com/article/771395简介: 别再写重复代码了本文收录在个人博客:www.chengxy-nds.top,技术资料共享,同进步时间格式化在项目中使用频率是非常高的,当我们的 API 接口返回结果,需要对其中某一个 date 字段属性进行特殊的格式化处理,通常会用到......
  • SpringMVC - 1( 了解 + postman 工具 + 请求与响应 + Rest 风格 )
    SpringMVC目录SpringMVCSpringMVC概述SpringMVC入门案例案例制作工作流程解析启动服务器初始化过程单次请求过程bean加载控制问题分析思路分析环境准备bean加载控制PostMan工具的使用PostMan简介PostMan安装保存当前请求请求与响应设置请求映射路径环境准备问题分析设置映......
  • nmon监控工具
    方案概述nomon介绍:nmon是一种在AIX与各种Linux操作系统上广泛使用的监控与分析工具nmon能在系统运行过程中实时地捕捉系统资源的使用情况,记录的信息比较全面nmon将服务器系统资源耗用情况收集起来并输出一个特定的文件,并可利用excel分析工具(nmonanalyser)进行数据的统计分......
  • 基于springboot智能考试系统的设计与实现-计算机毕业设计源码+LW文档
    摘要随着信息技术的发展,管理系统越来越成熟,各种企事业单位使用各种类型的管理系统来提高工作效率,从而降低手工操作的弊端。我国政府一直以来都非常重视中学阶段教育的发展,近几年来学生人数逐渐增加,对在线考试的需求越来越多。因此,通过开发基于springboot智能考试系统来提高学习效......
  • 混沌测试工具ChaosBlade
    目录  Chaosblade是什么?  Chaosblade怎么用?  场景一:服务器CPU爆满  场景二:服务器磁盘爆满  场景三:调用某个Dubbo服务超时  场景四:JVM中某个方法抛出异常或者修改方法返回值  场景五:调用Mysql超时或出现异常  场景六:服务器网络缓慢 Chaosblade是什么?Chaos......
  • 详解Spring缓存注解@Cacheable、@CachePut和@CacheEvict
    详解Spring缓存注解@Cacheable、@CachePut和@CacheEvict的使用简介在大型的应用程序中,缓存是一项关键技术,用于提高系统的性能和响应速度。Spring框架提供了强大的缓存功能,通过使用缓存注解可以轻松地集成缓存机制到应用程序中。本文将详细介绍Spring框架中的@Cacheable、@CachePu......
  • Symantec GhostCast Server是一款用于网络传输和部署镜像的软件工具 Symantec GhostCa
    SymantecGhostCastServer是一款用于网络传输和部署镜像的软件工具,它提供了一组命令行选项来配置和控制其功能。以下是一些常用的SymantecGhostCastServer命令:ghostsrv-clone:启动GhostCastServer并允许克隆图像。ghostsrv-multicast:启动GhostCastServer以启用多播传......
  • springMvc页面跳转---重定向和转发
    准备工作1.导入json依赖点击查看代码<!--jsp需要依赖!jstl--><dependency><groupId>jakarta.servlet.jsp.jstl</groupId><artifactId>jakarta.servlet.jsp.jstl-api</artifactId><version>3.......
  • redisson 工具类
    importcom.alibaba.fastjson.JSON;importcom.juxiao.xchat.dao.room.dto.CallStatus;importcom.juxiao.xchat.manager.cache.redis.RedissonManager;importlombok.extern.slf4j.Slf4j;importorg.redisson.api.*;importorg.redisson.client.protocol.ScoredEntry;i......
  • CI/CD 工具和技术:释放 DevOps 的力量
    在快节奏的软件开发世界中,持续集成和持续部署(CI/CD)已成为DevOps服务中不可或缺的实践。CI/CD使团队能够更频繁、更高效、更高质量地交付软件更新。为了实现这些目标,开发人员依靠一系列尖端工具和技术来简化工作流程并自动化开发过程的各个阶段。在这篇博文中,我们将探讨有助于Dev......