編譯時註解APT

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


    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章