SpringMVC自定义注解---[详细介绍]

news2024/12/23 17:57:57

一,对于SpringMVC自定义注解概念

       是一种特殊的 Java 注解,它允许开发者在代码中添加自定义的元数据,并且可以在运行时使用反射机制来获取和处理这些信息。在 Spring MVC 中,自定义注解通常用于定义控制器、请求处理方法、参数或者其他相关组件的行为

创建和使用由开发者自行编写,可以根据具体需求定义注解元素,并编写相应的注解处理器和逻辑来处理注解可用于标记特定的类、方法或参数

1.2 java注解的分类(主三种)

基本注解:

@Override:用于标记一个方法覆盖了父类或接口的方法。

@Deprecated用于标记一个类、方法或字段已经过时,不推荐使用。

@SuppressWarnings(value = "unchecked")用于抑制编译器产生的警告信息

元注解:

@Retention指定注解的生命周期,包括 RetentionPolicy.SOURCERetentionPolicy.CLASS 和 RetentionPolicy.RUNTIME

@Target指定注解的应用目标,包括 ElementType.TYPEElementType.FIELD

ElementType.METHOD 等。

@Documented指示注解是否包含在 Java 文档中。

@Inherited指示注解是否可以被继承。

@Repeatable指示注解可以多次应用于同一元素。

注:可以指定多个位置,例如:
@Target({ElementType.METHOD, ElementType.TYPE}),也就是此注解可以在方法和类上面使用

@Inherited:指定被修饰的Annotation将具有继承性

@Documented:指定被修饰的该Annotation可以被javadoc工具提取成文档.

自定义注解:

注解分类(根据Annotation是否包含成员变量,可以把Annotation分为两类):

标记Annotation:
没有成员变量的Annotation; 这种Annotation仅利用自身的存在与否来提供信息

元数据Annotation:
包含成员变量的Annotation; 它们可以接受(和提供)更多的元数据;

注意:自定义注解只是一种提供元数据的机制,具体的处理逻辑需要通过注解处理器来实现。在使用自定义注解时,需要编写注解处理器来解析注解,并执行相应的行为。

1.3 如何使用自定义注解

使用@interface关键字, 其定义过程与定义接口非常类似, 需要注意的是:
Annotation的成员变量在Annotation定义中是以无参的方法形式来声明的, 其方法名和返回值类型定义了该成员变量的名字和类型。而且我们还可以使用default关键字为这个成员变量设定默认值

二,使用自定义注解

案例一(获取类与方法上的注解值)

定义一个Package名为annotation包定义TranscationModel类

package com.Bingzy.annotation;

public enum  TranscationModel {
    Read, Write, ReadWrite;
    private Integer id;
    private String name;
    public void init1(){
        Read.id=1;
        Read.name="zs";
    }

    public void init2(){
        Write.id=2;
        Write.name="lisi";
    }

    public void init3(){
        ReadWrite.id=3;
        ReadWrite.name="wangwu";
    }
}

在annotation包下再创建测试类test01:

package com.Bingzy.annotation;

/**
 * @Name BingBing
 * @company zking cy
 * @create 2023-09-14-19:43
 */
public class Test01 {

    public static void main(String[] args) {
        System.out.println(TranscationModel.Read);
        System.out.println(TranscationModel.Write);
        System.out.println(TranscationModel.ReadWrite);
    }
}

在annotation包下再创建一个Package包Demo01 方便测试案例一:

创建三个注解类:

MyAnnotation1类:

