APT及JavaPoet基本使用

本文来自享学derry老师的课堂分享

APT初窥

 

APT是什么?

 

APT(Annotation Processing Tool)

 

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

 

通俗理解:根据规则,帮我们生成代码、生成类文件

 

APT中用到的重要元素

 

PackageElement:

表示一个包程序元素。提供对有关包及其成员的信息的访问

 

ExecutableElement:

表示某个类或接口的方法、构造方法或初始化程序(静态或实例)

 

TypeElement:

表示一个类或接口程序元素。提供对有关类型及其成员的信息的访问。

 

VariableElement:

表示一个字段、enum 常量、方法或构造方法参数、局部变量或异常参数

 

APT中用到API

 

问:

1、APT全称:annotation process tool是一种处理注释的工具,它对源代码文件进行检测查找出其中的Annotation,使用Annotation进行额外的处理

APT 编译的时候 ---> 处理注解

APT 传统方式 ---> 生成 java文件

APT JavaPoet方式 --> 生成Java文件

2、传统方式 那些 开源项目有用到?

​答:看看EventBus源码就知道了

https://www.jianshu.com/p/f057c460c77e

https://www.jianshu.com/p/633348569198

传统方式:优点(编程的流程写下去) 缺点(没有oop思想加入进来)

 

3、JavaPoet是什么?

 

JavaPoet是square推出的开源java代码生成框架,提供Java Api生成.java源文件 这个框架功能非常实用,也是我们习惯的Java面向对象OOP语法

可以很方便的使用它根据注解生成对应代码

通过这种自动化生成代码的方式,

可以让我们用更加简洁优雅的方式要替代繁琐冗杂的重复工作

项目主页及源码:https://github.com/square/javapoet

 

JavaPoet相关

1、属性

2、高级用法

 

JavaPoet到底是什么?

​答:oop思想方式:优点(加入oop思想) 缺点(不习惯,倒序)

3、组件化项目部署、ARouter原理

项目结构

1、建立相应的项目结构

2、导入需要的包

compiler下的gradle,compiler是apt的注解包

apply plugin: 'java-library'

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    // 背后的服务 能够监听 你是否在编译中.....
    // AS3.4.1 + Gradle 5.1.1 + 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'

    // 帮助我们通过类调用的形式来生成Java代码 [JavaPoet]
    implementation "com.squareup:javapoet:1.9.0"

    // 依赖注解
    implementation project(":arouter-annotations")
}

arouter-annotation中的自定义的注解处理器

@Target(TYPE) // 类上
@Retention(CLASS) // 编译期    XUtil==运行期
public @interface ARouter {

    String path();

    String group() default "";

}

app工程下gradle,添加两个注解包的依赖

 // 依赖注解
    implementation project(":arouter-annotations")

    // 依赖注解处理器 注解处理器才能工作
    annotationProcessor project(":compiler")

下面看一下注解处理器怎么处理注解,在编译时期识别注解并转成java代码的

在compiler下创建ARouterProcessor,这里面就是手动获取了被注解的类的信息,并且一一对应生成了对应的java文件

@AutoService(Processor.class) // 启用服务
@SupportedAnnotationTypes({"com.derry.arouter_annotations.ARouter"}) // 注解
@SupportedSourceVersion(SourceVersion.RELEASE_7) // 环境的版本


// 接收 安卓工程传递过来的参数
@SupportedOptions("student")

public class ARouterProcessor extends AbstractProcessor {

    // 操作Element的工具类(类,函数,属性,其实都是Element)
    private Elements elementTool;

    // type(类信息)的工具类,包含用于操作TypeMirror的工具方法
    private Types typeTool;

    // Message用来打印 日志相关信息
    private Messager messager;

