SpringMVC之自定义注解

2023-09-15 10:47:33

目录

一.什么是Java注解

1.简介

2.注解的分类 

3.JDK元注解

 二.自定义注解

1.自定义注解的分类

        1.1.标记Annotation:

        1.2.元数据Annotation:

2.如何使用自定义注解

3.案例演示

3.1 获取类、方法及属性上的注解值 

 3.2@Inherited 的使用

3.3获取类属性上的注解属性值

3.4获取参数修饰注解对应的属性值

三.aop自定义注解

1.导入aop自定义注解需要的Maven依赖

2.创建自定义注解


一.什么是Java注解

1.简介

Java注解是一种附加在代码中的元数据,它提供了关于代码的额外信息。注解可以在类、方法、字段、参数等各个层面上使用,用于描述代码的特性、行为、要求等。注解是在编译时被处理的,并且可以被编译器、工具或运行时框架解析和利用。

Java注解的语法形式以"@"符号开头,紧跟着注解的名称和可选的参数列表。注解可以包含元素,类似于接口的成员。这些元素可以具有默认值,也可以在使用注解时显式地指定值。

2.注解的分类 

  1. 提供信息给编译器:可以使用注解在源代码级别嵌入信息,这些信息可以被编译器解析和利用。例如,@Deprecated注解用于标记过时的代码,编译器会在编译时生成警告信息。

  2. 编译时进行额外的处理:某些注解可以触发编译时的额外操作。例如,@Entity注解用于标记实体类,编译器可以根据这个注解生成与数据库表的映射代码。

  3. 运行时的处理:某些注解在程序运行时可以被解析和处理。这些注解可以通过反射机制在运行时获取并执行相关操作。例如,Spring框架使用注解来配置依赖注入、AOP等特性。

3.JDK元注解

JDK提供了一些元注解(meta-annotation),它们是用于定义和配置自定义注解的注解。元注解可以应用于自定义注解上,用于指定注解的行为和特性。以下是JDK中常用的元注解:

  1. @Retention:指定注解的保留策略,即注解在何时生效。常用的保留策略有三种:

    • RetentionPolicy.SOURCE:注解仅在源代码中保留,编译器在编译时会忽略它们。
    • RetentionPolicy.CLASS:注解在编译时会被保留在字节码文件中,但在运行时不可获取。这是默认的保留策略。
    • RetentionPolicy.RUNTIME:注解在运行时保留,可以通过反射机制获取和解析注解信息。
  2. @Target:指定注解可以应用的目标元素类型,包括以下一些常见的目标类型(ElementType):

    • ElementType.TYPE:类、接口、枚举类型
    • ElementType.FIELD:字段、枚举常量
    • ElementType.METHOD:方法
    • ElementType.PARAMETER:方法参数
    • ElementType.CONSTRUCTOR:构造方法
    • ElementType.LOCAL_VARIABLE:局部变量
    • ElementType.ANNOTATION_TYPE:注解类型
    • ElementType.PACKAGE:包
  3. @Documented:指定注解是否包含在生成的Java文档中。如果一个注解被标记为@Documented,在生成文档时将包含该注解的信息。

  4. @Inherited:指定注解是否可以被继承。如果一个注解被标记为@Inherited,则表示子类会继承父类的注解。

除了以上元注解,JDK还提供了其他几个元注解,如@Repeatable(指定注解是否可重复应用于同一元素)和@Native(指定注解与本地代码关联)等。这些元注解为自定义注解的设计和行为提供了灵活性和可扩展性。

使用元注解可以通过注解注解(annotate the annotation)的方式来配置自定义注解的行为和特性,从而实现更加灵活和强大的注解功能。

 二.自定义注解

1.自定义注解的分类

        1.1.标记Annotation:

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

        1.2.元数据Annotation:

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

2.如何使用自定义注解

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

3.案例演示

3.1 获取类、方法及属性上的注解值 

(1)自定义三个注解

package com.YU.annotation.demo;

import java.lang.annotation.*;

/**
 * MyAnnotation1注解可以用在类、接口、属性、方法上
 * 注解运行期也保留
 * 不可继承
 */
