• SpringMVC之自定义注解


    目录

    一.什么是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)自定义三个注解

    1. package com.YU.annotation.demo;
    2. import java.lang.annotation.*;
    3. /**
    4. * MyAnnotation1注解可以用在类、接口、属性、方法上
    5. * 注解运行期也保留
    6. * 不可继承
    7. */
    8. @Target({ElementType.TYPE, ElementType.FIELD,ElementType.METHOD})
    9. @Retention(RetentionPolicy.RUNTIME)
    10. @Documented
    11. public @interface MyAnnotation1 {
    12. String name();
    13. }
    1. package com.YU.annotation.demo;
    2. import java.lang.annotation.*;
    3. /**
    4. * MyAnnotation2注解可以用在方法上
    5. * 注解运行期也保留
    6. * 不可继承
    7. */
    8. @Target(ElementType.METHOD)
    9. @Retention(RetentionPolicy.RUNTIME)
    10. @Documented
    11. public @interface MyAnnotation2 {
    12. TranscationModel model() default TranscationModel.ReadWrite;
    13. }
    1. package com.YU.annotation.demo;
    2. import java.lang.annotation.*;
    3. /**
    4. * @author YU
    5. * @time 2023/9/14 18:52:55
    6. *
    7. * MyAnnotation3注解可以用在方法上
    8. * 注解运行期也保留
    9. * 可继承
    10. */
    11. @Target(ElementType.METHOD)
    12. @Retention(RetentionPolicy.RUNTIME)
    13. @Inherited
    14. @Documented
    15. public @interface MyAnnotation3 {
    16. TranscationModel[] models() default TranscationModel.ReadWrite;
    17. }

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

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

    1. @Test
    2. public void list() throws Exception {
    3. // 获取类上的注解
    4. MyAnnotation1 annotation1 = Demo1.class.getAnnotation(MyAnnotation1.class);
    5. System.out.println(annotation1.name());//abc
    6. // 获取方法上的注解
    7. MyAnnotation2 myAnnotation2 = Demo1.class.getMethod("list").getAnnotation(MyAnnotation2.class);
    8. System.out.println(myAnnotation2.model());//Read
    9. // 获取属性上的注解
    10. MyAnnotation1 myAnnotation1 = Demo1.class.getDeclaredField("age").getAnnotation(MyAnnotation1.class);
    11. System.out.println(myAnnotation1.name());// xyz
    12. }

    Demo1

    1. package com.YU.annotation.demo1;
    2. /**
    3. * @author YU
    4. * 获取类与方法上的注解值
    5. */
    6. @MyAnnotation1(name = "abc")
    7. public class Demo1 {
    8. @MyAnnotation1(name = "xyz")
    9. public Integer age;
    10. public Integer getAge() {
    11. return age;
    12. }
    13. @MyAnnotation2(model = TranscationModel.Read)
    14. public void list() {
    15. System.out.println("list");
    16. }
    17. @MyAnnotation3(models = {TranscationModel.Read, TranscationModel.Write})
    18. public void edit() {
    19. System.out.println("edit");
    20. }
    21. }

    测试结果

     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获取类属性上的注解属性值

    首先我们定义一个注解

    1. package com.YU.annotation.demo2;
    2. import java.lang.annotation.ElementType;
    3. import java.lang.annotation.Retention;
    4. import java.lang.annotation.RetentionPolicy;
    5. import java.lang.annotation.Target;
    6. /**
    7. * @author YU
    8. */
    9. //@Retention(RetentionPolicy.SOURCE)
    10. @Retention(RetentionPolicy.RUNTIME)
    11. @Target(ElementType.FIELD)
    12. public @interface TestAnnotation {
    13. String value() default "默认value值";
    14. String what() default "这里是默认的what属性对应的值";
    15. }

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

    1. package com.YU.annotation.demo2;
    2. /**
    3. * @author YU
    4. *
    5. * 获取类属性上的注解属性值
    6. */
    7. public class Demo2 {
    8. @TestAnnotation(value = "这就是value对应的值_msg1", what = "这就是what对应的值_msg1")
    9. private static String msg1;
    10. @TestAnnotation("这就是value对应的值1")
    11. private static String msg2;
    12. @TestAnnotation(value = "这就是value对应的值2")
    13. private static String msg3;
    14. @TestAnnotation(what = "这就是what对应的值")
    15. private static String msg4;
    16. }

    测试结果得出的结论为

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

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

    (1)定义一个注解

    1. package com.YU.annotation.demo3;
    2. import java.lang.annotation.*;
    3. /**
    4. * @author YU
    5. *
    6. * 非空注解:使用在方法的参数上,false表示此参数可以为空,true不能为空
    7. */
    8. @Documented
    9. @Target({ElementType.PARAMETER})
    10. @Retention(RetentionPolicy.RUNTIME)
    11. public @interface IsNotNull {
    12. boolean value() default false;
    13. }

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

    1. package com.YU.annotation.demo3;
    2. /**
    3. * @author YU
    4. *
    5. * 获取参数修饰注解对应的属性值
    6. */
    7. public class Demo3 {
    8. public void hello1(@IsNotNull(true) String name) {
    9. System.out.println("hello:" + name);
    10. }
    11. public void hello2(@IsNotNull String name) {
    12. System.out.println("hello:" + name);
    13. }
    14. }

     (3)编写测试方法

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

    测试方法解读:

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

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

    三.aop自定义注解

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

    1. <dependency>
    2. <groupId>org.springframework.bootgroupId>
    3. <artifactId>spring-boot-starter-aopartifactId>
    4. dependency>

    2.创建自定义注解

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

    1. @Documented
    2. @Target({ElementType.PARAMETER})
    3. @Retention(RetentionPolicy.RUNTIME)
    4. public @interface IsNotNull {
    5. boolean value() default false;
    6. }

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

    1. @Component
    2. @Aspect
    3. public class MyLogAspect {
    4. private static final Logger logger = LoggerFactory.getLogger(MyLogAspect.class);
    5. /**
    6. * 只要用到了com.javaxl.p2.annotation.springAop.MyLog这个注解的,就是目标类
    7. */
    8. @Pointcut("@annotation(com.YU.annotation.aop.MyLog)")
    9. private void MyValid() {
    10. }
    11. @Before("MyValid()")
    12. public void before(JoinPoint joinPoint) {
    13. MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    14. logger.debug("[" + signature.getName() + " : start.....]");
    15. System.out.println("[" + signature.getName() + " : start.....]");
    16. MyLog myLog = signature.getMethod().getAnnotation(MyLog.class);
    17. logger.debug("【目标对象方法被调用时候产生的日志,记录到日志表中】:"+myLog.desc());
    18. System.out.println("【目标对象方法被调用时候产生的日志,记录到日志表中】:" + myLog.desc());
    19. }
    20. }

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

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

  • 相关阅读:
    python使用memory_profiler分析代码运行内存占用
    安装CDH配置本地CM、CDH源时,配置Apache Web服务器一直显示403看不到目录
    juc之常用4大并发工具类 (四)
    C++基础教程(转载)
    RabbitMQ_概述
    RK3399应用开发 | 编译安装 mesa 3D 图形库(23.0.0)
    Elastic Stack--09--ElasticsearchRestTemplate
    LeetCode:207. 课程表、210. 课程表 II(拓扑排序 C++)
    kubernetes-Service
    14道高频手写JS面试题及答案,巩固你的JS基础
  • 原文地址:https://blog.csdn.net/weixin_73320743/article/details/132888052