ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

注解的定义与处理

2022-07-12 16:31:10  阅读:128  来源: 互联网

标签:String 处理 value class default 注解 public 定义


定义注解

使用 @Interface定义注解

public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

注解的参数类似无参方法,可以使用default设置一个默认值,最常用的参数建议命名为value

元注解

可以修饰其他注解的注解称为元注解,对于元注解以使用为主,通常不去编写

@Target

@Target可以定义注解可以用于源码哪些位置

  • 类或接口:ElementType.TYPE
  • 字段:ElementType.FIELD
  • 方法:ElementType.METHOD
  • 构造方法:ElementType.CONSTRUCTOR
  • 方法参数:ElementType.PARAMETER

例如想要让自定义的注解可作用于方法上,可以通过这样的方式定义:

@Target({ElementType.METHOD})
public @interface Report {
    int type() default 0;
    String level() default "info";
    String value() default "";
}

@Retention

该元注解主要定义了注解的生命周期

  • 仅编译期:RetentionPolicy.SOURCE
  • 仅 class 文件:RetentionPolicy.CLASS
  • 运行期:RetentionPolicy.RUNTIME

@Override就是属于第一种,因为其主要作用是让编译器检查该方法是否重写,并不会进入 class 文件

如果@Retention不存在,则默认为 CLASS 。一般定义的注解都是 RUNTIME 的,所以需要加上@Retention(RetentionPolicy.RUNTIME)

@Repeatable

这个元注解主要是定义注解是否可以重复,用的不多

处理注解

注解本身对代码逻辑没有影响,如何使用注解由工具决定。SOURCE 类型的注解主要由编译器使用,因此我们一般只使用,不编写。CLASS 类型的注解主要由底层工具库使用,涉及到 class 的加载,一般我们很少用到。只有 RUNTIME 类型的注解不但要使用,还经常需要编写

因为注解定义后也是一种 class ,所有注解都继承自java.lang.annotation.Annotation,所以读取注解需要使用到反射

一个简单的例子

注释类

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "";
    int type() default 0;
}

实体类

@MyAnnotation(value = "Chinese",type = 1)
public class Person {
    private String name;
    private int age;
}

测试类

@SpringBootTest
public class AnnotationTest {

    @Test
    public void testMethod() {
//        方式1
//        Class<Person> personClass = Person.class;
//        if (personClass.isAnnotationPresent(MyAnnotation.class)) {
//            MyAnnotation annotation = personClass.getAnnotation(MyAnnotation.class);
//            String value = annotation.value();
//            int type = annotation.type();
//            System.out.println(value + "---" + type);
//        }
//----------------------------------------------
//        方式2
        MyAnnotation annotation = Person.class.getAnnotation(MyAnnotation.class);
        if(annotation != null){
            System.out.println(annotation.value());
            System.out.println(annotation.type());
        }
    }

}

在上面的例子中,注解是作用在类上的,作用在方法、字段和构造方法的注解处理和它大同小异,但是作用在方法参数上的注解处理起来会比较麻烦一些:因为方法参数本身可以看成一个数组,而每个参数又可以定义多个注解,所以,一次获取方法参数的所有注解就必须用一个二维数组来表示

读取方法参数的注解

方法参数注解

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface MySpeak {
    String value() default "";
    String toWho() default "nobody";
    boolean required() default true;
}

实体类

@MyAnnotation(value = "Chinese", type = 1)
public class Person {
    private String name;
    private int age;

    public void speak(
            @MySpeak(value = "hello", toWho = "tom", required = true)
            @NotNull
                    String msg
    ) {
        System.out.println(msg);
    }
}

测试类

@SpringBootTest
public class AnnotationTest {

    @Test
    public void testFunction() throws Exception{
        Class<Person> personClass = Person.class;
        // 获取方法
        Method speakMethod = personClass.getMethod("speak", String.class);
        // 获取所有参数的注解
        Annotation[][] anno = speakMethod.getParameterAnnotations();
        // anno[0]代表第一个参数的注解,anno[0][0]代表第一个参数的第一个注解
        for(Annotation a : anno[0]){
            if(a instanceof MySpeak){
                MySpeak mp = (MySpeak) a;
            }else if(a instanceof NotNull){
                NotNull nn = (NotNull) a;
            }
        }
    }

}

使用注解

注释类

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Range {
    int min() default 0;
    int max() default 255;
}

实体类

@MyAnnotation(value = "Chinese", type = 1)
@Data
@AllArgsConstructor
public class Person {
    @Range(min = 3,max = 16)
    private String name;
    @Range(max = 24)
    private String city;
    private int age;

    public void speak(
            @MySpeak(value = "hello", toWho = "tom", required = true)
            @NotNull
                    String msg
    ) {
        System.out.println(msg);
    }
}

测试类

@SpringBootTest
public class AnnotationTest {

    @Test
    public void testFunction() throws Exception {
        Person person = new Person("tom",
                "hebeishengshijiazhuangshixianhuaqu",
                18
        );
        check(person);
    }

    //注释逻辑
    public void check(Person person) throws Exception {
        //获取所有字段
        Field[] fields = Person.class.getDeclaredFields();
        for(Field f : fields){
            Range range = f.getAnnotation(Range.class);
            //判断字段上是否使用注解 @range
            if(range != null){
                //因为是私有属性所以必须设置为true
                f.setAccessible(true);
                //获取每个字段的值
                Object val = f.get(person);
                if(val instanceof String){
                    String s = (String) val;
                    //判断长度
                    if(s.length() < range.min() || s.length() > range.max()){
                        throw new RuntimeException("数据长度不合法");
                    }else{
                        System.out.println("数据长度合法");
                    }
                }
            }
        }
    }

}

输出

数据长度合法
java.lang.RuntimeException: 数据长度不合法

通过这个例子可以明显的观察到,定义了注解对实际的业务逻辑完全没有影响,还是需要靠编写代码来完成注解的逻辑。比如,定义了@Range注解,系统不会为我们自动生成检测字段长度是否合法的逻辑,而是需要自己通过反射机制来完成注解的逻辑



参考内容
注解

标签:String,处理,value,class,default,注解,public,定义
来源: https://www.cnblogs.com/colee51666/p/16470555.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有