@Target({ElementType.TYPE, ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation1 {
    String name();
}
package com.YU.annotation.demo;

import java.lang.annotation.*;

/**
 *  MyAnnotation2注解可以用在方法上
 *  注解运行期也保留
 *  不可继承
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAnnotation2 {
    TranscationModel model() default TranscationModel.ReadWrite;
}
package com.YU.annotation.demo;

import java.lang.annotation.*;

/**
 * @author YU
 * @time 2023/9/14 18:52:55
 *
 * MyAnnotation3注解可以用在方法上
 * 注解运行期也保留
 * 可继承
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MyAnnotation3 {
    TranscationModel[] models() default TranscationModel.ReadWrite;
}

其中的JDK元注解作用在前面都有介绍

这里我们编写测试方法,尝试获取类、方法及属性上的注解值 

@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
    }

Demo1

package com.YU.annotation.demo1;

/**
 * @author YU
 * 获取类与方法上的注解值
 */
@MyAnnotation1(name = "abc")
public class Demo1 {

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

    public Integer getAge() {
        return 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");
    }
}

测试结果

 3.2@Inherited 的使用

还是根据上述提供的代码,新建一个demo2的类去继承demo1,当我们在使用demo2去获取注解值的时候,因为MyAnnotation1和MyAnnotation2并没有使用@Inherited,所以在测试时,获取不到MyAnnotation1和MyAnnotation2的的属性值

(1)当我们运行测试方法时,demo1中的MyAnnotation1没有使用@Inherited,测试时报出空指针异常

(2)但是在MyAnnotation1中加上@Inherited后,我们就可以通过demo2去获取属性中的注解值

所以回到我们最开始@Inherited的作用

@Inherited:指定注解是否可以被继承。如果一个注解被标记为@Inherited,则表示子类会继承父类的注解。

结论:类继承类时,只有被指定@Inherited的注解才能被继承使用

3.3获取类属性上的注解属性值

首先我们定义一个注解

package com.YU.annotation.demo2;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

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

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

然后使用注解,获取注解上的属性值

package com.YU.annotation.demo2;

/**
 * @author YU
 *
 * 获取类属性上的注解属性值
 */
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;
}

测试结果得出的结论为

当我们同时为两个属性都设置值时,那么获取到的都为设置值,当我们没有去指定为哪个属性赋值时,但是又传了属性值,那么默认会给第一个设置属性值,当我们没有去设置属性值时,获取到的为注解中默认的属性值

3.4获取参数修饰注解对应的属性值

(1)定义一个注解

package com.YU.annotation.demo3;

import java.lang.annotation.*;

/**
 * @author YU
 *
 * 非空注解:使用在方法的参数上,false表示此参数可以为空,true不能为空
 */
@Documented
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface IsNotNull {
    boolean value() default false;
}

(2)通过获取参数修饰注解对应的属性值

package com.YU.annotation.demo3;

/**
 * @author YU
 *
 * 获取参数修饰注解对应的属性值
 */
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);
    }
}

 (3)编写测试方法

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

测试方法解读:

当我们在进行测试时,先根据getMethod获取方法的返回值,将定义好的属性值传入(实际开发为前端返回的数据),通过判断传入的属性值来返回不同的结果,例如:

传入的值为“zs” 参数修饰注解的属性值为true,当两个条件同时满足时,将参数进行传递并调用业务方法

三.aop自定义注解

1.导入aop自定义注解需要的Maven依赖

<dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.创建自定义注解

首先,在代码中创建一个用于 AOP 的自定义注解。自定义注解应使用 @interface 关键字进行定义,并可以包含一些元数据

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

在创建好注解后,我们要相应得去创建一个切面类,增加切入点和增强逻辑

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

    /**
     * 只要用到了com.javaxl.p2.annotation.springAop.MyLog这个注解的,就是目标类
     */
    @Pointcut("@annotation(com.YU.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());
    }
}

在上述示例中,使用 @Pointcut 注解定义了一个切入点MyValid(),并使用 @Before 注解分别在切入点前后执行增强逻辑

 今天的学习到这里就结束了,感谢各位大大的观看,各位大大的三连是博主更新的动力,感谢谢谢谢谢谢谢谢谢各位的支持!!!!! 

更多推荐

【ES6知识】 Reflect 与 Proxy

文章目录前言一、Proxy代理对象1.1基本应用1.2同一个拦截器函数,可以设置拦截多个操作:1.3Proxy支持的拦截操作一览,一共13种:二、Reflect对象2.1基本使用2.2`Reflect`对象一共有13个静态方法三、使用Proxy实现观察者模式前言Proxy与Reflect是ES6为了操作对象引入的API

