【06】保留在源码级别注解的应用场景

(1)一个人只要自己不放弃自己,整个世界也不会放弃你.
(2)天生我才必有大用
(3)不能忍受学习之苦就一定要忍受生活之苦,这是多么痛苦而深刻的领悟.
(4)做难事必有所得
(5)精神乃真正的刀锋
(6)战胜对手有两次,第一次在内心中.
(7)编写实属不易,若喜欢或者对你有帮助记得点赞+关注或者收藏哦~

保留在源码级别注解的应用场景

1.注解的应用场景

(1)根据注解的保留级别不同,对注解的使用自然存在不同场景。

(2)由注解的三个不同保留级别可知,注解作用于:源码、字节码与运行时时你能举一些案例吗?

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

2.保留在源码SOURCE级别注解的应用场景

主要是结合APT(Annotation Processor Tools)技术,在编译期获取注解声明的类,以及类中的所有成员信息,常用于生成辅助类。

2.1APT注解处理器

APT:Annotation Processor Tools (注解处理工具)

2.1.1注解处理器是如何运行的,运行在什么阶段的?

(1).java - > javac —> .class

(2).java文件需要 通过javac 编译成class文件

(3)采集所有的注解信息

(4)将采集的信息包装成元素节点Element

(5)由javac去调起注解处理器,执行我们的注解处理程序。(不是我们手动调用的,是由javac去调用的)

(6)注解处理程序是在类文件被javac编译成字节码文件之前执行的。

2.1.2 注解处理器定义

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.TypeElement;

public class LanceProcessor extends AbstractProcessor {
    
    

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    
    
        return false;
    }
}

(1)让我们的类extends AbstractProcessor,这一个类就是一个注解处理程序。

(2)这一个类会被javac在编译的时候去实例化,然后再调用里面的process方法.

(3)写好这样一个类之后,javac还是不能直接去调用它,即仍然不能使用它,还缺少很关键的一步,还需要去注册。

2.1.3如何注册注解处理器?

(1)在main目录下面创建一个名为resources的目录

(2)在resources目录之下再创建一个名为META-INF,(注意不要把INF写成INFO)

(3)在META-INF目录下再创建services目录

(4)在services目录之下创建javax.annotation.processing.Processor文件

在这里插入图片描述

(5)在文件中写入注解处理程序的全类名

	com.gdc.compiler.LanceProcessor

(6)注解处理程序编写

  • 在注解处理器的实现方法里面,输出一些信息

  • 不要使用System.out.pringln去输出,用processingEnv这个成员属性去输出.

  • 注解处理程序是否可以指定处理某个注解呢?

可以通过@SupportedAnnotationTypes注解指定注解处理器只处理某个注解。

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;

@SupportedAnnotationTypes("com.gdc.javabase.annotation.Lance")
public class LanceProcessor extends AbstractProcessor {
    
    

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    
    
        //s1.获取messager对象
        Messager messager = processingEnv.getMessager();

        //s2.设置输出消息级别
        messager.printMessage(Diagnostic.Kind.NOTE,"=======注解处理器程序被调用=======");

        return false;
    }
}

(7)在app模块中去引用注解处理器模块,即修改build.gradle文件

//s1.引用注解处理器程序模块
annotationProcessor project(':compiler')

(8)编译项目,查看注解处理程序运行结果

  • Build ----> make project
  • 结果如下图所示:

2.1.4 使用Google插件注册注解处理器

(1)Google提供了一个插件来帮助我们更方便的注册注解处理器,你只需要导入对应的依赖包,在自定义的Processor类上方添加@AutoService(Processor.class)即可。

(2)导入依赖包

api 'com.google.auto.service:auto-service:1.0-rc2'

(3)添加声明

@AutoService(Processor.class)
@SupportedAnnotationTypes("com.gdc.annotationprocessor.annotation.Address")
public class AddressProcessor extends AbstractProcessor {
    
    

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    
    

        //s1.获取messager对象
        Messager messager = processingEnv.getMessager();

        //s2.设置消息级别
        messager.printMessage(Diagnostic.Kind.NOTE,"=======注解处理器程序被调用=======");

