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的使用及介绍。