当前位置:   article > 正文

APT及JavaPoet基本使用_apt javapoet

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的注解包

  1. apply plugin: 'java-library'
  2. dependencies {
  3. implementation fileTree(dir: 'libs', include: ['*.jar'])
  4. // 背后的服务 能够监听 你是否在编译中.....
  5. // AS3.4.1 + Gradle 5.1.1 + auto-service:1.0-rc4
  6. compileOnly'com.google.auto.service:auto-service:1.0-rc4'
  7. annotationProcessor'com.google.auto.service:auto-service:1.0-rc4'
  8. // 帮助我们通过类调用的形式来生成Java代码 [JavaPoet]
  9. implementation "com.squareup:javapoet:1.9.0"
  10. // 依赖注解
  11. implementation project(":arouter-annotations")
  12. }

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

  1. @Target(TYPE) // 类上
  2. @Retention(CLASS) // 编译期 XUtil==运行期
  3. public @interface ARouter {
  4. String path();
  5. String group() default "";
  6. }

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

  1. // 依赖注解
  2. implementation project(":arouter-annotations")
  3. // 依赖注解处理器 注解处理器才能工作
  4. annotationProcessor project(":compiler")

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

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

  1. @AutoService(Processor.class) // 启用服务
  2. @SupportedAnnotationTypes({"com.derry.arouter_annotations.ARouter"}) // 注解
  3. @SupportedSourceVersion(SourceVersion.RELEASE_7) // 环境的版本
  4. // 接收 安卓工程传递过来的参数
  5. @SupportedOptions("student")
  6. public class ARouterProcessor extends AbstractProcessor {
  7. // 操作Element的工具类(类,函数,属性,其实都是Element)
  8. private Elements elementTool;
  9. // type(类信息)的工具类,包含用于操作TypeMirror的工具方法
  10. private Types typeTool;
  11. // Message用来打印 日志相关信息
  12. private Messager messager;
  13. // 文件生成器, 类 资源 等,就是最终要生成的文件 是需要Filer来完成的
  14. private Filer filer;
  15. @Override
  16. public synchronized void init(ProcessingEnvironment processingEnvironment) {
  17. super.init(processingEnvironment);
  18. elementTool = processingEnvironment.getElementUtils();
  19. messager = processingEnvironment.getMessager();
  20. filer = processingEnvironment.getFiler();
  21. String value = processingEnvironment.getOptions().get("student");
  22. // 如果我想在注解处理器里面抛出异常 可以使用Diagnostic.Kind.ERROR
  23. messager.printMessage(Diagnostic.Kind.NOTE, ">>>>>>>>>>>>>>>>>>>>>>"+value);
  24. }
  25. // 服务:在编译的时候干活
  26. // 坑:如果没有在任何地方使用,次函数是不会工作的
  27. @Override
  28. public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
  29. messager.printMessage(Diagnostic.Kind.NOTE, ">>>>>>> Derry run...");
  30. if (set.isEmpty()) {
  31. return false; // 不干活
  32. }
  33. // 循环?
  34. // 获取被 ARouter注解的 "类节点信息"
  35. Set<? extends Element> elements =
  36. roundEnvironment.getElementsAnnotatedWith(ARouter.class);
  37. for (Element element : elements) { // for 3 // 1 element == MainActivity 2
  38. element == MainActivity2
  39. // 注释部分是基本使用,写死了各种类
  40. /**
  41. 模块一
  42. package com.example.helloworld;
  43. public final class HelloWorld {
  44. public static void main(String[] args) {
  45. System.out.println("Hello, JavaPoet!");
  46. }
  47. }
  48. */
  49. // Java 万物皆对象
  50. // C 万物皆指针
  51. /*// 1.方法
  52. MethodSpec mainMethod = MethodSpec.methodBuilder("main")
  53. .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
  54. .returns(void.class)
  55. .addParameter(String[].class, "args")
  56. // 增加main方法里面的内容
  57. .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
  58. .build();
  59. // 2.类
  60. TypeSpec testClass = TypeSpec.classBuilder("DerryTest")
  61. .addMethod(mainMethod)
  62. .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
  63. .build();
  64. // 3.包
  65. JavaFile packagef = JavaFile.builder("com.xiangxue.test", testClass).build();
  66. // 生成文件
  67. try {
  68. packagef.writeTo(filer);
  69. } catch (IOException e) {
  70. e.printStackTrace();
  71. messager.printMessage(Diagnostic.Kind.NOTE, "生成Test文件时失败,异常:" + e.getMessage());
  72. }*/
  73. //获取包名方法示例
  74. // 包信息
  75. String packageName = elementTool.getPackageOf(element).getQualifiedName().toString();
  76. // 获取简单类名,例如:MainActivity MainActivity2 MainActivity3
  77. String className = element.getSimpleName().toString();
  78. messager.printMessage(Diagnostic.Kind.NOTE, "被@ARetuer注解的类有:" + className);
  79. // String className = element.getSimpleName().toString();
  80. // 目标:要生成的文件名称 MainActivity$$$$$$$$$ARouter
  81. String finalClassName = className + "$$$$$$$$$ARouter";
  82. /**
  83. 模板:
  84. public class MainActivity3$$$$$$$$$ARouter {
  85. public static Class findTargetClass(String path) {
  86. return path.equals("/app/MainActivity3") ? MainActivity3.class : null;
  87. }
  88. }
  89. */
  90. ARouter aRouter = element.getAnnotation(ARouter.class);
  91. //这下面开始是动态使用
  92. // 1.方法
  93. MethodSpec findTargetClass = MethodSpec.methodBuilder("findTargetClass")
  94. .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
  95. .returns(Class.class)
  96. .addParameter(String.class, "path")
  97. // 方法里面的内容 return path.equals("/app/MainActivity3") ? MainActivity3.class : null;
  98. // 需要JavaPoet包装转型
  99. .addStatement("return path.equals($S) ? $T.class : null",
  100. aRouter.path(),
  101. ClassName.get((TypeElement) element))
  102. .build();
  103. // 2.类
  104. TypeSpec myClass = TypeSpec.classBuilder(finalClassName)
  105. .addMethod(findTargetClass)
  106. .addModifiers(Modifier.PUBLIC)
  107. .build();
  108. // 3.包
  109. JavaFile packagef = JavaFile.builder(packageName, myClass).build();
  110. // 开始生成
  111. try {
  112. packagef.writeTo(filer);
  113. } catch (IOException e) {
  114. e.printStackTrace();
  115. messager.printMessage(Diagnostic.Kind.NOTE, "生成" + finalClassName + "文件时失败,异常:" + e.getMessage());
  116. }
  117. }
  118. return true; // false不干活了 true干完了
  119. }
  120. }

来看一下调用

  1. @ARouter(path = "/app/MainActivity3")
  2. public class MainActivity3 extends AppCompatActivity {
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_main);
  7. }
  8. }

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

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

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

  1. @ARouter(path = "/app/MainActivity")
  2. public class MainActivity extends AppCompatActivity {
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_main);
  7. }
  8. public void startMainActivity3(View view) {
  9. Class startClass = MainActivity3$$$$$$$$$ARouter.findTargetClass("/app/MainActivity3");
  10. }
  11. }

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/240121
推荐阅读
相关标签
  

闽ICP备14008679号