什么是JavaPoet
项目主页及源码:https://github.com/square/javapoet
APT + JavaPoet = 超级利刃
JavaPoet是square推出的开源java代码生成框架,提供Java Api生成.java源文件,这个框架功能非常实用,也是我们习惯的Java面向对象OOP语法,可以很方便的使用它根据注解生成相应的代码,通过这种自动化生成代码的方式,可以让我们更加简洁优雅的方式要替代繁琐冗杂的重复工作(常规的通过继承注解处理器AbstractProcessor,根据注解生成java代码的时候,需要一行一行的进行字符串的拼接,如果生成java文件内容较多,实在是不优雅,可以说是不人性)。
依赖JavaPoet库
Android Studio 3.4.2 + Gradle5.1.1 (向下兼容)
compileOnly ‘com.google.auto.service:auto-service:1.0-rc4’
annotationProcessor ‘com.google.auto.service:auto-service:1.0-rc4’
依赖
//帮助我们通过类调用的形式来生成java代码
implementation ‘com.squareup:javapoet:1.9.0’
JavaPoet的8个常用类
注意与APT中结构体语言表述区别
类对象 | 说明 |
---|---|
MethodSpec | 代表一个构造函数或方法说明 |
TypeSpec | 代表一个类,接口,或者枚举声明 |
FieldSpec | 代表一个成员变量,一个字段声明 |
JavaFIle | 包含一个顶级类的Java文件 |
ParameterSpec | 用来创建参数 |
AnnotationSpec | 用来创建注解 |
ClassName | 用来包装一个类 |
TypeName | 类型,如在添加返回值类型是使用TypeName.VOID |
JavaPoet字符串格式化规则
L”,10
S, “hello”
T,MainActivity
N来表示(可以理解为占位符)
通过AbstractProcessor生成代码对比
传统方式
//触发注解处理器生成java文件
@AutoService(Processor.class)
//允许/支持的注解类型,让注解处理器处理
@SupportedAnnotationTypes({Constants.AROUTER_ANNOTATION_TYPE})
//指定JDK编译版本
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class ARouterProcessor extends AbstractProcessor {
//Messager用来报告错误,警告和其它提示信息
private Messager messager;
//Elements中包含用于操作Element的工具方法
private Elements elementUtils;
//Filter用来创建新的源文件,类文件以及辅助文件
private Filer filer;
//Types中包含用于操作TypeMirror的工具方法
private Types typeUtils;
/**
* 初始化操作,通过ProcessingEnvironment可以获取一系列有用的工具类
* @param processingEnv
*/
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
//初始化
elementUtils = processingEnv.getElementUtils();
typeUtils = processingEnv.getTypeUtils();
filer = processingEnv.getFiler();
messager = processingEnv.getMessager();
messager.printMessage(Diagnostic.Kind.NOTE,
"注解处理器初始化完成,开始处理注解......");
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnv) {
if (set.isEmpty()) return false;
//获取所有带@ARouter注解的类节点
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(ARouter.class);
//遍历所有类节点
for (Element element : elements) {
//通过类节点获取包节点(全路径:com.example.xxx)
String packageName = elementUtils.getPackageOf(element).getQualifiedName().toString();
//获取简单类名
String className = element.getSimpleName().toString();
messager.printMessage(Diagnostic.Kind.NOTE, "被注解的类有:" + className);
//最终想生成的类文件名
String finalClassName = className + "$$ARouter";
//常规写法,也是EventBus写法(https://github.com/greenrobot/EventBus)
try {
//创建一个新的源文件,并返回一个对象以允许写入它
JavaFileObject sourceFile = filer.createSourceFile(packageName + "." + finalClassName);
//定义Writer对象,开启写入
Writer writer = sourceFile.openWriter();
//设置包名
writer.write("package " + packageName + ";\n");
writer.write("import android.util.Log;\n");
writer.write("public class " + finalClassName + "{\n");
writer.write("public void hello(String path) {");
writer.write("Log.d(\"xpf >>> \", \"APT......\");\n}\n");
writer.write("\n}");
//最后结束别忘了
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
}
生成的java文件
package com.example.apt_javapoet;
import android.util.Log;
public class MainActivity$$ARouter {
public void hello(String path) {
Log.d("xpf >>> ", "APT......");
}
}
采用JavaPoet方式
//触发注解处理器生成java文件
@AutoService(Processor.class)
//允许/支持的注解类型,让注解处理器处理
@SupportedAnnotationTypes({Constants.HELLOWORLD_ANNOTATION_TYPE})
//指定JDK编译版本
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class HelloWorldProcessor extends AbstractProcessor {
//Messager用来报告错误,警告和其它提示信息
private Messager messager;
//Elements中包含用于操作Element的工具方法
private Elements elementUtils;
//Filter用来创建新的源文件,类文件以及辅助文件
private Filer filer;
//Types中包含用于操作TypeMirror的工具方法
private Types typeUtils;
/**
* 初始化操作,通过ProcessingEnvironment可以获取一系列有用的工具类
* @param processingEnv
*/
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
//初始化
elementUtils = processingEnv.getElementUtils();
typeUtils = processingEnv.getTypeUtils();
filer = processingEnv.getFiler();
messager = processingEnv.getMessager();
messager.printMessage(Diagnostic.Kind.NOTE,
"注解处理器初始化完成,开始处理注解......");
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnv) {
if (set.isEmpty()) return false;
//获取所有被@HelloWorld注解的元素(方法)集合
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(HelloWorld.class);
if (!elements.isEmpty()) {
//解析元素,生成类文件
//方法体
MethodSpec main = MethodSpec.methodBuilder("main")//方法名
.addModifiers(Modifier.PUBLIC,Modifier.STATIC)//方法修饰符
.returns(void.class)//方法返回值(默认void)
.addParameter(String[].class, "args")//方法参数
.addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")//方法内容
.build();//构建
//类
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")//类名
.addModifiers(Modifier.PUBLIC)//类修饰符
.addMethod(main)//加入方法体
.build();//构建
//文件生成器
JavaFile javaFile = JavaFile.builder("com.example.helloWorld", helloWorld)
.build();
//写文件
try {
javaFile.writeTo(filer);
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
}
生成的java文件
package com.example.helloWorld;
import java.lang.String;
import java.lang.System;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, JavaPoet!");
}
}
一对比就知道差别了,省去了惨无人道的字符串拼接操作,谁用谁知道。