源码编译Qt 5.15.9+msvc2019

官方文档里给出了详细步骤:BuildingQtSourcesBuildingQt5fromGit(Wiki)注:本文基于windows11+vs2019x64+qt5.15.9,不编译QtWebEngine归纳总结如下:准备阶段QtforWindows-Requirements安装python,我这里用的是anacon

如何在Vue中引入video.js,并如何监听相关事件,禁止拖拽

如何在Vue中引入video.js,并如何监听相关事件最近考虑做一个视频播放网站,所以接触video.js会多一些,之前考虑到使用Vue-video-player来实现相关功能,结果发现当前技术已不再支持Flash播放器,无奈采用videojs,官方文档链接奉上Video.js1.Vue使用Video.jsa.引入vi

PDF文件的页眉页脚无法删除的原因和三种替代方法

大家好!今天六分职场为大家介绍一个PDF的常用操作。有的时候我们需要为PDF文件添加页眉页脚,但如果我们这个PDF文档是从其他地方参考的,经常会发现无法直接编辑或者删除PDF文件中页眉页脚。不用担心,我们使用WPS的PDF软件,有两种替代的方法可以删除PDF的页眉页脚,然后插入自己的页眉页脚。一、PDF页眉页脚无法删除

Windows系统部署WebDAV服务结合内网穿透实现公网访问,轻松共享文件与资源

windows搭建WebDAV服务,并内网穿透公网访问【无公网IP】文章目录windows搭建WebDAV服务,并内网穿透公网访问【无公网IP】1.安装IIS必要WebDav组件2.客户端测试3.cpolar内网穿透3.1打开Web-UI管理界面3.2创建隧道3.3查看在线隧道列表3.4浏览器访问测试4.安装Raidr

分布式事物【XA强一致性分布式事务实战、分布式架构的理论知识、TCC核心组成】(六)-全面详解(学习总结---从入门到深化)

目录XA强一致性分布式事务实战_业务层实现分布式架构的理论知识_BASE理论BASE理论三要素分布式事务解决方案_最终一致性分布式事务最终一致性分布式事务解决方案_TCC是什么TCC核心组成Hmily实现TCC分布式事务实战_认识Hmily-TCCHmily实现TCC分布式事务实战_业务场景介绍XA强一致性分布式事务实

leetcode题目分析(一)leetcode155最小栈

一、前言本题基于leetcode155最小栈这道题,说一下通过java解决的一些方法。需要尤其注意的是,此题输入的值的区间范围在-2^31<=val<=2^31-1.这将会影响我们最后一种最优解的结果出现问题。这些都是后话。二、解决思路其实在一开始的提交记录,我的方案忽略了题干中的常数时间,而是使用了偏向于工程的,将栈

经典指标策略回测一览

编辑经典指标策略回测一览关键词A股市场(沪深京三市)5000+股票20年内日线走势回测,区分除权,前复权,后复权三种模式;由于数据量较大,采用两种方式共享数据,一是天启网站的数据表格可视化,而是phpadmin数据管理台查看(自行SQL查询),登录方式在最下方。1、天启平台登录网站地址天启量化交易平台http://ma

redisplusplus笔记

设计点在redis层使用函数作为模板类型参数,也就是command层定义的函数。template<typenameCmd,typename...Args>autoRedis::command(Cmdcmd,Args&&...args)->typenamestd::enable_if<!std::is_convertib

算法通关18关 | 回溯模板如何解决复原IP问题

18关的前几篇文章看过之后,对回溯的模板问题基本解题思路就知道了,就是固定的for循环问题,外层for循环控制横向,递归控制纵向,还要考虑撤销操作和元素是否能被重复利用问题,重复利用的情景较少,只用注意撤销就行。1.复原IP地址题目经典题目,LeetCode93有效IP地址正好由四个整数(每个整数位于0到255之间组成

SpringMVC初级

文章目录一、SpringMVC概述二、springMVC步骤1、新建maven的web项目2、导入maven依赖3、创建controller4、创建spring-mvc.xml配置文件(本质就是spring的配置件)5、web.xml中配置前端控制器6、新建a.jsp文件7、配置tomcat8、启动测试三、工作流程分析

热文推荐