HOME/Articles/

Java注解

Article Outline

Java注解

最近在学习Java和反射相关的知识,看到了注解的部分,感觉也是一个非常有用的部分,特意提出来记录一下。

自定义注解的定义


@Retention(RetentionPolicy.RUNTIME)
public @interface XYC {
    String value();

就像类用class声明一样,注解用@interface来声明;

元注解:

在编写自定义注解的过程中,一定会或多或少的用到元注解,元注解简单的理解就是Java语言中自带的,用于定义其他注解的注解。

从JDK 1.5开始, Java增加了对元数据(MetaData)的支持,也就是 Annotation(注解)。除了直接使用JDK 定义好的注解,我们还可以自定义注解,在JDK 1.5中提供了4个标准的用来对注解类型进行注解的注解类,我们称之为 meta-annotation(元注解)

@Target

@Target注解的作用是描述注解的使用范围(注解可以用在什么地方),通过枚举,一共规定了以下的取值,

  • TYPE 用于类、接口、枚举类
  • FIELD 用于成员变量(枚举变量)
  • METHOD 用于成员方法
  • PARAMETER 用于方法参数
  • CONSTRUCTOR 用于构造方法
  • LOCAL_VARIABLE 用于局部变量
  • ANNOTATION_TYPE 用于注解类
  • PACKAGE 用于包
  • TYPE_PARAMETER 类型参数 JDK1.8中新增
  • TYPE_USE JDK1.8新增,用于任何地方

@Retention注解

@Retention注解的作用是: 描述注解保留的时间范围,用来限定那些被它注解的注解类在注解在其他类上以后,可被保留到何时,通过枚举规定,共 三种:

  • SOURCE 源文件保留
  • CLASS 编译器保留
  • RUNTIME 运行期保留 通常自定义注解都是RUNTIME,可以通过注解获取注解信息

@Documented

Documented注解的作用是:描述在使用javadoc工具为类生成帮助文档时是否要保留其注解信息。

@Inherited

Inherited注解的作用是:被它修饰的注解具有继承性,如果某个类使用了这个注解,那么他的子类自动具有该注解。

自定义注解的变量

String value();  
Boolean right();

和一般的类不同,自定义注解的变量是 类型 变量名();定义的变量可以通过反射取到;

自定义注解的使用

这也是我最近才研究清楚的,之前总是不明确自定义注解应该如何使用,但是在各个框架中,几乎都会用到注解。并且一般都会结合反射来使用

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

@Retention(RetentionPolicy.RUNTIME)
public @interface Xyc {
    String value();
    boolean print() default  false;
}

首先,定义了一个名为@Xyc的注解,这个注解有两个变量,value 和boolean类型的print变量。可以看到,在自定义注解中,并没有使用@Target注解,如果不使用Target注解,那么表示这个注解可以用在 各种地方。

我们可以定义一下逻辑:如果print为true,那么就打印一句话。

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

@Xyc(value = "xyc",print = true)
public class Test {
    @Xyc("xy")
    private String a = "1";
    private void setA(String a) {
        this.a = a;
    }
    @Xyc("yc")
    private String getA() {
        return a;
    }

    public static void main(String[] args) {
        Test test = new Test();
        Class<? extends Test> aClass = test.getClass();
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            Xyc annotation = field.getAnnotation(Xyc.class);
            if (annotation != null) {
                boolean print = annotation.print();
                if (print){
                System.out.println("############################" + annotation.value());
                }
            }
        }
        Method[] methods = aClass.getDeclaredMethods();
        for (Method method : methods) {
            Xyc annotation = method.getAnnotation(Xyc.class);
            if (annotation != null) {
                System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + annotation.value() + "!!!!!!!!!!");
            }
        }
        Annotation[] annotations = aClass.getAnnotations();
        for (Annotation annotation : annotations) {
            if (annotation instanceof Xyc) {
                String value = ((Xyc) annotation).value();
                System.out.println("xyc" + "\t" + value);
            }
        }
    }
}

让我们运行一下:································

############################xy
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!yc!!!!!!!!!!
xyc    xyc

可以看到当print为true的时候,可以成功打印。

然后新建一个Test类,在这个类中,我们使用自定义的@Xyc注解。我们在类中,方法中,和成员变量中,都使用了自定义注解。注解的使用逻辑如下:

成员变量的注解:

  1. 首先通过反射,获取到Test的信息;通过aClass.getDeclaredFields();可以获取到所有的成员变量(包括private修饰的成员变量);
  2. 然后可以遍历Field数组,通过field.getAnnotation(Xyc.class); 查找这个field(成员变量中)是否包含Xyc注解,如果有存在,则会返回注解对象。
  3. 判断返回的对象,是否为null,可以判断是否含有注解,并且可以通过注解.变量名来获取注解中的变量值。在获取到变量值后,可以根据变量值完成后续的业务逻辑,这里我们打印一句话;

方法上的注解

方法上的注解的获取方式和成员变量的注解获取方式详细,先通过反射获取到全部的Method,然后在进行针对Method判断该method是否含有注解信息,并实现后续的逻辑。

判断注解的方式:

  • 可以通过field.getAnnotation(注解.class)或者method.getAnnotation(注解.class)
  • 通过getAnnotations();获取全部的注解,然后循环注解,与想要判断的注解进行比较 instanceof.

一般就是这两种方式来判断和获取注解。

以上大致总结了(随便写写)注解的相关知识点,以及简单的用途(当然用途不仅仅如此,有机会更)。