编译时注解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)


    image.png

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 set, RoundEnvironment roundEnvironment) {
        Set 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();
        }


    }

你可能感兴趣的:(编译时注解APT)