@Target({ElementType.TYPE, ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation1 {
    String name();
}

MyAnnotation2类:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation2 {
    TranscationModel model() default TranscationModel.ReadWrite;
}

MyAnnotation3类:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MyAnnotation3 {
    TranscationModel[] models() default TranscationModel.ReadWrite;
}

在Demo01包下创建测试类代码

Demo1:

@MyAnnotation1(name = "abc")
public class Demo1 {

    @MyAnnotation1(name = "xyz")
    private Integer age;

    @MyAnnotation2(model = TranscationModel.Read)
    public void list() {
        System.out.println("list");
    }

    @MyAnnotation3(models = {TranscationModel.Read, TranscationModel.Write})
    public void edit() {
        System.out.println("edit");
    }
}

Demo1Test:

public class Demo1Test {
    @Test
    public void list() throws Exception {
//        获取类上的注解
        MyAnnotation1 annotation1 = Demo1.class.getAnnotation(MyAnnotation1.class);
        System.out.println(annotation1.name());//abc

//        获取方法上的注解
        MyAnnotation2 myAnnotation2 = Demo1.class.getMethod("list").getAnnotation(MyAnnotation2.class);
        System.out.println(myAnnotation2.model());//Read

//        获取属性上的注解
        MyAnnotation1 myAnnotation1 = Demo1.class.getDeclaredField("age").getAnnotation(MyAnnotation1.class);
        System.out.println(myAnnotation1.name());// xyz
    }

    @Test
    public void edit() throws Exception {
        MyAnnotation3 myAnnotation3 = Demo1.class.getMethod("edit").getAnnotation(MyAnnotation3.class);
        for (TranscationModel model : myAnnotation3.models()) {
            System.out.println(model);//Read,Write
        }
    }
}

测试结果:

案例二(获取类属性上的注解属性值)

注解类:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface TestAnnotation {
    String value() default "默认value值";

    String what() default "这里是默认的what属性对应的值";
}

测试类Demo2类

public class Demo2 {
    @TestAnnotation(value = "这就是value对应的值_msg1", what = "这就是what对应的值_msg1")
    private static String msg1;

    @TestAnnotation("这就是value对应的值1")
    private static String msg2;

    @TestAnnotation(value = "这就是value对应的值2")
    private static String msg3;

    @TestAnnotation(what = "这就是what对应的值")
    private static String msg4;
}

Demo2Test:

public class Demo2Test {
    @Test
    public void test1() throws Exception {
        TestAnnotation msg1 = Demo2.class.getDeclaredField("msg1").getAnnotation(TestAnnotation.class);
        System.out.println("test1");
        System.out.println(msg1.value());
        System.out.println(msg1.what());
    }


    @Test
    public void test2() throws Exception{
        TestAnnotation msg2 = Demo2.class.getDeclaredField("msg2").getAnnotation(TestAnnotation.class);
        System.out.println("test2");
        System.out.println(msg2.value());
        System.out.println(msg2.what());
    }

    @Test
    public void test3() throws Exception{
        TestAnnotation msg3 = Demo2.class.getDeclaredField("msg3").getAnnotation(TestAnnotation.class);
        System.out.println("test3");
        System.out.println(msg3.value());
        System.out.println(msg3.what());
    }

    @Test
    public void test4() throws Exception{
        TestAnnotation msg4 = Demo2.class.getDeclaredField("msg4").getAnnotation(TestAnnotation.class);
        System.out.println("test4");
        System.out.println(msg4.value());
        System.out.println(msg4.what());
    }
}

总结:如果我们注解上没有指定是value还是what默认就是value,如果只想转递一个参数又不想默认是value那就需要指定what=""即可。 

 案例三(获取参数修饰注解对应的属性值)

注解类:

@Documented
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface IsNotNull {
    boolean value() default false;
}

测试类Demo3:

public class Demo3 {

    public void hello1(@IsNotNull(true) String name) {
        System.out.println("hello:" + name);
    }

    public void hello2(@IsNotNull String name) {
        System.out.println("hello:" + name);
    }
}

测试类DemoTest3:

public class Demo3Test {

    @Test
    public void hello1() throws Exception {
        Demo3 demo3 = new Demo3();
        for (Parameter parameter : demo3.getClass().getMethod("hello1", String.class).getParameters()) {
            IsNotNull annotation = parameter.getAnnotation(IsNotNull.class);
            if(annotation != null){
                System.out.println(annotation.value());//true
            }
        }
    }

    @Test
    public void hello2() throws Exception {
        Demo3 demo3 = new Demo3();
        for (Parameter parameter : demo3.getClass().getMethod("hello2", String.class).getParameters()) {
            IsNotNull annotation = parameter.getAnnotation(IsNotNull.class);
            if(annotation != null){
                System.out.println(annotation.value());//false
            }
        }
    }

    @Test
    public void hello3() throws Exception {
//        模拟浏览器传递到后台的参数 解读@requestParam
        String name = "zs";
        Demo3 demo3 = new Demo3();
        Method method = demo3.getClass().getMethod("hello1", String.class);
        for (Parameter parameter : method.getParameters()) {
            IsNotNull annotation = parameter.getAnnotation(IsNotNull.class);
            if(annotation != null){
                System.out.println(annotation.value());//true
                if (annotation.value() && !"".equals(name)){
                    method.invoke(demo3,name);
                }
            }
        }
    }
}

运行结果: 

 

总结:这个案例像不像我们的注解@RequestParam必须传递参数,除非指定required为false就可与不传参数,我们这个案例也是如此。 

三,Aop自定义注解

定义一个注解类:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
    String desc();
}

切面类:

@Component
@Aspect
public class MyLogAspect {
    private static final Logger logger = LoggerFactory.getLogger(MyLogAspect.class);

    /**
     * 只要用到了com.javaxl.p2.annotation.springAop.MyLog这个注解的,就是目标类
     */
    @Pointcut("@annotation(com.Bingzy.annotation.aop.MyLog)")
    private void MyValid() {
    }

    @Before("MyValid()")
    public void before(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        logger.debug("[" + signature.getName() + " : start.....]");
        System.out.println("[" + signature.getName() + " : start.....]");

        MyLog myLog = signature.getMethod().getAnnotation(MyLog.class);
        logger.debug("【目标对象方法被调用时候产生的日志,记录到日志表中】:"+myLog.desc());
        System.out.println("【目标对象方法被调用时候产生的日志,记录到日志表中】:" + myLog.desc());
    }
}

Controller层:

@Controller
public class LogController {
     @RequestMapping("/mylog")
    @MyLog(desc = "这是结合spring aop知识,讲解自定义注解应用的一个案例")
    public void testLogAspect(){
        System.out.println("这里随便来点啥");
    }
}

运行结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1012669.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux CentOS7系统运行级别

运行级别就是Linux操作系统当前正在运行的功能级别。在早期系统设置中,共设有七个运行级别,编号从0到6。系统可以引导到任何给定的运行级别。 每个运行级别指定不同的系统配置,并允许访问不同的进程组合。默认情况下,Linux会引导…

[C++ 网络协议] 多种I/O函数

1. Linux的send&recv函数 1.1 send函数和recv函数 #include <sys/socket.h> ssize_t send( int sockfd, //套接字文件描述符 const void* buf, //保存待传输数据的缓冲地址值 size_t nbytes, //待传输的字节数 int flags …

Layui快速入门之第六节 选项卡

目录 一&#xff1a;基本概念 选项卡依赖element模块 API 元素属性 基本使用&#xff1a; 二&#xff1a;选项卡风格 默认风格 简约风格 ​编辑 卡片风格 三&#xff1a;hash 状态匹配 四&#xff1a;tab相关操作 渲染 tab 添加 tab 删除 tab 切换 tab 自定义…

华为云云耀云服务器L实例评测 | 基于docker部署nacos2.2.3服务

#【有奖征文】华为云云服务器焕新上线&#xff0c;快来亲身感受评测吧&#xff01;# &#x1f468;‍&#x1f393;博主简介 &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&…

C++之哈希表、哈希桶的实现

哈希表、哈希桶的实现 哈希概念哈希冲突哈希函数哈希冲突解决闭散列哈希表闭散列实现哈希表的结构哈希表的插入哈希表的查找哈希表的删除 开散列开散列概念哈希表的结构哈希表的插入哈希表的查找哈希表的删除 哈希概念 顺序结构以及平衡树中&#xff0c;元素关键码与其存储位置…

Postman使用_Tests Script(断言测试)

断言测试可以在Collection、Folder和Request的 pre-request script 和 test script中编写&#xff0c;测试脚本可以检测请求响应的各个方面&#xff0c;包括正文、状态代码、头、cookie、响应时间等&#xff0c;只有测试符合自定义的要求后才能通过。 pm对象提供了测试相关功能…

LiveNVR监控流媒体Onvif/RTSP功能-支持海康摄像头海康NVR通过EHOME协议ISUP协议接入分发视频流或是转GB28181

LiveNVR支持海康NVR摄像头通EHOME接入ISUP接入LiveNVR分发视频流或是转GB28181 1、海康 ISUP 接入配置2、海康设备接入2.1、海康EHOME接入配置示例2.2、海康ISUP接入配置示例 3、通道配置3.1、直播流接入类型 海康ISUP3.2、海康 ISUP 设备ID3.3、启用保存3.4、接入成功 4、相关…

SadTalker 让图片说话

参考&#xff1a;https://github.com/OpenTalker/SadTalker 其他类似参考&#xff1a;https://www.d-id.com/ 输入图片加音频产生2d视频 安装使用 1、拉取github&#xff0c;下载对应安装库 2、下载对应模型baidu网盘 新建checkpoints&#xff0c;把下载sadtalker里模型拷贝进…

Docker 的使用

一、Docker 的作用和优势 软件集装箱化平台&#xff0c;可让开发者构建应用程序时&#xff0c;将它与环境一起打包到一个容器中&#xff0c;发布应用到任意平台中。 能在单台机器上运行多个Docker微容器&#xff0c;而每个微容器里都有一个微服务或独立应用&#xff0c; 如&am…

汽车行业新闻稿怎么写?怎么写关于汽车的新闻稿?

撰写汽车行业新闻稿需要遵循一定的结构和要点&#xff0c;以确保内容准确、清晰&#xff0c;并能吸引读者的兴趣。以下是关于汽车的新闻稿的一些写作要点和建议&#xff0c;接下来伯乐网络传媒就来给大家分享一下&#xff1a; 标题醒目&#xff1a;新闻稿的标题应该简洁明了&am…

多视角姿势估计:TEMPO: Efficient Multi-View Pose Estimation, Tracking, and Forecasting

论文作者&#xff1a;Rohan Choudhury,Kris Kitani,Laszlo A. Jeni 作者单位&#xff1a;Carnegie Mellon University 论文链接&#xff1a;http://arxiv.org/abs/2309.07910v1 内容简介&#xff1a; 1&#xff09;方向&#xff1a;多视角姿势估计模型 2&#xff09;应用&…

$value$plusargs字符串参数传递后如何随机

文章目录 前言一、背景二、解决办法总结 前言 在仿真过程中&#xff0c;经常在命令行通过$value$plusargs传递一个字符串到环境中&#xff0c;去选择不同的sequence&#xff0c;但是&#xff0c;有些时候需要随机选择其中某几个seq&#xff0c;而只有整数和枚举类型可以随机&a…

Ubuntu20.04安装Nvidia显卡驱动、CUDA11.3、CUDNN、TensorRT、Anaconda、ROS/ROS2

1.更换国内源 打开终端&#xff0c;输入指令&#xff1a; wget http://fishros.com/install -O fishros && . fishros 选择【5】更换系统源&#xff0c;后面还有一个要输入的选项&#xff0c;选择【0】退出&#xff0c;就会自动换源。 2.安装NVIDIA驱动 这一步最痛心…

腾讯mini项目-【指标监控服务重构】2023-08-03

今日已办 a&#xff0c;b两组的trace放到一个分支里 可以看到先前的没看到的 profile trace 的耗时&#xff0c;是由于时间跨度较长&#xff0c;没有滑动到 trace 末尾 明日待办 组长会议汇报项目进度和问题

我跟面试官说MySQL单表数据量不要超过两千万,面试官不信

&#x1f449;导读 作为一个合格的 DBA&#xff0c;在遇到线上单表数据量超过千万级别的时候&#xff0c;往往会建议用户通过分表来缩减单表数据量&#xff0c;当用户问为什么单表数据量不能超过千万时&#xff0c;DBA 往往会说&#xff1a;单表数据量超过千万&#xff0c;会影…

搜好货API接口解析,实现获得搜好货商品详情

要解析搜好货API接口并实现获取搜好货商品详情&#xff0c;你需要按照以下步骤进行操作&#xff1a; 了解搜好货开放平台&#xff1a;访问搜好货开放平台官网&#xff0c;找到API接口相关的开发者文档、指南等信息。注册开发者账号&#xff1a;在搜好货开放平台上注册一个开发…

C#开发的OpenRA游戏之调试菜单2

C#开发的OpenRA游戏之调试菜单2 前面已经分析了怎么样通过选项参数来打开这个调试界面,当创建游戏之后,从游戏里选择参数按钮,就会弹出下面的界面,如果没有选择调试参数是没有Debug这一页选项卡: 上面调试的选项是非常有用的,否则不能快速地测试游戏,不能快速地开发新的…

列表页面新增 字段查询 ,点击查询后,前端页面和后端控制台 出现红色报错信息,查询数据失败。

项目场景&#xff1a; 项目场景简述&#xff1a; 列表页面新增 字段查询 &#xff0c;点击查询后&#xff0c;前端页面和后端控制台 出现红色报错信息&#xff0c;查询数据失败。 问题描述 问题描述&#xff1a; <el-select v-model"dataForm.engineerId" clea…

类和对象(4)

文章目录 1. C/C内存分布2. C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free3.C内存管理方式new/delete3.1 new/delete操作内置类型3.2 new/delete操作自定义类型 4. operator new和operator delete函数&#xff08;重点&#xff09;4.1底层原理 5.malloc/free…

24届双非本科找工作到底有多难

记录一下自己的找工作过程&#xff0c;今天笔试失败感觉整个人都颓废了(那家公司真的是我的目标情司&#xff0c;呜呜呜&#xff0c;感觉我是废物)。 再加上不考虑出省&#xff0c;感觉找工作太难了 介绍一下自己情况&#xff1a;三个省奖&#xff08;开发类的&#xff0c;负责…