APT技术功能
APT(Annotation Processing Tool) 是 javac 提供 的一种 编译时扫描、处理注解的工具
它会对源代码文件进行检查,并找出其中的注解,然后根据用户自定义的注解处理方法进行额外的处理。
APT 工具能 解析注解,根据注解生成其他的源文件,将生成的新的源文件、原来的源文件【共同编译】
(注意:APT 不能对源文件进行修改操作,只能生成新的文件,例如在已有的类中添加方法)
注解处理器是 运行 它自己的虚拟机JVM 中
javac 启动一个 完整Java虚拟机 来运行注解处理器
2、APT使用依赖
1)注解处理器库(包含我们的注解处理器)
Android项目默认是不包含 APT相关类的。要使用APT技术,必须创建一个Java Library。
2)注解声明库(用于存储声明的注解)
开发者并不希望已经编译好的项目中,包含处理器相关代码,他仅仅想使用我们的注解。
把注解处理器 与 注解 分开单独抽成一个库是有必要的。
3)实际使用 APT的 Android/Java项目
3、注解处理器的声明
继承 AbstractProcessor 类
class MineProcessor extends AbstractProcessor {
/*
* 注解处理器被初始化的时候都会被调用
* 参数 ProcessingEnvironment 能提供很多有用的工具类,Elements、Types、Filer
*/
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {}
/*
* 注解处理器实际处理方法,一般要求子类实现该抽象方法,
* 可以在这里写扫描与处理注解的代码,以及生成Java文件。
*
* 其中参数RoundEnvironment ,可以查询出包含特定注解的被注解元素
*/
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return false;
}
/*
* 用来指定你使用的Java版本,
* 通常返回 SourceVersion.latestSupported()。
*
* 或者 SourceVersion.RELAEASE_XX 指定具体版本。
*/
@Override
public SourceVersion getSupportedSourceVersion() { }
/*
* 返回当前注解处理器处理注解的类型
*
* 返回值为一个字符串的集合。其中字符串为处理器需要处理的注解的合法全称。
*/
@Override
public Set<String> getSupportedAnnotationTypes() { }
}
在【Java1.6 版本】中提供了 @SupportedAnnotationTypes、@SupportedSourceVersion 两个注解
来替代getSupportedSourceVersion与getSupportedAnnotationTypes两个方法。
@SupportedSourceVersion(SourceVersion.RELEASE_6)
@SupportedAnnotationTypes({"合法注解的名称"})
class MineProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return false;
}
}
4、注册注解处理器
1)目录结构
-src
-main
-java
-processor
MineProcessor
-resources
-META-INF.services
javax.annotation.processing.Processor
META-INF/services 相当于一个信息包,
目录中的文件、目录获得 Java平台的认可 与 解释 用来配置应用程序、扩展程序、类加载器和服务文件,
在jar打包时自动生成。
javax.annotation.processing.Processor 文件中的内容:
每个注解处理器的合法的全名列表,每一个元素换行分割
javax.annotation.processing.Processor 文件中的内容为【每个注解处理器的合法的全名列表】
每一个元素换行分割,如下:
com.jennifer.andy.processor.MineProcessor1
com.jennifer.andy.processor.MineProcessor2
com.jennifer.andy.processor.MineProcessor3
将你生成的.jar放到你的buildPath中,
那么Java编译器会自动的检查和读取javax.annotation.processing.Processor中的内容,
并注册该注解处理器。
2)Android Studio中使用
只要创建1)中文件夹和文件,不用把jar放到buildPath,IDE会帮我们处理。
或者,使用 Google 提供的 AutoService 注解处理器,用于完成1)中创建目录和文件的工作。
@SupportedSourceVersion(SourceVersion.RELEASE_6)
@SupportedAnnotationTypes({"要处理的注解的名称"})
@AutoService(Processor.class)
class MineProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return false;
}
}
使用 AutoService,需要在Gradle 中添加依赖:
compile 'com.google.auto.service:auto-service:1.0-rc2’
4、注解处理器 的扫描
注解处理过程中,会 扫描所有的Java源文件,源代码的每一个部分都是一个特定类型的 Element。
Element 代表源文件中的元素,例如包、类、字段、方法等。
1)Element子/孙类:
Parameterizable:表示 混合类型的元素(不仅只有一种类型的Element)
TypeParameterElement:带有泛型参数 的类、接口、方法、构造器。
VariableElement:表示 字段、常量、方法、构造函数。参数、局部变量、资源变量、异常参数
QualifiedNameable:具有限定名称的元素
ExecutableElement:表示 类、接口的方法、构造函数、初始化器(静态或实例),包括注释类型元素。
TypeElement :表示 类和接口
PackageElement:表示 包
例子:
package com.jennifer.andy.aptdemo.domain;//PackageElement
class Person {//TypeElement
private String where;//VariableElement
public void doSomething() { }//ExecutableElement
public void run() {//ExecutableElement
int runTime;//VariableElement
}
}
获取一个元素的父元素和子元素:
TypeElement person= ... ;
for (Element e : person.getEnclosedElements()){ // 遍历它的孩子
Element parent = e.getEnclosingElement(); //拿到孩子元素的最近的父元素
}
2)元素种类判断
例子:
public class SpiltElementProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {
//获取源文件中所有包含@Who注解的元素
Set<? extends Element> elements = env.getElementsAnnotatedWith(Who.class);
for (Element element : elements) {
//element.getKind()具体判断当前元素种类
if (element.getKind() == ElementKind.CLASS) {//如果元素是类
} else if (element.getKind() == ElementKind.INTERFACE) {//如果当前元素是接口
}
}
return false;
}
...
}
ElementKind枚举:
public enum ElementKind {
PACKAGE, //包
ENUM, //枚举
CLASS, //类
ANNOTATION_TYPE, //注解
INTERFACE, //接口
ENUM_CONSTANT, //枚举常量
FIELD, //字段
PARAMETER, //参数
LOCAL_VARIABLE, //本地变量
EXCEPTION_PARAMETER, //异常参数
METHOD, //方法
CONSTRUCTOR, //构造函数
STATIC_INIT, //
INSTANCE_INIT,
TYPE_PARAMETER,
OTHER,
RESOURCE_VARIABLE;
...
}
3)元素类型判断
通过 TypeMirror 来获取元素类型
public class TypeKindSpiltProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {
Set<? extends Element> elements = env.getElementsAnnotatedWith(Who.class);
for (Element element : elements) {
if (element.getKind() == ElementKind.METHOD) {//如果当前元素是方法
ExecutableElement methodElement = (ExecutableElement) element;
//获取TypeMirror
TypeMirror returnType = methodElement.getReturnType();
//获取元素类型
TypeKind kind = returnType.getKind();
System.out.println("print return type----->" + kind.toString());
}
}
return false;
}
}
TypeKind 枚举类型:
public enum TypeKind {
BOOLEAN, //boolean 类型
BYTE, //byte 类型
SHORT, //short 类型
INT, //int 类型
LONG, //long 类型
CHAR, //char 类型
FLOAT, //float 类型
DOUBLE, //double 类型
VOID, //void 类型,主要用于方法的返回值
NONE, //无类型
NULL, //空类型
ARRAY, //数组类型
DECLARED,
ERROR,
TYPEVAR,
WILDCARD,
PACKAGE,
EXECUTABLE,
OTHER,
UNION,
INTERSECTION;
...
}
3)元素可见性修饰符
在注解处理器中,我们不仅能获得元素的种类和信息,
我们还能获取该元素的 可见性修饰符(例如public、private等)。
我们可以直接调用 Element.getModifiers()
public class GetModifiersProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {
Set<? extends Element> elements = env.getElementsAnnotatedWith(Who.class);
for (Element element : elements) {
if (element.getKind() == ElementKind.CLASS) {//如果元素是类
Set<Modifier> modifiers = element.getModifiers();//获取可见性修饰符
if (!modifiers.contains(Modifier.PUBLIC)) {//如果当前类不是public
throw new ProcessingException(classElement, "The class %s is not public.",
classElement.getQualifiedName().toString());
}
}
return false;
}
}
Modifer 为枚举类型:
public enum Modifier {
PUBLIC,
PROTECTED,
PRIVATE,
ABSTRACT,
DEFAULT,
STATIC,
FINAL,
TRANSIENT,
VOLATILE,
SYNCHRONIZED,
NATIVE,
STRICTFP;
...
}
4)错误处理 Messager
env.getMessager().printMessage(Diagnostic.Kind.NOTE, msg)
用来写一些信息给使用此注解库的第三方开发者
Diagnostic.Kind 消息级别枚举
public class GetModifiersProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {
Set<? extends Element> elements = env.getElementsAnnotatedWith(Who.class);
for (Element element : elements) {
if (element.getKind() == ElementKind.CLASS) {//如果元素是类
Set<Modifier> modifiers = element.getModifiers();//获取可见性修饰符
if (!modifiers.contains(Modifier.PUBLIC)) {//如果当前类不是public
/* 用来写一些信息给使用此注解库的第三方开发者的
* env.getMessager().printMessage(Diagnostic.Kind.NOTE, msg)
*/
env.getMessager().printMessage(Diagnostic.Kind.ERROR, "the class is not public");
}
}
return false;
}
}
4、生成新的类(也就是创建新的源文件)
通过 JavaPoet 来构造源文件
在gradle中你需要添加依赖:
compile 'com.google.auto.service:auto-service:1.0-rc2’
1)JavaPoet 的使用
@AutoService(Processor.class)
@SupportedAnnotationTypes("com.jennifer.andy.apt.annotation.Who")
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class CreateFileByJavaPoetProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment env) {
createFileByJavaPoet(set, env);
return false;
}
/**
* 通过JavaPoet生成新的源文件
*/
private void createFileByJavaPoet(Set<? extends TypeElement> annotations, RoundEnvironment env) {
//创建 main 方法
MethodSpec main = MethodSpec.methodBuilder("main")
//设置可见性修饰符public static
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
//设置返回值为void
.returns(void.class)
//添加参数类型为String数组,且参数名称为args
.addParameter(String[].class, "args")
//添加语句
.addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
.build();
//创建类
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
//将main方法添加到HelloWord类中
.addMethod(main)
.build();
//创建文件,第一个参数是包名,第二个参数是相关类
JavaFile javaFile = JavaFile.builder("com.jennifer.andy.aptdemo.domain", helloWorld)
.build();
try {
//创建文件
javaFile.writeTo(processingEnv.getFiler());
} catch (IOException e) {
log(e.getMessage());
}
}
/**
* 调用打印语句而已
*/
private void log(String msg) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, msg);
}
}
5、分离处理器和项目
annotationProcessor 是APT工具中的一种,
它是google开发的内置框架,不需要引入,可以直接在build.gradle文件中使用。
使用 annotationProcessor:
1)允许 只将编译时注解处理器配置为依赖项,而不在最终apk 或库中包括工件
2)设置源路径,以便 Android Studio 能正确找到注解处理器生成的代码
使用:
//如果是本地库
annotationProcessor project(':apt_compiler')
//如果是远程库
annotationProcessor 'com.jakewharton:butterknife-compiler:9.0.0-rc1'