        return false;
    }
}

(4)这种方式存在AutoService与gradle版本不兼容的问题,导致注解处理器无法获得执行,通常会采用gradle降级的方式处理,所以不太可取,后期看是事会有更优的方案处理这个兼容的问题。

解决方法:https://blog.csdn.net/qq_39969226/article/details/104538438

2.2使用注解制定IDE语法检查规则

2.2.1@IntDef注解

(1)在Android开发中,support-annotations与androidx.annotation中均有提供@InDef注解,此注解的定义如下:

@Retention(SOURCE)
@Target({
    
    ANNOTATION_TYPE})
public @interface IntDef {
    
       
    int[] value() default {
    
    };
    boolean flag() default false;
    boolean open() default false;
}

(2)Java中Enum(枚举)的实质是特殊的静态成员变量,在运行期所有枚举类作为单例,全部加载到内存中。比常量多5到10倍的内存占用。

(3)此注解的意义在于能够取代枚举,实现如方法参数限制。

(4)@IntDef是一个元注解。

(5)利用注解检测语法规则案例

class Test2 {
    
    

    //s1.此处的@DrawableRes注解是提供一个语法检查的
    public static void setDrawable(@DrawableRes int id){
    
    

    }

    public static void main(String[] args) {
    
    
        //s1.@DrawableRes注解的应用,限定只能传递drawable型的整型资源数据
        setDrawable(R.drawable.ic_launcher_background);
    }
}

2.2.2使用@IntDef注解来制定我们自己的语法检测规则

2.2.2.1使用枚举的缺点
  • 在Java中,枚举本质上是对象,比较占用内存。

  • 一个对象占用几个字节?是由12个字节的对象头,加上它的成员所占的字节构成的,同时还有8个字节的对齐。

  • 枚举比较占用内存,可以考虑使用常量代替枚举类型来降低对内存的占用。

2.2.2.2枚举的应用举例
class Test {
    
    

    private static WeekDay mCurrentDay;

    enum WeekDay{
    
    
        SUNDAY,MONDAY
    }

    public static void setCurrentDay(WeekDay weekDay){
    
    
        mCurrentDay = weekDay;
    }

    public static void main(String[] args) {
    
    
        //s1.枚举的应用
        setCurrentDay(WeekDay.SUNDAY);
    }
}
2.2.2.3使用自定义注解限定方法的入参参数值的选定范围

使用常量代替枚举类型来降低对内存的占用

public class Test3 {
    
    

    private static final int SUNDAY = 0;
    private static final int MONDAY = 1;

    @WekDay
    private static int mCurrentIntDay;

    //s1.定义一个注解,实现方法参数的限定
    @IntDef({
    
    SUNDAY,MONDAY})//限定入参参数值的选定范围
    @Target({
    
    ElementType.FIELD,ElementType.PARAMETER})
    @Retention(RetentionPolicy.SOURCE)
    @interface WekDay{
    
    

    }

    //s2.使用@IntDef注解来制定我们自己的语法检测规则,对int型的入参数据做一些限定
    public static void setCurrentDay(@WekDay int currentDay){
    
    
        mCurrentIntDay = currentDay;
    }

    public static void main(String[] args) {
    
    
        //s3.@IntDef注解的应用
        setCurrentDay(SUNDAY);
    }
}
```

##### 2.2.2.4注解实现语法检查是由谁做的?1)由IDE实现的,或者说是由IDE插件实现的。

 setCurrentDay(@WekDay int currentDay){
    
    
        mCurrentIntDay = currentDay;
    }

    public static void main(String[] args) {
    
    
        //s3.@IntDef注解的应用
        setCurrentDay(SUNDAY);
    }
}
```

##### 2.2.2.4注解实现语法检查是由谁做的?1)由IDE实现的,或者说是由IDE插件实现的。

(2)IDEA是由Java开发的,它可以对我们的文件进行分析,对我们的语法进行检测。

猜你喜欢

转载自blog.csdn.net/xiogjie_67/article/details/108628949