Java的注解

一、注解的定义

1.1 通用的注解声明方式

public @interface 注解名 {
    //定义成员
}
  • 通过 @interface 表示这是一个注解接口;
  • 注解中的成员变量在注解声明中以无参数方法的形式来声明,其方法名定义了该成员的名字,返回值定义了该成员的类型,注解中成员变量的类型是受限制的,只能是基本类型及其包装类、String类型、Class类型、枚举类型、注解类型、以及这些类型的数组。

1.2 使用元注解定义注解

在定义注解时,必须使用元注解 @Retention、@Target、@Inherited 和 @Documented 说明注解的作用域、使用范围、是否可继承和是否输出到 API 文档中。

@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Target(ElementType.TYPE)
@Documented
public @interface MyAnnotation {
    String name() default "default";
}
  • "@Retention"表示保留期,它的值定义了该注解的生命周期。RetentionPolicy.RUNTIME 表示在运行时可以获取注解信息;
  • “@Target” 表示适用范围,可以指定作用在注解上的目标元素类型,如类、方法、成员变量等。多个目标类型可以用逗号隔开,如果省略该元素,表示定义的注解可以用在所有的程序元素上。
  • “@Inherited” 表示注解可以被继承,默认情况下,注解不会被继承。如果注解需要被子类继承,可以在定义注解时使用 @Inherited 元注解。
  • “@Documented” 表示注解中的元素可以被 javadoc 工具读取并生成文档。
1. @Retention

@Retention 注解指定了被修饰的注解保留多长时间。

@Retention 属性取值如下:

  • RetentionPolicy.SOURCE:注解只存在于编译器处理期间,不会在编译输出的 class 文件中出现,适用于限制程序员编写代码时使用的注解。
  • RetentionPolicy.CLASS:注解会被编译器记录在 class 文件中,但是在运行时 JVM 不会保留,适用于需反射获取注解信息的情况。
  • RetentionPolicy.RUNTIME:注解会被编译器记录在 class 文件中,同时在 JVM 运行时也会保留,可以通过反射动态获取注解信息。
2. @Target

@Target 注解指定了注解可以被用来修饰哪些程序元素,例如类、方法、域等。

@Target 的取值类型有以下常量:

  • ElementType.TYPE:用于标注类、接口、枚举。
  • ElementType.CONSTRUCTOR:用于标注构造方法。
  • ElementType.FIELD:用于标注成员变量。
  • ElementType.METHOD:用于标注方法。
  • ElementType.PARAMETER:用于标注参数。
  • ElementType.LOCAL_VARIABLE:用于标注局部变量。
  • ElementType.ANNOTATION_TYPE:用于标注注解。
  • ElementType.PACKAGE:用于标注包。
3. @Inherited

@Inherited 注解表示标注了此注解的类的子类也会被标注此注解。

4. @Documented

@Documented 注解也被称为文档化注解,加上这个注解后,注解的信息就可以被 javadoc 所解析。

1.3 注解中的成员

注解中的成员可以使用 default 关键字指定默认值。

@Target(ElementType.METHOD)
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodInfo {
    String name() default "TestMethod";
    String data() default "2020-05-10";
    int id() default 0;
}

name、data、id 都是注解中的成员变量,在注解使用时都可以使用这些成员变量来对注解进行赋值。

二、使用注解

2.1 注解的解析

可以通过反射机制获取到指定类或方法上的注解信息。

public class RunMyAnnotation {

    @MethodInfo(id=1,name = "Main",data="2022-08-22",value="")
    public void test1(){
        System.out.println("test1");
    }

    public static void main(String[] args) {
        try {
            Method method = RunMyAnnotation.class.getMethod("test1");
            MethodInfo annotation = method.getAnnotation(MethodInfo.class);
            System.out.println("id: " + annotation.id());
            System.out.println("name: " + annotation.name());
            System.out.println("data: " + annotation.data());
            System.out.println("value: " + annotation.value());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

在上面的代码中,使用了反射机制获取到指定类中指定方法的注解信息。

2.2 给注解赋值

注解中成员的赋值,可以使用“成员名=值”的方式进行赋值,如果只有一个成员需要赋值,并且成员名称为"value",那么在赋值时,可以省略成员名称和“=”。

  • 指定变量名进行赋值:
@MethodInfo(name="sayHello")
public void say(){
    System.out.println("Hello Annotation");
}
  • 只有一个默认值’value’进行赋值:
@MethodInfo("HelloWord!")
public void say(){
    System.out.println("Hello Annotation");
}

2.3 编译时注解处理器

Java 提供了 apt 工具来处理注解,Apt 是 Annotation Processing Tool 的缩写。它是负责在编译期扫描和处理类的注解的程序,也就是提供注释处理器服务的程序。

apt 在 Eclipse 和 IDEA 中的默认设置是关闭的,需要进行配置。

2.4 自定义注解处理器

注解处理器的工作就是扫描源代码中的注解,并解析和使用注解。

自定义注解处理器一般包括两部分:

  • 注解声明类型;
  • 注解处理类。

​ 以 @BindView 注解为例子:

2.4.1 定义注解 @BindView
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BindView {
    int value();
}

这个注解用于设置控件的 ID,它只能用于成员变量,作用域是运行时。

2.4.2 自定义注解处理器

自定义注解处理器需要继承 javax.annotation.processing.AbstractProcessor 抽象类并且覆写处理注解的方法。

@SupportedAnnotationTypes("com.yuk.customannotaion.BindView")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyAnnotationProcessor extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        System.out.println("进入注解处理器");

        // 获取到注解@BindView
        Set<? extends Element> es = roundEnvironment.getElementsAnnotatedWith(BindView.class);
        for (Element e : es) {
            // 强转成变量元素
            VariableElement ve = (VariableElement) e;
            // 获取变量元素所在的类
            TypeElement enclosingElement = (TypeElement) ve.getEnclosingElement();
            // 获取类名和包名
            String packageName = elementUtils.getPackageOf(enclosingElement).getQualifiedName().toString();
            String className = enclosingElement.getSimpleName().toString();
            // 控件ID
            int id = ve.getAnnotation(BindView.class).value();
            // 控件名称
            String fieldName = ve.getSimpleName().toString();
            System.out.println(String.format("packageName=%s, className=%s, fieldName=%s, id=%d", packageName, className, fieldName, id));
        }
        System.out.println("退出注解处理器");
        return true;
    }
}

2.5 使用 apt 工具解析注解

在注解处理器完成后,需要通过 apt 工具把注解处理器编译并生成处理结果,并在运行时被解析。

2.5.1 编译注解处理器

使用如下命令编译注解处理器:

javac -encoding UTF-8 MyAnnotationProcessor.java

之后会生成 MyAnnotationProcessor.class 文件。

2.5.2 使用 apt 工具解析注解

使用如下命令编译包含 @BindView 注解的类:

javac -processor com.xxx.xxx.MyAnnotationProcessor TestAnnotation.java

在编译时,Apt 会扫描 @BindView 注解并调用 @BindView 注解处理器生成相应的代码,生成的代码就是把 @BindView 注解的字段和控件ID绑定起来的代码。

关注微信公众号:“小虎哥的技术博客”。我们会定期发布关于Java技术的详尽文章,让您能够深入了解该领域的各种技巧和方法,让我们一起成为更优秀的程序员‍‍!

你可能感兴趣的:(Java系列之打好基础,java,开发语言)