APT的介绍与使用

作者: migill | 来源:发表于2019-12-24 22:46 被阅读0次

    APT(Annotation Processing Tool)是一种处理注解的工具,它对源代码文件进行检测找出其中的Annotation,根据注解自动生成代码,如果想要自定义的注解处理器能够正常运行,必须要通过APT工具来进行处理。也可以这样理解,只有通过声明APT工具后,程序在编译期间自定义注解解释器来能执行。
    通俗理解:根据规则,帮我们生成代码、生成类文件

    element组成的结构体

    对于java源文件来说,java也是一种结构体语言


    Element程序元素
    PackageElement:表示一个包程序元素。提供对有关包及其成员的信息的访问
    ExecutableElement:表示某个类或接口的方法、构造方法或初始化程序(静态或实例)
    TypeElement:表示一个类或接口程序元素。提供对有关类型及其成员的信息的访问。
    VariableElement:表示一个字段、enum常量、方法或构造方法参数、局部变量或异常参数
    需要掌握的API

    自定义注解

    定义格式:
    元注解
    public @interface 注解名称{
    格式1: 修饰符 数据类型 属性名( );
    格式2: 修饰符 数据类型 属性名( ) default 默认值 ;
    }

    注解的属性:
    1.作用: 使用注解时可以传递参数 , 让注解功能更强大.
    2.属性使用的数据类型: 四类八种 , String类型 , Class 类型 , 枚举 , 注解类型.
    3.特殊属性Value : 当只有一个属性 && 名称是value , 可以直接给属性值.

    使用格式:
    没有属性: @注解名称
    有 属 性 : @注解名称(属性名 = 属性值 , 属性名 = 属性值 … )
    value属性: @注解名称(属性值) ;

    元注解@Target:

    • @Target(ElementType.TYPE) // 接口、类、枚举、注解
    • @Target(ElementType.FIELD) // 属性、枚举的常量
    • @Target(ElementType.METHOD) // 方法
    • @Target(ElementType.PARAMETER) // 方法参数
    • @Target(ElementType.CONSTRUCTOR) // 构造函数
    • @Target(ElementType.LOCAL_VARIABLE)// 局部变量
    • @Target(ElementType.ANNOTATION_TYPE)// 该注解使用在另一个注解上
    • @Target(ElementType.PACKAGE) // 包
      元注解@Retention:
    • @Retention(RetentionPolicy.RUNTIME) //注解会在class字节码文件中存在,jvm加载时可以通过反射获取到该注解的内容
    • @Retention(RetentionPolicy.CLASS) // 要在编译时进行一些预处理操作。注解会在class文件中存在
    • @Retention(RetentionPolicy.SOURCE) //做一些检查性的操作,注解仅存在源码级别,在编译的时候丢弃该注解
      生命周期:SOURCE < CLASS < RUNTIME
      1、一般如果需要在运行时去动态获取注解信息,用RUNTIME注解
      2、要在编译时进行一些预处理操作,如ButterKnife,用CLASS注解。注解会在class文件中存在,但是在运行时会被丢弃
      3、做一些检查性的操作,如@Override,用SOURCE源码注解。注解仅存在源码级别,在编译的时候丢弃该注解

    开发环境兼容

    APT的使用

    1、创建annotation的Module与自定义注解

    \color{red}{创建annotation的module}


    \color{red}{创建ARouter自定义注解}
    @Target(ElementType.TYPE)//该注解作用在类之上
    @Retention(RetentionPolicy.CLASS)//要在编译时进行一些预处理操作。注解会在class文件中存在
    public @interface ARouter {
        //详细路由路径(必填)
        String path();
        // 路由组名(选填,如果开发者不填写,可以从path中截取出来)
        String group() default "";
    }
    

    \color{red}{在app中引入annotation模块依赖}


    2、创建注解处理器的Module

    \color{red}{在根目录的build.gradle添加阿里镜像}

    / 超级实用:很多时候经常被墙,强烈推荐阿里云镜像更新
            maven {
                url "http://maven.aliyun.com/nexus/content/groups/public/"
            }
    

    \color{red}{在compiler module的build.gradle中引入annotation与导包}

    apply plugin: 'java-library'
    
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
    
        //auto-service是谷歌为我们提供的处理注解的服务,主要是注册注解并对其生成一个文件
        // As-3.4.1 + gradle5.1.1-all + auto-service:1.0-rc4
        compileOnly'com.google.auto.service:auto-service:1.0-rc4'
        annotationProcessor'com.google.auto.service:auto-service:1.0-rc4'
    
        // 引入annotation,让注解处理器-处理注解
        implementation project(':annotation')
    }
    // java控制台输出中文乱码
    tasks.withType(JavaCompile) {
        options.encoding = "UTF-8"
    }
    // jdk编译版本1.7
    sourceCompatibility = "7"
    targetCompatibility = "7"
    

    \color{red}{创建注解处理器ARouterProcessor类}

    import com.google.auto.service.AutoService;
    import com.migill.annotation.ARouter;
    import java.io.IOException;
    import java.io.Writer;
    import java.util.Set;
    import javax.annotation.processing.AbstractProcessor;
    import javax.annotation.processing.Filer;
    import javax.annotation.processing.Messager;
    import javax.annotation.processing.ProcessingEnvironment;
    import javax.annotation.processing.Processor;
    import javax.annotation.processing.RoundEnvironment;
    import javax.annotation.processing.SupportedAnnotationTypes;
    import javax.annotation.processing.SupportedOptions;
    import javax.annotation.processing.SupportedSourceVersion;
    import javax.lang.model.SourceVersion;
    import javax.lang.model.element.Element;
    import javax.lang.model.element.TypeElement;
    import javax.lang.model.util.Elements;
    import javax.lang.model.util.Types;
    import javax.tools.Diagnostic;
    import javax.tools.JavaFileObject;
    
    //AutoService是固定的写法,用来注册
    @AutoService(Processor.class)
    //允许/支持的注解处理器类型,让注解处理器处理
    @SupportedAnnotationTypes("com.migill.annotation.ARouter")
    //指定DJK编译版本
    @SupportedSourceVersion(SourceVersion.RELEASE_7)
    //注解处理器接收的参数
    @SupportedOptions("content")
    public class ARouterProcessor extends AbstractProcessor {
        //操作Elememts工具类(类、函数、属性都是Element)
        private Elements elementUtils;
        //type(类信息)工具类,包含用于操作TypeMirror的工具方法
        private Types typeUtils;
        //Messager用来报告错误,警告和其他提示信息
        private Messager messager;
        //文件生成器 类/资源,Filter用于创建新的源文件,class文件预计辅助文件
        private Filer filer;
        //主要用于一些初始化的操作,通过该方法的参数processingEnvironment可以获取一些有用的工具类
        @Override
        public synchronized void init(ProcessingEnvironment processingEnvironment) {
            super.init(processingEnvironment);
            elementUtils = processingEnvironment.getElementUtils();
            messager = processingEnvironment.getMessager();
            filer = processingEnvironment.getFiler();
            //通过processingEnvironment去获取build.gradle传过来的参数
            String content = processingEnvironment.getOptions().get("content");
            messager.printMessage(Diagnostic.Kind.NOTE,content);
    
        }
    
        /**
         * 相当于main函数,开始处理注解
         * 注解处理器的核心方法,处理具体的注解,生成Java文件
         * @param set 使用了支持处理注解的节点集合(类 上面写了注解)
         * @param roundEnvironment 当前或是之前的运行环境,可以通过该对象查找找到的注解
         * @return true 表示后续处理器不会再处理(已经处理完毕)
         */
        @Override
        public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
            if(set.isEmpty()) return false;
            Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(ARouter.class);
            for(Element element : elements) {
                //通过类节点获取包节点
                String packageName = elementUtils.getPackageOf(element).getQualifiedName().toString();
                //获取简单类名
                String className = element.getSimpleName().toString();
                messager.printMessage(Diagnostic.Kind.NOTE,"备注注解的类:"+packageName+"."+className);
                // 最终想生成的类文件名
                String finalClassName = className + "$$ARouter";
                try{
                    JavaFileObject sourceFile = filer.createSourceFile(packageName+"."+finalClassName);
                    Writer writer = sourceFile.openWriter();
                    writer.write("package " + packageName + ";\n");
                    writer.write("public class " + finalClassName + " {\n");
                    writer.write("public static Class<?> findTargetClass(String path) {\n");
                    ARouter aRouter = element.getAnnotation(ARouter.class);
                    writer.write("if (path.equals(\"" + aRouter.path() + "\")) {\n");
                    writer.write("return " + className + ".class;\n}\n");
                    writer.write("return null;\n");
                    writer.write("}\n}");
                    // 最后结束别忘了
                    writer.close();
                }catch (IOException e){
                   e.printStackTrace();;
                }
            }
            return false;
        }
    }
    

    \color{red}{接着重新配置一下app模块下的build.gradle文件}

    apply plugin: 'com.android.application'
    
    android {
      ...
        defaultConfig {
          ...
            // 在gradle文件中配置选项参数值(用于APT传参接收)
            // 切记:必须写在defaultConfig节点下
            javaCompileOptions {
                annotationProcessorOptions {
                    arguments = [content: 'hello apt']
                }
            }
        }
    ...
    }
    
    dependencies {
      ...
        //依赖注解
        implementation project(":annotation")
        //依赖注解处理器
        annotationProcessor project(":compiler")
    }
    

    注解处理器写完了,配置也完成了,现在编译,就可以生成对应的文件了,如下图,就是生成的文件与文件的内容


    \color{red}{现在就可以使用生成的文件了,MainActivity—>Main2Activity—>Main3Activity—>MainActivity}




    效果如下


    相关文章

      网友评论

        本文标题:APT的介绍与使用

        本文链接:https://www.haomeiwen.com/subject/lmvtoctx.html