    // 文件生成器, 类 资源 等,就是最终要生成的文件 是需要Filer来完成的
    private Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);

        elementTool = processingEnvironment.getElementUtils();
        messager = processingEnvironment.getMessager();
        filer = processingEnvironment.getFiler();

        String value = processingEnvironment.getOptions().get("student");

        // 如果我想在注解处理器里面抛出异常 可以使用Diagnostic.Kind.ERROR
        messager.printMessage(Diagnostic.Kind.NOTE, ">>>>>>>>>>>>>>>>>>>>>>"+value);
    }

    // 服务:在编译的时候干活
    // 坑:如果没有在任何地方使用,次函数是不会工作的
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
    
        messager.printMessage(Diagnostic.Kind.NOTE, ">>>>>>> Derry run...");

        if (set.isEmpty()) {
            return false; // 不干活
        }

        // 循环?
        // 获取被 ARouter注解的 "类节点信息"
        Set<? extends Element> elements =         
        roundEnvironment.getElementsAnnotatedWith(ARouter.class);
        for (Element element : elements) { // for 3    // 1 element == MainActivity    2     
        element == MainActivity2
        // 注释部分是基本使用,写死了各种类
            /**
            
             模块一
             package com.example.helloworld;

             public final class HelloWorld {

             public static void main(String[] args) {
                 System.out.println("Hello, JavaPoet!");
             }
             }

             */
            // Java 万物皆对象
            // C  万物皆指针
            /*// 1.方法
            MethodSpec mainMethod = MethodSpec.methodBuilder("main")
                    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                    .returns(void.class)
                    .addParameter(String[].class, "args")

                    // 增加main方法里面的内容
                    .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")

                    .build();

            // 2.类
            TypeSpec testClass = TypeSpec.classBuilder("DerryTest")
                    .addMethod(mainMethod)
                    .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                    .build();

            // 3.包
            JavaFile packagef = JavaFile.builder("com.xiangxue.test", testClass).build();

            // 生成文件
            try {
                packagef.writeTo(filer);
            } catch (IOException e) {
                e.printStackTrace();
                messager.printMessage(Diagnostic.Kind.NOTE, "生成Test文件时失败,异常:" + e.getMessage());
            }*/
            //获取包名方法示例
            // 包信息
            String packageName = elementTool.getPackageOf(element).getQualifiedName().toString();

            // 获取简单类名,例如:MainActivity  MainActivity2  MainActivity3
            String className = element.getSimpleName().toString();
            messager.printMessage(Diagnostic.Kind.NOTE, "被@ARetuer注解的类有:" + className);

            // String className = element.getSimpleName().toString();

            // 目标:要生成的文件名称  MainActivity$$$$$$$$$ARouter
            String finalClassName = className + "$$$$$$$$$ARouter";

            /**
             模板:
             public class MainActivity3$$$$$$$$$ARouter {

                public static Class findTargetClass(String path) {
                    return path.equals("/app/MainActivity3") ? MainActivity3.class : null;
                }

             }
             */

            ARouter aRouter = element.getAnnotation(ARouter.class);
            //这下面开始是动态使用
            // 1.方法
            MethodSpec findTargetClass = MethodSpec.methodBuilder("findTargetClass")
                    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                    .returns(Class.class)
                    .addParameter(String.class, "path")
                    // 方法里面的内容 return path.equals("/app/MainActivity3") ? MainActivity3.class : null;

                    // 需要JavaPoet包装转型
                    .addStatement("return path.equals($S) ? $T.class : null",
                            aRouter.path(),
                            ClassName.get((TypeElement) element))
                    .build();

            // 2.类
            TypeSpec myClass = TypeSpec.classBuilder(finalClassName)
                    .addMethod(findTargetClass)
                    .addModifiers(Modifier.PUBLIC)
                    .build();

            // 3.包
            JavaFile packagef = JavaFile.builder(packageName, myClass).build();

            // 开始生成
            try {
                packagef.writeTo(filer);
            } catch (IOException e) {
                e.printStackTrace();
                messager.printMessage(Diagnostic.Kind.NOTE, "生成" + finalClassName + "文件时失败,异常:" + e.getMessage());
            }
        }

        return true; // false不干活了      true干完了
    }
}

来看一下调用

@ARouter(path = "/app/MainActivity3")
public class MainActivity3 extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

看一下build的文件,名字,方法都是我们框架里自定义的

public class MainActivity3$$$$$$$$$ARouter {
  public static Class findTargetClass(String path) {
    return path.equals("/app/MainActivity3") ? MainActivity3.class : null;
  }
}

可以看到我们可以通过编译成的代码来获取其他未实例化的方法,从mainactivity获取类mainactivity3

@ARouter(path = "/app/MainActivity")
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void startMainActivity3(View view) {
        Class startClass = MainActivity3$$$$$$$$$ARouter.findTargetClass("/app/MainActivity3");
    }
}

这就是apt以及为了实现apt技术的手段:javapoet的使用及介绍。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值