APT 动态创建 对象(小试牛刀)

109 阅读1分钟

厌烦了代码中无穷无尽的if-else . 早之前的项目中使用了这个利器,当时感觉眼前一亮。随好好学习一下,然后去改造新的项目。

基础:

  1. java Annotation 标记工具
  2. Abstract Processor 预处理器
  3. javapoet 代码生成器

实施: 通过IDEA 创建一个android demo project(base on java) 新增两个library apt-annotation apt-processor

image.png app 的build.gradle 中新增

dependencies {
...
implementation project(path: ':apt-annotation')
annotationProcessor project(path: ':apt-processor')
}

apt-processor 的build.gradle 中新增

dependencies {
    //自动注册,动态生成 META-INF/...文件
    implementation 'com.google.auto.service:auto-service:1.0-rc6'
    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'

    //依赖apt-annotation
    implementation project(path: ':apt-annotation')

    //javaPoet
    implementation "com.squareup:javapoet:1.13.0"
}

apt-annotation 中新增两个类

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Executor {
    String name() default "default";
}
public interface SpeechExecutor {
    boolean execute(String str);
}

apt-processor 中新增

@AutoService(Processor.class)
public class MyProcessor extends AbstractProcessor {

    private Messager mMessager;

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        HashSet<String> hashSet = new HashSet<>();
        hashSet.add(Executor.class.getCanonicalName());
        return hashSet;
    }

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        mMessager = processingEnv.getMessager();
        mMessager.printMessage(Diagnostic.Kind.NOTE, "Hello APT");
    }

    public static CodeBlock generateInnerClause(List<? extends Element> elements, String str) {
        Builder mainBuilder = CodeBlock.builder();
        if (elements.size() == 0) {
            return mainBuilder.addStatement("return null").build();
        }

        int i = 0;
        for (Element element : elements) {
            Executor[] es = element.getAnnotationsByType(Executor.class);
            if (es[0].name().length() == 0) {
                continue;
            }

            if (i == 0) {
                mainBuilder.beginControlFlow("if ($L.equals($S))", str, es[0].name());
            } else {
                mainBuilder.nextControlFlow("else if ($L.equals($S))", str, es[0].name());
            }
            mainBuilder.addStatement("return new $T()", element.asType());
            i++;
        }
        mainBuilder.nextControlFlow("else");
        mainBuilder.addStatement("return null");
        mainBuilder.endControlFlow();

        return mainBuilder.build();
    }

    private void generateCode(Set<? extends Element> elements) {
        //生成类
        TypeSpec.Builder classBuilder = TypeSpec
            .classBuilder("ExecutorFactory")
            .addModifiers(Modifier.PUBLIC, Modifier.FINAL);

        List<Element> list = new ArrayList<>();
        Types typeUtils = processingEnv.getTypeUtils();
        Elements elementUtils = processingEnv.getElementUtils();
        Element speech = elementUtils.getTypeElement(SpeechExecutor.class.getCanonicalName());
        for (Element element : elements) {
            // 判断是否实现了接口
            if (!typeUtils.isSubtype(element.asType(), speech.asType())) {
                continue;
            }
            list.add(element);
        }

        //生成方法
        MethodSpec method = MethodSpec.methodBuilder("create")
            .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
            .addParameter(String.class, "sName")
            .returns(SpeechExecutor.class)
            .addCode(generateInnerClause(list, "sName")).build();

        classBuilder.addMethod(method);

        //包
        JavaFile javaFile = JavaFile
            .builder("com.example.helloapt", classBuilder.build())
            .build();
        try {
            javaFile.writeTo(processingEnv.getFiler());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        mMessager.printMessage(Diagnostic.Kind.NOTE, "process size=" + annotations.size());

        //拿到所有添加Print注解的成员变量
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Executor.class);
        // apt processor 会执行多次,这里通过 size 判断是否需要产生代码
        if (annotations.size() > 0) {
            // 产生代码
            generateCode(elements);
        }

        return false;
    }
}

完整代码连接: github.com/yanyue/Hell…

恩,还是要多读源码

参考文档: juejin.cn/post/698247… juejin.cn/post/684490… github.com/Xiasm/EasyR…