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();
}
}