1.什么是APT?
APT即为Annotation Processing Tool,它是javac的一个工具,中文意思为编译时注解处理器。APT可以用来在编译时扫描和处理注解。通过APT可以获取到注解和被注解对象的相关信息,在拿到这些信息后我们可以根据需求来自动的生成一些代码,省去了手动编写。注意,获取注解及生成代码都是在代码编译时候完成的,相比反射在运行时处理注解大大提高了程序性能。APT的核心是AbstractProcessor类,关于AbstractProcessor类后面会做详细说明
在编译器对源码文件进行扫描,通过Gradle 调用java代码的方式,生成源码.java 文件,再编译编译到Apk文件中
我们通过注解处理器生成一个简单工厂模式的工厂类,用于动态给新增的产品添加生产方法
1.需要通过apt 编译时期处理的需要通过annotationProcessor 进行依赖,那么就需要把注解处理器单独放到lib 中,同时引用到注解声明,避免依赖成环,也需要把注解声明单独成lib
dependencies {
implementation project(':annotation')
annotationProcessor project(':processor')
}
-
annotationProcessor 会检查javax.annotation.processing.Processor 文件中声明的注解处理器,并根据注解处理器中getSupportedAnnotationTypes 注解去检查每一个文件元素(Class 方法 成员变量 都可以是元素Element)
public class Circle { // TypeElement
private int i; // VariableElement
private Triangle triangle; // VariableElement
public Circle() {} // ExecuteableElement
public void draw( // ExecuteableElement
String s) // VariableElement
{
System.out.println(s);
}
@Override
public void draw() { // ExecuteableElement
System.out.println("Draw a circle");
}
}
3.只介绍核心方法process 用于生成代码,为避免繁重的文件String 操作,和生成代码出错概率,引入javapoet,提供了一套面向对象式的操作生成.java文件的方法
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(InjectFactory.class);
superClassTypes.clear();
for (Element element : elementsAnnotatedWith) {
if (element.getKind() != ElementKind.CLASS) {
try {
throw new Exception(element.toString() + " only can be annotated on class ");
} catch (Exception e) {
e.printStackTrace();
}
}
TypeElement typeElement = (TypeElement) element;
messager.printMessage(Diagnostic.Kind.NOTE,"process tag ...............");
FactoryAnnotatedClass factoryAnnotatedClass = new FactoryAnnotatedClass(messager,typeElement);
messager.printMessage(Diagnostic.Kind.NOTE,"process tag ...............1");
SameFactoryCollection factoryGrouped = superClassTypes.get(factoryAnnotatedClass.qualifiedSuperClassName);
if (factoryGrouped == null) {
factoryGrouped = new SameFactoryCollection(factoryAnnotatedClass.qualifiedSuperClassName);
superClassTypes.put(factoryAnnotatedClass.qualifiedSuperClassName,factoryGrouped);
}
factoryGrouped.addAnnotationClass(factoryAnnotatedClass);
}
for (SameFactoryCollection value : superClassTypes.values()) {
generateCode(value);
}
return true;
}
public void generateCode(SameFactoryCollection annotatedClass) {
MethodSpec.Builder method = MethodSpec.methodBuilder("create")
.addModifiers(Modifier.PUBLIC)
.addParameter(String.class, "id")
.returns(TypeName.get(elementUtils.getTypeElement(annotatedClass.qualifiedSuperClassName).asType()));
// check if id is null
method.beginControlFlow("if (id == null)")
.addStatement("throw new IllegalArgumentException($S)", "id is null!")
.endControlFlow();
for (FactoryAnnotatedClass factoryAnnotatedClass : annotatedClass.set) {
method.beginControlFlow("if ($S.equals(id))",factoryAnnotatedClass.id)
.addStatement("return new $L()",factoryAnnotatedClass.classElement.getQualifiedName().toString())
.endControlFlow();
}
method.addStatement("throw new IllegalArgumentException($S + id)", "Unknown id = ");
TypeElement superClass = elementUtils.getTypeElement(annotatedClass.qualifiedSuperClassName);
TypeSpec typeSpec = TypeSpec.classBuilder(superClass.getSimpleName() + "Factory")
.addModifiers(Modifier.PUBLIC)
.addMethod(method.build())
.build();
TypeElement typeElement = elementUtils.getTypeElement(annotatedClass.qualifiedSuperClassName);
PackageElement pkg = elementUtils.getPackageOf(typeElement);
String packageName = pkg.isUnnamed() ? null : pkg.getQualifiedName().toString();
try {
JavaFile.builder(packageName,typeSpec).build().writeTo(filer);
} catch (IOException e) {
e.printStackTrace();
}
}