Java注解

165 阅读3分钟

注解的定义

注解本身没有任何意义,单独的注解就是一种注释,他需要结合其他如反射、插桩等技术才有意义.

Java注解(Annotation)又称Java标注,是JDK1.5 引入的一种注释机制。是元数据的一种形式,提供有关于程序但不属于程序本身的数据。注解对它们注解的代码的操作没有直接影响。 JAVA注解(注释)又称Java标注,是JDK1.5引入的一种注释机制。是元数据的一种形式,提供有关于程序但不属于程序本身的数据.注解对它们注解的代码的操作没有直接影响.

元注解

在定义注解时,注解类也能够使用其他的注解声明。对注解类型进行注解的注解类,我们称之为meta-annotation(元注解)。声明的注解允许作用于哪些节点使用@Target声明;保留级别由@Retention声明。其中保留级别如下。

@Retention 注解指定标记注解的存储方式: 
RetentionPolicy.SOURCE
标记的注解仅保留在源级别中,并被编译器忽略。
RetentionPolicy.CLASS
标记的注解在编译时由编译器保留,但Java虚拟机(JVM)会忽略。
RetentionPolicy.RUNTIME
标记的注解由JVM保留,因此运行时环境可以使用它。

@Target 注解标记另一个注解,以限制可以应用注解的Java元素类型。目标注解指定以下元素类型之一作为其值:
ElementType.ANNOTATION_TYPE  可以应用于注解类型。
ElementType.CONSTRUCTOR  可以应用于构造函数。
ElementType.FIELD  可以应用于字段或属性。
ElementType.LOCAL_VARIABLE  可以应用于局部变量。
ElementType.METHOD  可以应用于方法级注解。
ElementType.PACKAGE  可以应用于包声明。
ElementType.PARAMETER  可以应用于方法的参数。
ElementType.TYPE  可以应用于类的任何元素。

SOURCE<CLASS<RUNTIME,即CLASS包含了SOURCE,RUNTIME包含SOURCE、CLASS。

级别技术说明
源码APT在编译期能够获取注解与注解声明的类包括类中所有成员信息,一般用于生成额外的辅助类。
字节码字节码增强在编译出Class后,通过修改Class数据以实现修改代码逻辑目的。对于是否需要修改的I分或者修改为不同逻辑的判断可以使用注解。
运行时反射在程序运行期间,通过反射技术动态获取注解与其元素,从而完成不同的逻辑判定。。

还有其他一些元注解 例如:

@DrawableRes 入参校验指定为Resource
@IntDef 入参校验指定为int
@StringDef 校验指定为String

注解处理器&拦截器 Android Annotation Processor

注解处理器(Annotation Processor)是javac的一个工具,不管是运行时注解还是编译时注解,都会通过处理器在编译时进行扫描和处理注解。 Java中有默认的注解处理器,使用者也可以自定义注解处理器,注册后使用注解处理器处理注解,最终达到注解本身起到的效果。

注解处理器将标记了注解的类,变量等作为输入内容,经过注解处理器处理,生成想要生成的java代码。所以处理器可以理解为就是一个生成代码的工具,只是是通过注解的规则生成。生成后的代码,可以看作是同一般代码,最终被编译。

自定义处理器

  • 自定义处理器需要创建java 库java- Library(自行创建)
  • implementation 'com.google.auto.service:auto-service:1.0-rc3'
  • implementation 'com.google.auto:auto-common:0.8'
创建自定义Processor类 如创建TestProcessor
  • TestProcessor继承 AbstractProcessor 添加@SupportedAnnotationTypes({"注解类全路径"})注解
    如:
@SupportedAnnotationTypes({"com.xx.annotation.TestAnnotation", ""})//支持传入多个
public class PermissionProcessor extends AbstractProcessor {

    /* 
    init()方法,它会被注解处理工具调用,并输入ProcessingEnviroment参数。
    ProcessingEnviroment提供很多有用的工具类Elements, Types和Filer。
    */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
    }

    /*
    这相当于每个处理器的主函数main()。扫描、评估和处理注解的代码,以及生成Java文件。
    输入参数RoundEnviroment,可以让你查询出包含特定注解的被注解元素。后面我们将看到详细的内容。
    */
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        return false;
    }
}
创建javax.annotation.processing.Processor 注册注解处理器

在使用注解处理器需要先声明,步骤:
1、需要在 processors 库的 main 目录下新建 resources 资源文件夹;
2、在 resources文件夹下建立 META-INF/services 目录文件夹
3、创建javax.annotation.processing.Processor文件
4、文件内容添加:com.xx.java.processor.TestProcessor 自己创建注解处理器的路径\

ProcessingEnvironment 包含了注解处理器相关的工具类和编译器配置的参数

public interface ProcessingEnvironment {
    Map<String, String> getOptions(); // 编译期间,app给注解处理器传的值

    Messager getMessager(); // 在注解处理器处理注解生成新的源代码过程中,我们可用Messager来将一些错误信息打印到控制台上

    Filer getFiler(); // 我们可以通过这个类来创建新的文件。

    /**
    * 它其实是一个工具类,用来处理所有的Element 元素,
    * 而我们可以把生成代码的类中所有的元素都可以成为Element 元素,
    * 如包就是PackageElement, 
    * 类/接口为TypeElement, 
    * 变量为VariableElement, 
    * 方法为ExecutableElement
    */
    Elements getElementUtils();

    /**
    * 它其实也是一个工具类,只是用来处理TypeMirror. 也就是一个类的父类。
    * TypeMirror superClassType = currentClass.getSuperclass();
    */
    Types getTypeUtils(); 

    SourceVersion getSourceVersion();

    Locale getLocale();
}

RoundEnvironment 指在每一轮的扫描和处理源代码中获取被注解的Element

// 获取被注解的Element
Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(GetPermissions.class);

// 遍历Element 获取相应添加注解的信息
for (Element element : elementsAnnotatedWith) {
    TypeElement classElement = (TypeElement) element.getEnclosingElement(); // 获取类
    classElement.getSimpleName().toString(); // 获取类名
    element.getAnnotation(注解类.class); // 获取注解的实体类对象,可获取里面相应的值
    element.getModifiers() // 获取修饰符
    // 强转方法的Element
    ExecutableElement methodElement = (ExecutableElement) element;
    // 获取方法名
    String methodName = methodElement.getSimpleName().toString();
    executableElement.getParameters(); // 获取注解形参
    executableElement.getReturnType(); // 获取返回值类型
    enclosingElement.getTypeParameters(); // 获取返回值
    executableElement.getThrownTypes(); // 获取方法throw的异常
}