注解的定义与处理

2022/7/12 23:31:05

本文主要是介绍注解的定义与处理,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

定义注解

使用 @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注解,系统不会为我们自动生成检测字段长度是否合法的逻辑,而是需要自己通过反射机制来完成注解的逻辑



参考内容
注解



这篇关于注解的定义与处理的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程