注解和反射(一)【注解的基础知识和架构】

Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。用到“注释”这个词来描述,不太准备,容易让人误解为,类似于函数注释、属性注释说明一样的功能。和Javadoc不同的是,Java 标注可以通过反射获取标注内容。可以在编译时、运行时、类加载不同时期转换为相应java代码,或者进行字节码文件的修改。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。

1.注解

我们先来自己看一下java注解的整体简单架构
注解和反射(一)【注解的基础知识和架构】_第1张图片

从架构图上,先来直接总结一下:
1)Java Annotation 没啥特殊的,只是一个接口
2)我们平常研发过程中,经常见到的,Deprecated, Documented, Inherited, Override等等,是Annotation 实现类而已
3) Annotation 与 ElementType 、RetentionPolicy 从架构图上理解是一种关联的关系,ElementType 与 RetentionPolicy 只是对于 Annotation 的用途或者作用域等的说明

Annotation.java

package java.lang.annotation;
public interface Annotation {

    boolean equals(Object obj);

    int hashCode();

    String toString();

    Class<? extends Annotation> annotationType();
}

接下来,我们从源码理解、验证、总结一下

1.1 两个属性

注解和反射(一)【注解的基础知识和架构】_第2张图片

1.1.1 ElementType-用途

ElementType 用来指定Annotation 的用途,例如,若一个 Annotation 对象是 METHOD 类型,则该 Annotation 只能用来修饰方法。
`ElementType.java

package java.lang.annotation;

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    //类、接口(包括注释类型)或枚举声明
    TYPE,

    /** Field declaration (includes enum constants) */
    //字段声明(包括枚举常量)
    FIELD,

    /** Method declaration */
    //方法声明
    METHOD,

    /** Formal parameter declaration */
    //方法参数声明
    PARAMETER,

    /** Constructor declaration */
    //构造方法声明
    CONSTRUCTOR,

    /** Local variable declaration */
    //局部变量声明
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    //注解类型声明
    ANNOTATION_TYPE,

    /** Package declaration */
    //包声明
    PACKAGE
}

1.1.2 RetentionPolicy -作用域

RetentionPolicy 用来指定Annotation 的作用域,例如,若一个 Annotation 的类型为 SOURCE,则意味着:Annotation 仅存在于编译器处理期间,编译器处理完之后,该 Annotation 就没用了。
RetentionPolicy.java

package java.lang.annotation;

public enum RetentionPolicy {
    /**
     * Annotations are to be discarded by the compiler.
     * Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了
     */
    SOURCE,

    /**
     * Annotations are to be recorded in the class file by the compiler
     * but need not be retained by the VM at run time.  This is the default
     * behavior.
     * 编译器将Annotation存储于类对应的.class文件中。默认行为
     */
    CLASS,

    /**
     * Annotations are to be recorded in the class file by the compiler and
     * retained by the VM at run time, so they may be read reflectively.
     *
     * @see java.lang.reflect.AnnotatedElement
     * 编译器将Annotation存储于class文件中,并且可由JVM读入
     */
    RUNTIME
}

1.2 Java自带的Annotation

注解和反射(一)【注解的基础知识和架构】_第3张图片

1.2.1 meta-annotation 元注解

所谓元注解,其主要作用就是负责注解其他注解,为其他注解提供了相关的解释说明,我们元注解都在package java.lang.annotation;下。

@Documented
– @Documented 所标注内容,可以出现在javadoc中。

package java.lang.annotation;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

@Inherited
– @Inherited只能被用来标注“Annotation类型”,它所标注的Annotation具有继承性。


package java.lang.annotation;


@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

@Retention
– @Retention只能被用来标注“Annotation类型”,而且它被用来指定Annotation的RetentionPolicy属性。

package java.lang.annotation;


@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

@Target
– @Target只能被用来标注“Annotation类型”,而且它被用来指定Annotation的ElementType属性。

package java.lang.annotation;


@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}

@Repeatable
–@Repeatable用于声明标记的注解为可重复类型注解,可以在同一个地方多次使用。


package java.lang.annotation;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
    /**
     * Indicates the containing annotation type for the
     * repeatable annotation type.
     * @return the containing annotation type
     */
    Class<? extends Annotation> value();
}

1.2.2 Java自带其他注解

@Deprecated
@Deprecated所标注内容,不再被建议使用。



package java.lang;

import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;


@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}

@SuppressWarnings
@SuppressWarnings 所标注内容产生的警告,编译器会对这些警告保持静默。

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
  
    String[] value();
}

@Override
@Override 只能标注方法,表示该方法覆盖父类中的方法。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

2 Annotation的实际用处

2.1 编译检查

Annotation 具有"让编译器进行编译检查的作用"。
例如,@SuppressWarnings, @Deprecated 和 @Override 都具有编译检查作用。
注解和反射(一)【注解的基础知识和架构】_第4张图片

package com.itbird.annotation.test;

/**
 * OverrideTest测试类
 * Created by itbird on 2022/4/12
 */
public class OverrideTest {

    /**
     * toString() 在java.lang.Object中定义;
     * 因此,这里用 @Override 标注是对的。
     */
    @Override
    public String toString() {
        return "OverrideTest toString";
    }


    /**
     * getString() 没有在OverrideTest的任何父类中定义;
     * 但是,这里却用 @Override 标注,因此会产生编译错误!
     */
    @Override
    public String getString() {
        return "OverrideTest getString";
    }
}

2.2 在反射中使用 Annotation

在反射的 Class, Method, Field 等函数中,有许多于 Annotation 相关的接口。
这也意味着,我们可以在反射中解析并使用 Annotation。

package com.itbird.annotation.test;

/**
 * Created by itbird on 2022/4/12
 */

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * Annotation在反射函数中的使用示例
 */
@Retention(RetentionPolicy.RUNTIME)
@interface DefineAnnotation {
    String[] value() default "unknown";
}

/**
 * Person类。它会使用MyAnnotation注解。
 */
class Person {
    /**
     * empty()方法同时被 "@Deprecated" 和 "@MDefineAnnotation(value={"a","b"})"所标注
     * (01) @Deprecated,意味着empty()方法,不再被建议使用
     * (02) @DefineAnnotation, 意味着empty() 方法对应的DefineAnnotation的value值是默认值"unknown"
     */
    @DefineAnnotation
    @Deprecated
    public void empty() {
        System.out.println("\nempty");
    }

    /**
     * sombody() 被 @DefineAnnotation(value={"girl","boy"}) 所标注,
     *
     * @DefineAnnotation(value={"girl","boy"}), 意味着MDefineAnnotation的value值是{"girl","boy"}
     */
    @DefineAnnotation(value = {"girl", "boy"})
    public void somebody(String name, int age) {
        System.out.println("\nsomebody: " + name + ", " + age);
    }
}

public class AnnotationReflectTest {
    public static void main(String[] args) {
        Person person = new Person();
        try {
            // 获取 somebody() 方法的Method实例
            Method somebodyMethod = Person.class.getDeclaredMethod("somebody", new Class[]{String.class, int.class});
            // 获取 somebodyMethod的注解
            iteratorAnnotation(somebodyMethod);
            // 执行 somebodyMethod方法
            somebodyMethod.invoke(person, new Object[]{"itbird", "18"});


            // 获取 empty() 方法的Method实例
            Method emptyMethod = Person.class.getDeclaredMethod("empty", null);
            // 获取 emptyMethod的注解
            iteratorAnnotation(emptyMethod);
            // 执行 emptyMethod
            emptyMethod.invoke(person, null);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private static void iteratorAnnotation(Method somebodyMethod) {
        //方法是否拥有注解
        if (somebodyMethod.isAnnotationPresent(DefineAnnotation.class)) {
            //获取方法的所有注解
            DefineAnnotation annotation = (DefineAnnotation) somebodyMethod.getAnnotation(DefineAnnotation.class);
            // 循环打印注解
            String[] values = annotation.value();
            for (String str : values)
                System.out.printf(str + ", ");
            System.out.println();
        }
    }
}

2.3 根据 Annotation 生成帮助文档

通过给 Annotation 注解加上 @Documented 标签,能使该 Annotation 标签出现在 javadoc 中。

2.4 能够帮忙查看查看代码

通过 @Override, @Deprecated 等,我们能很方便的了解程序的大致结构。

你可能感兴趣的:(Java随笔,注解,反射)